Hi Matt --
Love the quiz module, it's beautifully designed. I downloaded Quiz
6.x-2.0, hoping it would handle abandoned quizzes. Right now, Quiz counts
a viewing of the first question as an actual test, whether a question is
answered or not. This led me to wonder about abandoned quiz efforts and
the possibility of starting a viewer where they left off during a quiz.
I'm new to Drupal, but have been coding in PHP for awhile. I haven't yet
learned how to play in the Drupal development arena, so I hope I'm sending
this code snippet to the right person. If not, please forward it. To
resolve this "unexpected feature" (so much nicer than bug..), I added a new
function "quiz_is_active" to be called from your function
"quiz_start_actions". Both are below. Please feel free to write back with
questions/comments. I've tested this and it works -- please let me know if
this seems worthwhile to you.

best,
- terri
________________________________________________________________________
function quiz_start_actions($quiz) {
global $user;

$user_is_admin = user_access('create quiz');

// Make sure this is available.
if ($quiz->quiz_always != 1) {
// Compare current GMT time to the open and close dates (which should
still be in GMT time).
$now = gmmktime();
if ($now >= $quiz->quiz_close || $now < $quiz->quiz_open) {
drupal_set_message(t('This @quiz is not currently available.',
array('@quiz' => QUIZ_NAME)), 'status');
if (!$user_is_admin) {
// Can't take quiz.
return FALSE;
}
}
}

// Check to see if this user is allowed to take the quiz again:
if($quiz->takes > 0) {
$query = "SELECT COUNT(*) AS takes FROM {quiz_node_results} WHERE uid
= %s AND nid = %s AND vid = %s";
$taken = db_result(db_query($query, $user->uid, $quiz->nid,
$quiz->vid));
$allowed_times = format_plural($quiz->takes, '1 time', '@count
times');
$taken_times = format_plural($taken, '1 time', '@count times');

// The user has already taken this quiz (nid/vid combo).
if ($taken) {
// If the user has already taken this quiz too many times, stop the
user.
if ($taken >= $quiz->takes) {
drupal_set_message(t('You have already taken this quiz @really.
You may not take it again.', array('@really' => $taken_times)), 'error');
if (!$user_is_admin) {
return FALSE;
}
}
// If the user has taken the quiz more than once, see if we should
report this.
else if (variable_get('quiz_show_allowed_times', TRUE)) {
drupal_set_message(t("You can only take this quiz @allowed. You
have taken it @really.", array('@allowed' => $allowed_times, '@really' =>
$taken_times)), 'status');
}
}
}

// Check to see if the (a) user is registered, and (b) user alredy
passed this quiz.
if ($user->uid && quiz_is_passed($user->uid, $quiz->nid, $quiz->vid)) {
drupal_set_message(t('You have already passed this @quiz.',
array('@quiz' => QUIZ_NAME)), 'status');
}

// On error, we want to return before here to avoid creating an empty
entry in quiz_node_results.
// Otherwise, we get fairly clutter Quiz Results.

// Insert quiz_node_results record.
// TJT: OK, you need to initialize record prior to adding answers,
// perhaps you could check for if abandoned quiz exists first
$active_quiz = quiz_is_active($user->uid, $quiz->nid, $quiz->vid);

if(isset($active_quiz)) {
return $active_quiz;
} else {
$result = db_query(
"INSERT INTO {quiz_node_results} (result_id, nid, vid, uid,
time_start) VALUES (%d, %d, %d, %d, %d)",
$rid, $quiz->nid, $quiz->vid, $user->uid, time()
);
if ($result) {
// Return the last RID.
return db_last_insert_id('quiz_node_results', 'result_id');
}
else {
drupal_set_message(t('There was a problem starting the @quiz. Please
try again later.', array('@quiz' => QUIZ_NAME), 'error'));
return FALSE;
}
}
}

/**
* TJT: Check a user/quiz combo to see if the user has an active quiz.
* This will return the RESULT_ID of any incomplete quiz and allow the
user
* to continue and complete the quiz.
* @param $uid
* The user ID.
* @param $nid
* The node ID.
* @param $vid
* The version ID.
*/
function quiz_is_active($uid, $nid, $vid) {
$arid = db_result(db_query("SELECT result_id as arid
FROM {quiz_node_results}
WHERE vid = %d AND nid = %d AND uid =%d AND time_end IS NULL",
$vid, $nid, $uid
));

return $arid;
}

Comments

nirvanajyothi’s picture

this will be a great feature,i think!

web360’s picture

I also think that this would be a nice feature.... and I will definitely try out the snippet. Thanks mysterri!

davemybes’s picture

Category: task » feature
StatusFileSize
new2.26 KB

+1 on this feature.

The above snippet doesn't work properly due to a couple of errors:

  • if(isset($active_quiz)) should simply be if ($active_quiz), otherwise it always seems to evaluate as TRUE.
  • AND time_end IS NULL will never find a result as that field is never NULL - its zero: AND time_end = 0

I have attached a patch for the code. It works, but takes you to the start of the quiz. Personally, I think it would be nicer to take you straight to the last question, but I haven't figured out how to do that yet (I assume its possible?). Your answers are remembered, but people can change the answers as they go through the questions to get to where they stopped - depending on the site, this could be desirable or not.

mbutcher’s picture

I will gladly add this patch if we can get it all working correctly.

I won't add code that does checks like this:

if ($active_quiz)

This will cause warnings in PHP, and such constructions are supposed to be removed from core. It's not considered good coding practice because it leaves too much to the PHP interpreter (and because it violates E_STRICT).

Can a finer-grained check be made?

With a fix for that, I'll give this patch a try.

akalsey’s picture

Status: Active » Needs work

With this patch, when a user attempts to return to a quiz, they're told they're on question #1.

During quiz startup, quiz_take_quiz() hard codes the question number with the line

$_SESSION['quiz_'. $quiz->nid]['question_number'] = 0;

The fix to this would be to check the result_id to see how many questions have been answered and set that number. I'll work on a patch.

akalsey’s picture

Status: Needs work » Needs review
StatusFileSize
new3.5 KB

Attached is a patch that fixes the issues with the patch from #3 and includes code to update arrays of previously answered and remaining quiz questions. This ensures the user takes the correct number of questions and shows the correct progress meter. The patch also sets a status message when a quiz is resumed.

mbutcher’s picture

akalsey: The patch is against Quiz 2 or quiz 3?

akalsey’s picture

Version: 6.x-2.0 » 6.x-2.1

This is against 6.x-2.1

Happy to re-roll against any CVS branch you want.

mbutcher’s picture

Can you re-roll against the 3.x dev snapshot? This bug is one of the showstoppers that is preventing 3.x from going into alpha, so if you solve it... GREAT!

Sivaji is working on a patch to allow admins to set max time limits for partially completed quizzes. Coupled with this, that would be cool.

mbutcher’s picture

Version: 6.x-2.1 » 6.x-3.x-dev
Status: Needs review » Fixed

akalsey: Thanks so much for the patch. I reworked it for Quiz 3 (and tried to cover a few additional gotchas) and then committed to CVS.

The next dev build will have support for resuming partially completed quizzes.

Please test!!!

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.