Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixes bug #1854

  • Loading branch information...
commit 84250ebb1248e259e706bd5eedfe63668af00194 1 parent b348450
kaipe authored

Showing 1 changed file with 167 additions and 94 deletions. Show diff stats Hide diff stats

  1. +167 94 mod/quiz/lib.php
261 mod/quiz/lib.php
@@ -191,8 +191,8 @@ function create_response($question, $nameprefix, $questionsinuse) {
191 191 }
192 192
193 193 function extract_response($rawresponse, $nameprefix) {
194   - /// This function is very mcuh the inverse of convert_to_response_answer
195   - /// This function and convert_to_response_answer, should be
  194 + /// This function is very much the inverse of convert_to_response_answer_field
  195 + /// This function and convert_to_response_answer_field, should be
196 196 /// obsolete as soon as we get a better response storage
197 197 /// Right now they are a bridge between a consistent
198 198 /// response model and the old field answer in quiz_responses
@@ -711,8 +711,8 @@ function quiz_get_attempt_questions($quiz, $attempt, $attempting = false) {
711 711 /// Returns the questions of the quiz attempt at a format used for
712 712 /// grading and printing them...
713 713 /// On top of the ordinary persistent question fields,
714   - /// this function also set these properties
715   -
  714 + /// this function also set these properties:
  715 + //
716 716 /// ->response - contains names (as keys) and values (as values)
717 717 /// for all question html-form inputs
718 718 /// ->recentlyadded - true only if the question has been added to the quiz
@@ -724,121 +724,178 @@ function quiz_get_attempt_questions($quiz, $attempt, $attempting = false) {
724 724 global $QUIZ_QTYPES;
725 725 global $CFG;
726 726
  727 + /////////////////////////
727 728 /// Get the questions:
  729 + /////////////////////////
728 730 if (!($questions =
729 731 get_records_list('quiz_questions', 'id', $quiz->questions))) {
730 732 notify('Error when reading questions from the database!');
731 733 return false;
732 734 }
733 735
734   - /// Retrieve ->maxgrade for all questions
  736 + ////////////////////////////////////////////
  737 + /// Determine ->maxgrade for all questions
  738 + ////////////////////////////////////////////
735 739 If (!($grades = quiz_get_question_grades($quiz->id, $quiz->questions))) {
736 740 $grades = array();
737 741 }
738   -
739   - /// Get any existing responses on this attempt:
740   - if (!($rawresponses = get_records_sql
741   - ("SELECT question, answer, attempt FROM {$CFG->prefix}quiz_responses
742   - WHERE attempt = '$attempt->id'
743   - AND question IN ($quiz->questions)"))
744   - and $quiz->attemptonlast
745   - // Try to get responses from the previous attempt:
746   - and $lastattemptnum = $attempt->attempt - 1) {
747   - do {
748   - $lastattempt = get_record('quiz_attempts',
749   - 'quiz', $quiz->id,
750   - 'userid', $attempt->userid,
751   - 'attempt', $lastattemptnum);
752   - } while(empty($lastattempt) && --$lastattemptnum);
753   -
754   - if (0 == $lastattemptnum or
755   - !($rawresponses = get_records_sql
756   - ("SELECT question, answer, attempt
757   - FROM {$CFG->prefix}quiz_responses
758   - WHERE attempt = '$lastattempt->id'
759   - AND question IN ($quiz->questions)"))) {
760   - $rawresponses = array();
761   - } else {
762   - /// We found a last attempt that is now to be used:
763   -
764   - /// This line can be uncommented for debuging
765   - // echo "Last attempt is $lastattempt->id with number $lastattemptnum";
766   - }
767   - }
768   -
769   - /// Set the additional question properties
770   - /// response, recentlyadded and grade
771 742 foreach ($questions as $qid => $question) {
772   -
773 743 if (isset($grades[$qid])) {
774 744 $questions[$qid]->maxgrade = $grades[$qid]->grade;
775 745 } else {
776 746 $questions[$qid]->maxgrade = 0.0;
777 747 }
778   -
779   - if (isset($rawresponses[$qid])) {
780   - $questions[$qid]->response = $QUIZ_QTYPES[$question->qtype]
781   - ->extract_response($rawresponses[$qid],
782   - quiz_qtype_nameprefix($question));
783   - $questions[$qid]->recentlyadded = false;
784   - } else {
785   - $questions[$qid]->response = array();
786   - $questions[$qid]->recentlyadded = !empty($rawresponses);
787   - }
788 748 }
  749 +
  750 + //////////////////////////////////////////////////////////////
  751 + /// Determine attributes ->response and ->recentlyadded (hard)
  752 + //////////////////////////////////////////////////////////////
  753 +
  754 + /// Get all existing responses on this attempt
  755 + $rawresponses = get_records_sql("
  756 + SELECT question, answer, attempt
  757 + FROM {$CFG->prefix}quiz_responses
  758 + WHERE attempt = '$attempt->id' ");
789 759
  760 + /// The setting for ->recentlyadded depends on whether this is
  761 + /// a test attempt of just a review
790 762 if ($attempting) {
791   - /// Questions are requested for a test attempt that is
792   - /// about to start and there are no responses to reuse
793   - /// for current question, so we need to create new ones...
794   -
795   - /// For the case of wrapping question types that can
796   - /// wrap other arbitrary questions, there is a need
797   - /// to make sure that no question will appear twice
798   - /// in the quiz attempt:
  763 + /// This is a test attempt so there is a need to create responses
  764 + /// in case there are none existing.
  765 + /// Further - the attribute recentlyadded is determined from
  766 + /// whether the question has a response in the previous attempt,
  767 + /// which might be used in case the attemptonlast quiz option
  768 + /// is true.
  769 +
  770 + $prevattempt = $attempt->attempt;
  771 + $prevresponses= array();
  772 + while (--$prevattempt) {
  773 + $prevresponses = get_records_sql("
  774 + SELECT r.question, r.answer, r.attempt
  775 + FROM {$CFG->prefix}quiz_responses r, {$CFG->prefix}quiz_attempts a
  776 + WHERE a.quiz='$quiz->id' AND a.userid='$attempt->userid'
  777 + AND a.attempt='$prevattempt' AND r.attempt=a.id ");
  778 + if (!empty($prevresponses)) {
  779 + break;
  780 + }
  781 + }
799 782
800   - $questionsinuse = $quiz->questions;
801   - foreach ($questions as $question) {
802   - if ($wrapped = $QUIZ_QTYPES[$question->qtype]->wrapped_questions
803   - ($question, quiz_qtype_nameprefix($question))) {
804   - $questionsinuse .= ",$wrapped";
  783 + $questionsinuse = $quiz->questions; // used if responses must be created
  784 + foreach ($questions as $qid => $question) {
  785 + if ($questions[$qid]->recentlyadded =
  786 + $prevattempt && empty($prevresponses[$qid])) {
  787 + /* No action */
  788 +
  789 + } else if ($prevattempt && $quiz->attemptonlast
  790 + && empty($rawresponses[$qid])) {
  791 + /// Store the previous response on this attempt!
  792 + $rawresponses[$qid] = $prevresponses[$qid];
  793 + $rawresponses[$qid]->attempt = $attempt->id;
  794 + $rawresponses[$qid]->id =
  795 + insert_record("quiz_responses", $rawresponses[$qid])
  796 + or error("Unable to create attemptonlast response for question $qid");
  797 +
  798 + ///////////////////////////////////////////
  799 + /// WORKAROUND FOR QUESTION TYPE RANDOM ///
  800 + ///////////////////////////////////////////
  801 + if (RANDOM == $question->qtype) {
  802 + $randomqid = $prevresponses[$qid]->answer;
  803 + if (empty($prevresponses[$randomqid]) || ereg(
  804 + "(^|,)$randomqid(,|$)", $questionsinuse)) {
  805 + // Ooops!
  806 + // The randomly picked question has been included
  807 + // among the fixed ones or did not get any response
  808 + // in the previous attempt - either way the raw
  809 + // responserecord created above needs to go!
  810 + delete_records('quiz_responses', 'id',
  811 + $rawresponses[$qid]->id);
  812 + unset($rawresponses[$qid]);
  813 +
  814 + } else if (empty($rawresponses[$randomqid])) {
  815 + /// Also copy this response from the previous attempt
  816 + $rawresponses[$randomqid] = $prevresponses[$randomqid];
  817 + $rawresponses[$randomqid]->attempt = $attempt->id;
  818 + $rawresponses[$randomqid]->id =
  819 + insert_record('quiz_responses', $rawresponses[$randomqid])
  820 + or error("Unable to create attemptonlast response for question $qid");
  821 +
  822 + }
  823 + } ////// END OF WORKAROUND ///////
  824 + }
  825 +
  826 + /* Extract possible response and its wrapped questions */
  827 + if (!empty($rawresponses[$qid])) {
  828 + $questions[$qid]->response = $QUIZ_QTYPES[$question->qtype]
  829 + ->extract_response($rawresponses[$qid],
  830 + quiz_qtype_nameprefix($question));
  831 + /// Catch any additional wrapped questions:
  832 + if ($wrapped = $QUIZ_QTYPES[$question->qtype]
  833 + ->wrapped_questions($questions[$question->id],
  834 + quiz_qtype_nameprefix($question))) {
  835 + $questionsinuse .= ",$wrapped";
  836 + }
805 837 }
806 838 }
807 839
808 840 /// Make sure all the questions will have responses:
809 841 foreach ($questions as $question) {
810 842 if (empty($question->response)) {
  843 + /// No response on this question
  844 +
811 845 $nameprefix = quiz_qtype_nameprefix($question);
812 846 $questions[$question->id]->response =
813 847 $QUIZ_QTYPES[$question->qtype]->create_response
814 848 ($question, $nameprefix, $questionsinuse);
815 849
816   - //////////////////////////////////////////////////
817   - // In the future, a nice feature could be to save
818   - // the created response right here, so that if a
819   - // student quits the quiz without saving, the
820   - // student will have the oppertunity to go back
821   - // to same quiz if he/she restarts the attempt.
822   - // Today, the student gets new RANDOM questions
823   - // whenever he/she restarts the quiz attempt.
824   - //////////////////////////////////////////////////
825   - // The above would also open the door for a new
826   - // quiz feature that allows the student to save
827   - // all responses if he/she needs to switch computer
828   - // or have any other break in the middle of the quiz.
829   - // (Or simply because the student feels more secure
830   - // if he/she has the chance to save the responses
831   - // a number of times during the quiz.)
832   - //////////////////////////////////////////////////
  850 + //////////////////////////////////////////////
  851 + // Saving the newly created response before
  852 + // continuing with the quiz...
  853 + //////////////////////////////////////////////
  854 + $responserecord->attempt = $attempt->id;
  855 + $responserecord->question = $question->id;
  856 + $responserecord->answer = $QUIZ_QTYPES[$question->qtype]
  857 + ->convert_to_response_answer_field
  858 + ($questions[$question->id]->response);
  859 +
  860 + ///////////////////////////////////////////
  861 + // WORKAROUND for question type RANDOM:
  862 + ///////////////////////////////////////////
  863 + if ($question->qtype == RANDOM and ereg(
  864 + '^random([0-9]+)-(.*)$', $responserecord->answer, $afields)) {
  865 + $responserecord->answer = $afields[1];
  866 + insert_record("quiz_responses", $responserecord)
  867 + or error("Unable to create an initial random response for question $question->id");
  868 +
  869 + $responserecord->question = $responserecord->answer;
  870 + $responserecord->answer = $afields[2];
  871 + } /// End of WORKAROUND //////////////////////
  872 +
  873 + insert_record("quiz_responses", $responserecord)
  874 + or error("Unable to create initial response for question $question->id");
833 875
834 876 /// Catch any additional wrapped questions:
835 877 if ($wrapped = $QUIZ_QTYPES[$question->qtype]
836 878 ->wrapped_questions($questions[$question->id],
837   - $nameprefix)) {
  879 + quiz_qtype_nameprefix($question))) {
838 880 $questionsinuse .= ",$wrapped";
839 881 }
840 882 }
841 883 }
  884 +
  885 + } else {
  886 + /// In the case of review, the recentlyadded flag is set true
  887 + /// when the question has been added after the attempt and new
  888 + /// responses are never created
  889 +
  890 + foreach ($questions as $qid => $question) {
  891 + if ($questions[$qid]->recentlyadded = empty($rawresponses[$qid])) {
  892 + /* No action */
  893 + } else {
  894 + $questions[$qid]->response = $QUIZ_QTYPES[$question->qtype]
  895 + ->extract_response($rawresponses[$qid],
  896 + quiz_qtype_nameprefix($question));
  897 + }
  898 + }
842 899 }
843 900
844 901 return $questions;
@@ -1598,7 +1655,7 @@ function quiz_save_attempt($quiz, $questions, $result, $attemptnum) {
1598 1655 $attempt->timefinish = time();
1599 1656 $attempt->timemodified = time();
1600 1657
1601   - if (! update_record("quiz_attempts", $attempt)) {
  1658 + if (!update_record("quiz_attempts", $attempt)) {
1602 1659 notify("Error while saving attempt");
1603 1660 return false;
1604 1661 }
@@ -1606,32 +1663,48 @@ function quiz_save_attempt($quiz, $questions, $result, $attemptnum) {
1606 1663 // Now let's save all the questions for this attempt
1607 1664
1608 1665 foreach ($questions as $question) {
1609   - $response->attempt = $attempt->id;
1610   - $response->grade = $result->details[$question->id]->grade;
1611   - $response->question = $question->id;
1612 1666
  1667 + // Fetch the response record for this question...
  1668 + $response = get_record('quiz_responses',
  1669 + 'attempt', $attempt->id, 'question', $question->id);
  1670 +
  1671 + $response->grade = $result->details[$question->id]->grade;
  1672 +
1613 1673 if (!empty($question->response)) {
1614   - $response->answer = $QUIZ_QTYPES[$question->qtype]
  1674 + $responseanswerfield = $QUIZ_QTYPES[$question->qtype]
1615 1675 ->convert_to_response_answer_field($question->response);
1616 1676
1617 1677 ///////////////////////////////////////////
1618 1678 // WORKAROUND for question type RANDOM:
1619 1679 ///////////////////////////////////////////
1620   - if ($question->qtype == RANDOM and
1621   - ereg('^random([0-9]+)-(.*)$', $response->answer, $afields)) {
1622   - $response->answer = $afields[1];
1623   - if (!insert_record("quiz_responses", $response)) {
1624   - notify("Error while saving response");
  1680 + if ($question->qtype == RANDOM) {
  1681 +
  1682 + /// This will update the grade only
  1683 + /// Everything else must already be in place...
  1684 + if (!update_record('quiz_responses', $response)) {
  1685 + notify("Error while saving grade on random response");
1625 1686 return false;
1626 1687 }
1627   - $response->question = $response->answer;
1628   - $response->answer = $afields[2];
  1688 +
  1689 + /// Rescue grade before fetching response record for actual question
  1690 + $responsegradefield = $response->grade;
  1691 +
  1692 + /// Fetch the response record containing
  1693 + /// the response on the actual question
  1694 + $response = get_record('quiz_responses',
  1695 + 'attempt', $attempt->id, 'question', $response->answer);
  1696 +
  1697 + $response->grade = $responsegradefield;
  1698 + ereg('^random[0-9]+-(.*)$', $responseanswerfield, $afields);
  1699 + $responseanswerfield = $afields[1];
1629 1700 } /// End of WORKAROUND //////////////////////
1630 1701
  1702 + $response->answer = $responseanswerfield;
1631 1703 } else {
1632   - $response->answer = "";
  1704 + $response->answer = '';
1633 1705 }
1634   - if (!insert_record("quiz_responses", $response)) {
  1706 +
  1707 + if (!update_record("quiz_responses", $response)) {
1635 1708 notify("Error while saving response");
1636 1709 return false;
1637 1710 }

0 comments on commit 84250eb

Please sign in to comment.
Something went wrong with that request. Please try again.