|
|
1 | |
|
|
2 | =head1 NAME |
|
|
3 | |
|
|
4 | PGsequentialmacros.pl |
|
|
5 | |
|
|
6 | Provides support for writing sequential problems, where certain parts |
|
|
7 | of the problem are hidden until earlier questions are answered correctly. |
|
|
8 | |
|
|
9 | |
|
|
10 | =head1 SYNPOSIS |
|
|
11 | |
|
|
12 | The basic sequential problem structure: |
|
|
13 | |
|
|
14 | DOCUMENT(); |
|
|
15 | loadMacros(.....); |
|
|
16 | ## first segment ## |
|
|
17 | BEGIN_TEXT |
|
|
18 | The first question: Enter \(sin(0) = \) \{ans_rule\}. |
|
|
19 | END_TEXT |
|
|
20 | ANS(num_cmp(0)); |
|
|
21 | if (@incorrect_answers = get_incorrect_answers( ) ) { |
|
|
22 | TEXT( "These answers are not correct ", join(" ",@incorrect_answers),$BR); |
|
|
23 | foreach my $label (@incorrect_answers) { |
|
|
24 | checkAnswer($label,debug=>1); |
|
|
25 | } |
|
|
26 | } |
|
|
27 | if (all_answers_are_correct() ) { |
|
|
28 | TEXT("$PAR Right! Now for the next part of the problem"); |
|
|
29 | } else { |
|
|
30 | STOP_RENDERING(); |
|
|
31 | } |
|
|
32 | ## second segment ## |
|
|
33 | .... |
|
|
34 | if (@incorrect_answers = get_incorrect_answers( ) ) { |
|
|
35 | TEXT( "These answers are not correct ", join(" ",@incorrect_answers),$BR); |
|
|
36 | foreach my $label (@incorrect_answers) { |
|
|
37 | checkAnswer($label,debug=>1); |
|
|
38 | } |
|
|
39 | } |
|
|
40 | if (all_answers_are_correct() ) { |
|
|
41 | TEXT("$PAR Right! Now for the next part of the problem"); |
|
|
42 | } else { |
|
|
43 | STOP_RENDERING(); |
|
|
44 | } |
|
|
45 | ## third segment ## |
|
|
46 | ENDDOCUMENT() # must be the last statement in the problem |
|
|
47 | |
|
|
48 | |
|
|
49 | |
|
|
50 | =head1 DESCRIPTION |
|
|
51 | |
|
|
52 | |
|
|
53 | =cut |
|
|
54 | |
|
|
55 | |
|
|
56 | =head2 listFormVariables |
|
|
57 | |
|
|
58 | listFormVariables(); |
|
|
59 | |
|
|
60 | Lists all variables submitted in the problem form. This is used for debugging. |
|
|
61 | |
|
|
62 | =cut |
|
|
63 | |
| 1 | sub listFormVariables { |
64 | sub listFormVariables { |
| 2 | # Lists all of the variables filled out on the input form |
65 | # Lists all of the variables filled out on the input form |
| 3 | # Useful for debugging |
66 | # Useful for debugging |
| 4 | TEXT($HR,"Form variables", ); |
67 | TEXT($HR,"Form variables", ); |
| 5 | TEXT(pretty_print($inputs_ref)); |
68 | TEXT(pretty_print($inputs_ref)); |
| 6 | TEXT("Environment",$BR); |
69 | TEXT("Environment",$BR); |
| 7 | TEXT(pretty_print(\%envir)); |
70 | TEXT(pretty_print(\%envir)); |
| 8 | TEXT($HR); |
71 | TEXT($HR); |
| 9 | } |
72 | } |
|
|
73 | |
|
|
74 | =head2 checkAnswer |
|
|
75 | |
|
|
76 | |
|
|
77 | checkAnswer($label); |
|
|
78 | |
|
|
79 | Checks the answer to the question labeled C<$label>. The result is 1 if the answer is completely correct. |
|
|
80 | 0 if the answer is wrong or partially wrong and undefined if that question has not yet |
|
|
81 | been answered. (Specifically if no answer hash is produced when the answer is evaluated |
|
|
82 | by the corresponding answer evaluator.) |
|
|
83 | |
|
|
84 | =cut |
|
|
85 | |
| 10 | sub checkAnswer { |
86 | sub checkAnswer { |
| 11 | # checks an answer on a given answer evaluator. |
87 | # checks an answer on a given answer evaluator. |
| 12 | my $answerName = shift; # get the name of the answer |
88 | my $answerName = shift; # get the name of the answer |
| 13 | my $ans_eval = get_PG_ANSWERS_HASH($answerName),; # get the answer evaluator |
89 | my $ans_eval = get_PG_ANSWERS_HASH($answerName),; # get the answer evaluator |
| 14 | my %options = @_; |
90 | my %options = @_; |
| … | |
… | |
| 24 | warn "Answer evaluator for answer $answerName is not defined" unless defined($ans_eval); |
100 | warn "Answer evaluator for answer $answerName is not defined" unless defined($ans_eval); |
| 25 | # it's ok to have a blank answer. |
101 | # it's ok to have a blank answer. |
| 26 | } |
102 | } |
| 27 | return $response; # response is (undef => no answer, 1=> correct answer, 0 => not completely correct |
103 | return $response; # response is (undef => no answer, 1=> correct answer, 0 => not completely correct |
| 28 | } |
104 | } |
|
|
105 | |
|
|
106 | =head2 listQueuedAnswers |
|
|
107 | |
|
|
108 | listQueuedAnswers(); |
|
|
109 | |
|
|
110 | Lists the labels of the answer blanks which have been printed so far. |
|
|
111 | The return value is a string which can be printed. This is mainly |
|
|
112 | used for debugging. |
|
|
113 | |
|
|
114 | =cut |
|
|
115 | |
|
|
116 | |
| 29 | sub listQueuedAnswers { |
117 | sub listQueuedAnswers { |
| 30 | # lists the names of the answer blanks so far; |
118 | # lists the names of the answer blanks so far; |
| 31 | my %pg_answers_hash = get_PG_ANSWERS_HASH(); |
119 | my %pg_answers_hash = get_PG_ANSWERS_HASH(); |
| 32 | join(" ", keys %pg_answers_hash); |
120 | join(" ", keys %pg_answers_hash); |
| 33 | } |
121 | } |
|
|
122 | |
|
|
123 | =head2 checkQueuedAnswers |
|
|
124 | |
|
|
125 | checkQueuedAnswers(); |
|
|
126 | |
|
|
127 | Returns a hash whose key/value pairs are the labels of the questions |
|
|
128 | have been printed so far and the scores obtained by evaluating the |
|
|
129 | answers to these questions. |
|
|
130 | |
|
|
131 | =cut |
|
|
132 | |
| 34 | sub checkQueuedAnswers { |
133 | sub checkQueuedAnswers { |
| 35 | # gather all of the answers submitted up to this time |
134 | # gather all of the answers submitted up to this time |
| 36 | my %options = @_; |
135 | my %options = @_; |
| 37 | my $debug = ($options{debug}) ? 1 :0; |
136 | my $debug = ($options{debug}) ? 1 :0; |
| 38 | my (%pg_answers_hash) = get_PG_ANSWERS_HASH(); |
137 | my (%pg_answers_hash) = get_PG_ANSWERS_HASH(); |
| … | |
… | |
| 40 | foreach $label (keys %pg_answers_hash) { |
139 | foreach $label (keys %pg_answers_hash) { |
| 41 | $scores{$label}=checkAnswer($label, debug=>$debug); |
140 | $scores{$label}=checkAnswer($label, debug=>$debug); |
| 42 | } |
141 | } |
| 43 | %scores; |
142 | %scores; |
| 44 | } |
143 | } |
|
|
144 | |
|
|
145 | =head2 all_answers_are_correct |
|
|
146 | |
|
|
147 | all_answers_are_correct(); |
|
|
148 | |
|
|
149 | Returns 1 if there is at least one answer and all of the questions |
|
|
150 | printed so far have been answered correctly. |
|
|
151 | |
|
|
152 | =cut |
|
|
153 | |
| 45 | sub all_answers_are_correct{ |
154 | sub all_answers_are_correct{ |
| 46 | # return 1 if all scores are 1, else it returns 0; |
155 | # return 1 if all scores are 1, else it returns 0; |
| 47 | # returns 0 if no answers have been checked yet |
156 | # returns 0 if no answers have been checked yet |
| 48 | my %scores = checkQueuedAnswers(); |
157 | my %scores = checkQueuedAnswers(); |
| 49 | return 0 unless %scores; |
158 | return 0 unless %scores; |
| 50 | my $result =1; |
159 | my $result =1; |
| 51 | foreach my $label (keys %scores) { if (not defined($scores{$label}) or $scores{$label} <1) {$result=0; last;} }; |
160 | foreach my $label (keys %scores) { if (not defined($scores{$label}) or $scores{$label} <1) {$result=0; last;} }; |
| 52 | $result; |
161 | $result; |
| 53 | } |
162 | } |
|
|
163 | |
|
|
164 | =head2 get_incorrect_answers |
|
|
165 | |
|
|
166 | get_incorrect_answers(); |
|
|
167 | |
|
|
168 | Returns a list of labels of questions which have been printed and have |
|
|
169 | been answered incorrectly. This list does NOT include blank or undefined |
|
|
170 | answers. It's possible for the returned list to be empty AND for all_answers_are_correct() |
|
|
171 | to return false. |
|
|
172 | |
|
|
173 | =cut |
|
|
174 | |
| 54 | sub get_incorrect_answers { |
175 | sub get_incorrect_answers { |
| 55 | # returns only incorrect answers, not blank or undefined answers. |
176 | # returns only incorrect answers, not blank or undefined answers. |
| 56 | my %scores = checkQueuedAnswers(); |
177 | my %scores = checkQueuedAnswers(); |
| 57 | my @incorrect = (); |
178 | my @incorrect = (); |
| 58 | foreach my $label (keys %scores) {push( @incorrect, $label) |
179 | foreach my $label (keys %scores) {push( @incorrect, $label) |