You are here

long_answer.module in Quiz 6.4

This module defines a long answer question type for quizzes.

Long answer questions make use of the quiz question framework (quiz_question.module). The functions in this file are largely used for grading long answer questions. Most of the real logic is in long_answer.classes.inc.

File

question_types/long_answer/long_answer.module
View source
<?php

/**
 * This module defines a long answer question type for quizzes.
 *
 * Long answer questions make use of the quiz question framework (quiz_question.module).
 * The functions in this file are largely used for grading long answer questions. Most
 * of the real logic is in long_answer.classes.inc.
 *
 * @file
 */

/**
 * Implementation of hook_help().
 */
function long_answer_help($path, $arg) {
  if ($path == 'admin/help#long_answer') {
    return '<p>' . t('This module provides long-answer (essay, multi-paragraph) questions to the quiz module.') . '</p><p>' . t('A long-answer question is designed to provide the quiz taker a lengthy area to expand on ideas.
      Common forms of long-answer questions include essays, single paragraph responses, hypothesis design problems,
      outlines and summaries, and lengthier math problems
      where the focus is on showing work rather than simply getting the correct answer.') . '</p>';
  }
}

/**
 * Implementation of hook_menu().
 */
function long_answer_menu() {
  $items['admin/quiz/reports/score-long-answer'] = array(
    'title' => 'Score long answer questions',
    'description' => 'Score the answers from quizzes that use long answer questions.',
    'page callback' => 'long_answer_view_unscored',
    'access arguments' => array(
      'score any quiz',
      'score own quiz',
    ),
    'access callback' => 'quiz_access_multi_or',
    'type' => MENU_NORMAL_ITEM,
    'file' => 'long_answer.admin.inc',
  );

  // Pass vid and rid to this path.
  $items['admin/quiz/reports/score-long-answer/%/%'] = array(
    'title' => 'Score long answer response',
    'description' => 'Score a response to a long answer question.',
    'page callback' => 'long_answer_edit_score',
    'page arguments' => array(
      4,
      5,
    ),
    'type' => MENU_NORMAL_ITEM,
    'access arguments' => array(
      4,
      5,
    ),
    'access callback' => 'quiz_question_access_to_score',
    'file' => 'long_answer.admin.inc',
  );
  return $items;
}

/**
 * Implementation of hook_quiz_question_info().
 */
function long_answer_quiz_question_info() {
  return array(
    'long_answer' => array(
      'name' => t('Long answer question'),
      'description' => t('Quiz questions that allow a user to enter multiple paragraphs of text.'),
      'question provider' => 'LongAnswerQuestion',
      'response provider' => 'LongAnswerResponse',
      'module' => 'quiz_question',
    ),
  );
}

/**
 * Implementation of hook_config
 */
function long_answer_config() {
  return FALSE;
}

/**
 * Implementation of hook_autoload_info().
 */
function long_answer_autoload_info() {
  return array(
    'LongAnswerQuestion' => array(
      'file' => 'long_answer.classes.inc',
    ),
    'LongAnswerResponse' => array(
      'file' => 'long_answer.classes.inc',
    ),
  );
}

/**
 * Implementation of hook_theme().
 */
function long_answer_theme() {
  return array(
    'long_answer_view_unscored' => array(
      'arguments' => array(
        'unscored' => array(),
      ),
      'path' => drupal_get_path('module', 'long_answer') . '/theme',
      'file' => 'long_answer.theme.inc',
    ),
    'long_answer_response_form' => array(
      'arguments' => array(
        'form' => NULL,
      ),
      'path' => drupal_get_path('module', 'long_answer') . '/theme',
      'file' => 'long_answer.theme.inc',
    ),
    'long_answer_answering_form' => array(
      'arguments' => array(
        'form' => NULL,
      ),
      'path' => drupal_get_path('module', 'long_answer') . '/theme',
      'template' => 'long-answer-answering-form',
    ),
  );
}

/**
 * Set a score for a long answer question.
 *
 * This stores a score for a long answer question and marks that question as having been evaluated.
 * The function updates all of the necessary data sources so that the individual answer results should be
 * reflected in the total scoring table.
 *
 * @param array $values
 *  quiz => Quiz node,
 *  nid => Node ID of question.
 *  vid => Version ID of question.
 *  rid => Result ID for the quiz results.
 *  score => The numeric score to assign the result.
 *  max_score => The max score for this question
 * @param $update_total
 *  Shall the total score be updated?
 *
 * @return int
 *  Number of scores adjusted. If a change was made, this should be 1.
 */
function long_answer_score_an_answer($values, $update_total = TRUE) {

  // Quiz scoring information is spread out across three tables:
  // 1. The module should retain its own scoring information in any case where scoring is non-trivial.
  // 2. The Quiz moduleretains a limited amount of scoring information.
  // 3. The Quiz module retains an overall score for a quiz. This is the percentage score for the combination of all
  //    questions on the quiz.
  //
  // We update all three.
  // First, we update the long answer table
  db_query("UPDATE {quiz_long_answer_user_answers}\n            SET score = %d * (\n              SELECT max_score\n              FROM {quiz_question_properties}\n              WHERE nid = %d\n              AND vid = %d\n            ) / %d, is_evaluated = 1, answer_feedback = '%s'\n            WHERE question_nid = %d\n            AND question_vid = %d\n            AND result_id = %d", $values['score'], $values['nid'], $values['vid'], $values['max_score'], $values['answer_feedback'], $values['nid'], $values['vid'], $values['rid']);
  $changed = db_affected_rows();
  if ($changed > 0) {

    // Second, we update the main quiz answers table
    // What do we do about the quiz_node_results_answers table? It assumes strict
    // bivalence (is_correct). I guess we consider any essay with over 50% to be correct?
    $max = db_result(db_query('SELECT max_score FROM {quiz_question_properties} WHERE vid = %d', $values['vid']));
    if ($max <= 0) {
      $is_correct = 0;
      $points_awarded = 0;
    }
    else {
      $is_correct = $values['score'] == $max ? 1 : 0;
      $points_awarded = $values['score'];
    }
    $sql = 'UPDATE {quiz_node_results_answers}
      SET points_awarded = %d, is_correct = %d
      WHERE question_vid = %d AND result_id = %d';
    db_query($sql, $points_awarded, $is_correct, $values['vid'], $values['rid']);

    // Third, we update the main quiz results table
    if ($update_total) {
      quiz_update_total_score($values['quiz'], $values['rid']);
    }
  }
  return $changed;
}

/**
 * Get the answer for a question.
 *
 * This stores a score for a long answer question and marks that question as having been evaluated.
 * @param $nid
 *  Node ID of question.
 * @param $vid
 *  Version ID of question.
 * @param $rid
 *  Result ID for the quiz results.
 *
 * @return Assoc array
 *  An array if successful, or FALSE if no result could be found. The array contains the following properties:
 *  <code>
 *  answer_id; // The answer ID
 *  answer; // The full text of the answer
 *  is_evaluated; // 0 if the question has not been evaluated, 1 if it has
 *  score; // The score the evaluator gave the user; this should be 0 if is_evaluated is 0.
 *  question_vid
 *  question_nid
 *  result_id
 *  </code>
 */
function long_answer_get_answer($question_nid, $question_vid, $result_id) {
  $sql = "SELECT answer_id, answer, la.is_evaluated, la.score, la.question_vid, la.question_nid, la.result_id, la.answer_feedback, rel.max_score AS rel_max_score, qt.max_score AS term_max_score, qnra.tid\n    FROM {quiz_long_answer_user_answers} la\n    JOIN {quiz_node_results} qnr ON (la.result_id = qnr.result_id)\n    JOIN {quiz_node_results_answers} qnra ON (qnr.result_id = qnra.result_id AND la.question_nid = qnra.question_nid AND la.question_vid = qnra.question_vid)\n    LEFT OUTER JOIN {quiz_node_relationship} rel ON (qnr.vid = rel.parent_vid AND rel.child_vid = la.question_vid)\n    LEFT OUTER JOIN {quiz_terms} qt ON qnr.nid = qt.nid AND qnr.vid = qt.vid AND qnra.tid = qt.tid\n    WHERE la.question_nid = %d AND la.question_vid = %d AND la.result_id = %d";
  $results = db_query($sql, $question_nid, $question_vid, $result_id);
  if (!$results) {
    return FALSE;
  }
  $answer = db_fetch_object($results);
  if ($answer->rel_max_score === NULL) {
    $answer->rel_max_score = $answer->term_max_score;
  }
  return $answer;
}

/**
 * Given a quiz, return a list of all the unscored answers.
 *
 * @param $nid
 *  Node ID for the quiz to check.
 * @param $vid
 *  Version ID for the quiz to check.
 * @param $count
 *  Number of items to return (default: 50).
 * @param $offset
 *  Where in the results we should start (default: 0).
 *
 * @return
 *  Indexed array of result IDs that need to be scored.
 */
function long_answer_get_unscored_answers_by_question($nid, $vid, $count = 50, $offset = 0) {
  $results = db_query_range('SELECT result_id
    FROM {quiz_long_answer_user_answers}
    WHERE is_evaluated = 0 AND question_nid = %d AND question_vid = %d', $nid, $vid, $offset, $count);
  $unscored = array();
  foreach (db_fetch_object($results) as $row) {
    $unscored[] = $row->result_id;
  }
  return $unscored;
}

/**
 * Get all quiz scores that have not yet been evaluated.
 *
 * @param $count
 *  Number of items to return (default: 50).
 * @param $offset
 *  Where in the results we should start (default: 0).
 *
 * @return
 *  Array of objects describing unanswered questions. Each object will have result_id, question_nid, and question_vid.
 */
function long_answer_get_all_unscored_answers($count = 50, $offset = 0) {
  global $user;
  $sql = 'SELECT a.result_id, a.question_nid, a.question_vid, r.title, qnr.time_end, qnr.time_start, qnr.uid
    FROM {quiz_long_answer_user_answers} AS a
    INNER JOIN {node_revisions} r ON a.question_vid = r.vid
    INNER JOIN {quiz_node_results} qnr ON a.result_id = qnr.result_id
    JOIN {node} n ON qnr.nid = n.nid
    WHERE a.is_evaluated = 0';
  if (!user_access('score any quiz')) {
    $sql .= ' AND n.uid = %d';
  }
  $results = db_query_range(db_rewrite_sql($sql), $user->uid, $offset, $count);
  $unscored = array();
  if ($results) {
    while ($row = db_fetch_object($results)) {
      $unscored[] = $row;
    }
  }
  return $unscored;
}

/**
 * Generate a list of long answer questions
 *
 * @return
 *  array containing nid as key and title as value
 */
function long_answer_questions_list() {
  $questions = array();
  $type = 'long_answer';
  $sql = "SELECT nid, title FROM {node} WHERE type = '%s'";
  $results = db_query($sql, $type);
  while ($result = db_fetch_object($results)) {
    $questions[$result->nid] = check_plain($result->title);
  }
  return $questions;
}

/**
 * Submit function for the report form
 *
 * @param $values
 *   The FAPI $form_state['values']
 */
function long_answer_report_submit($values) {
  long_answer_score_an_answer($values, FALSE);
}

/**
 * Validation function for the report form
 *
 * @param $values
 *  The FAPI $form_state['values']
 * @param $form_key
 *  Array key to add errors to
 */
function long_answer_report_validate($values, $form_key) {
  $max = (int) $values['max_score'];

  // Check to make sure that entered score is not higher than max allowed score.
  if (!_quiz_is_int($values['score'], 0, $max)) {
    form_set_error($form_key . '][score', t('The score needs to be a number between @min and @max', array(
      '@min' => 0,
      '@max' => $max,
    )));
  }
}

Functions

Namesort descending Description
long_answer_autoload_info Implementation of hook_autoload_info().
long_answer_config Implementation of hook_config
long_answer_get_all_unscored_answers Get all quiz scores that have not yet been evaluated.
long_answer_get_answer Get the answer for a question.
long_answer_get_unscored_answers_by_question Given a quiz, return a list of all the unscored answers.
long_answer_help Implementation of hook_help().
long_answer_menu Implementation of hook_menu().
long_answer_questions_list Generate a list of long answer questions
long_answer_quiz_question_info Implementation of hook_quiz_question_info().
long_answer_report_submit Submit function for the report form
long_answer_report_validate Validation function for the report form
long_answer_score_an_answer Set a score for a long answer question.
long_answer_theme Implementation of hook_theme().