I need a hook for when the quiz score becomes final. A quiz with long-answer questions doesn't have its final score until the long-answer questions are all scored, but the hook you have now (hook_quiz_finished) fires immediately when the quiz is completed, regardless of whether the score it has is final. If there already is a way of doing this that I may have missed, I'd definitely like to know about it.
I'm about to implement this in my own copy (I'm working against alpha1 right now), but I can't really be sure that what I come up with will really be the 'appropriate' way (for example, should I account for other future question types that may require delayed scoring, and if so how?), especially since I'm relatively new to Drupal. Any suggestions would be helpful, and I will be submitting a patch soon.
As I said, I'm working against alpha1, but feel free to change the version number attached to this issue if it ought to be changed.
| Comment | File | Size | Author |
|---|---|---|---|
| #4 | quiz-quiz.module-460456-v2.patch | 511 bytes | darktygur-1 |
Comments
Comment #1
mbutcher commentedLet's see... currently your best bet is to check the is_scored flag for long answer. There is not a quiz-wide flag that indicates that an entire quiz has been scored.
Let me know if you have any ideas about how this could best be done. It is certainly a highly desirable feature. I think I would only be able to get it into Quiz 3 if it did not involve a massive change to the core quiz module.
I suppose that the best way for this to be implemented at the API level is to add an isScored() method to QuizQuestion and all implementations. Automatically scored question types would respond TRUE immediately, while long_answer and other such question types would return TRUE only when the answer had been scored.
What do you think of that idea?
Matt
Comment #2
darktygur-1 commentedWell, I just looked at the code, and I'm thinking I may be able to get away with being less invasive.
The long-answer question module calls quiz_update_total_score() to update the score of the quiz when a question is corrected. That calls quiz_calculate_score() which relies on all the question modules to score their own questions. When the modules score questions, they apparently pass back an object with several fields, including one called is_evaluated. That field appears to indicate whether the question was evaluated/corrected. The quiz_calculate_score() function keeps track of it, and the associative array it returns has an "is_evaluated" value that indicates whether all questions were 'evaluated'.
This means that quiz_update_total_score() and quiz_end_actions() can both call the new hook based on the return value from quiz_calculate_score(). If quiz_end_actions() detects that $score['is_evaluated'] is 1 (I'd personally prefer checking for a nonzero value, rather than 1 specifically), it can call the hook. Otherwise quiz_update_total_score() will call the hook later when it checks $score['is_evaluated'].
I am mildly concerned about errant calls to quiz_update_total_score() causing the hook to fire twice for the same quiz (it can happen if it gets called for an already-corrected quiz). I wonder if an extra column in the quiz_node_results table might be useful to indicate whether the results themselves are final. Then that field can just be checked before it does anything. At this point, however, that function only gets called by the long-answer question module, and only when a question is manually scored.
What do you think?
Comment #3
mbutcher commentedI think you are on the right track. It is true that there is a danger of re-firing the hook. And one of the concerns would be that sometimes a second call to quiz_update_total_score() might be good (e.g. somebody fixes a grading error) but othertimes will be (as you pointed out) errant.
Comment #4
darktygur-1 commentedHmm..as far as I know, grading errors just can't be fixed right now. I haven't noticed such a feature. If it gets offered later, perhaps it might actually be good to re-trigger anything hooking into the grade change.
While I was testing this, I realized there's no real need to invoke the new hook in quiz_end_actions(). This patch only modifies quiz_update_total_score(), and invokes it there regardless of $score['is_evaluated'].
I figure this way, things will seem more sane for module authors implementing these hooks. hook_quiz_finished() will always and only be invoked right when the quiz is finished. And hook_quiz_scored() will always and only be invoked after a question is manually scored (or multiple questions, if they ever get done more than one at a time). In both cases, module authors can check $score['is_evaluated'] themselves to see whether the current score is final:
One final thought is documentation, and I'm also wondering if things could be named better. $score['is_evaluated'] doesn't seem very intuitive.
I was about to generate a patch against the DEVELOPER.txt file, when I noticed that hook_quiz_finished is documented as
hook_quiz_finished($quiz, $score)- the extra $rid argument that actually does get passed in isn't mentioned. Is that just an oversight?Comment #5
mbutcher commentedPatch has been checked in.