| … | |
… | |
| 18 | use WeBWorK::Form; |
18 | use WeBWorK::Form; |
| 19 | use WeBWorK::PG; |
19 | use WeBWorK::PG; |
| 20 | use WeBWorK::Utils qw(ref2string encodeAnswers decodeAnswers); |
20 | use WeBWorK::Utils qw(ref2string encodeAnswers decodeAnswers); |
| 21 | |
21 | |
| 22 | # TODO: |
22 | # TODO: |
| 23 | # :) enforce permissions for showCorrectAnswers and showSolutions |
|
|
| 24 | # (use $PRIV = $canPRIV && ($wantPRIV || $mustPRIV) -- cool syntax!) |
|
|
| 25 | # :) if answers were not submitted and there are student answers in the DB, |
|
|
| 26 | # decode them and put them into $formFields for the translator |
|
|
| 27 | # :) store submitted answers hash in database for sticky answers |
|
|
| 28 | # :) deal with the results of answer evaluation and grading :p |
|
|
| 29 | # :) introduce a recordAnswers option, which works on the same principle as |
|
|
| 30 | # the other permission-based options |
|
|
| 31 | # 7. make warnings work |
23 | # 7. make warnings work |
| 32 | |
24 | |
| 33 | ############################################################ |
25 | ############################################################ |
| 34 | # |
26 | # |
| 35 | # user |
27 | # user |
| … | |
… | |
| 46 | # redisplay - name of the "Redisplay Problem" button |
38 | # redisplay - name of the "Redisplay Problem" button |
| 47 | # submitAnswers - name of "Submit Answers" button |
39 | # submitAnswers - name of "Submit Answers" button |
| 48 | # |
40 | # |
| 49 | ############################################################ |
41 | ############################################################ |
| 50 | |
42 | |
| 51 | sub prepare { |
43 | sub pre_header_initialize { |
| 52 | my ($self, $setName, $problemNumber) = @_; |
44 | my ($self, $setName, $problemNumber) = @_; |
| 53 | my $courseEnv = $self->{courseEnvironment}; |
45 | my $courseEnv = $self->{courseEnvironment}; |
| 54 | my $r = $self->{r}; |
46 | my $r = $self->{r}; |
| 55 | my $userName = $r->param('user'); |
47 | my $userName = $r->param('user'); |
| 56 | |
48 | |
| … | |
… | |
| 165 | $self->{will} = \%will; |
157 | $self->{will} = \%will; |
| 166 | |
158 | |
| 167 | $self->{pg} = $pg; |
159 | $self->{pg} = $pg; |
| 168 | } |
160 | } |
| 169 | |
161 | |
|
|
162 | #sub header { |
|
|
163 | # # *** we need to print $pg->{header_text} here! |
|
|
164 | #} |
|
|
165 | |
|
|
166 | sub path { |
|
|
167 | my $self = shift; |
|
|
168 | my $args = $_[-1]; |
|
|
169 | my $setName = $self->{set}->id; |
|
|
170 | my $problemNumber = $self->{problem}->id; |
|
|
171 | |
|
|
172 | my $ce = $self->{courseEnvironment}; |
|
|
173 | my $root = $ce->{webworkURLs}->{root}; |
|
|
174 | my $courseName = $ce->{courseName}; |
|
|
175 | return $self->pathMacro($args, |
|
|
176 | "Home" => "$root", |
|
|
177 | $courseName => "$root/$courseName", |
|
|
178 | $setName => "$root/$courseName/set$setName", |
|
|
179 | "Problem $problemNumber" => "", |
|
|
180 | ); |
|
|
181 | } |
|
|
182 | |
|
|
183 | sub siblings { |
|
|
184 | my $self = shift; |
|
|
185 | my $setName = $self->{set}->id; |
|
|
186 | my $problemNumber = $self->{problem}->id; |
|
|
187 | |
|
|
188 | my $ce = $self->{courseEnvironment}; |
|
|
189 | my $root = $ce->{webworkURLs}->{root}; |
|
|
190 | my $courseName = $ce->{courseName}; |
|
|
191 | |
|
|
192 | my $wwdb = $self->{wwdb}; |
|
|
193 | my $user = $self->{r}->param("user"); |
|
|
194 | my @problems; |
|
|
195 | push @problems, $wwdb->getProblem($user, $setName, $_) |
|
|
196 | foreach ($wwdb->getProblems($user, $setName)); |
|
|
197 | foreach my $problem (sort { $a->id <=> $b->id } @problems) { |
|
|
198 | print CGI::a({-href=>"$root/$courseName/$setName/".$problem->id."/?" |
|
|
199 | . $self->url_authen_args}, "Problem ".$problem->id), CGI::br(); |
|
|
200 | } |
|
|
201 | } |
|
|
202 | |
|
|
203 | sub nav { |
|
|
204 | my $self = shift; |
|
|
205 | my $args = $_[-1]; |
|
|
206 | my $setName = $self->{set}->id; |
|
|
207 | my $problemNumber = $self->{problem}->id; |
|
|
208 | |
|
|
209 | my $ce = $self->{courseEnvironment}; |
|
|
210 | my $root = $ce->{webworkURLs}->{root}; |
|
|
211 | my $courseName = $ce->{courseName}; |
|
|
212 | |
|
|
213 | my $wwdb = $self->{wwdb}; |
|
|
214 | my $user = $self->{r}->param("user"); |
|
|
215 | |
|
|
216 | my @links = ("Problem List" => "$root/$courseName/set$setName"); |
|
|
217 | |
|
|
218 | my $prevProblem = $wwdb->getProblem($user, $setName, $problemNumber-1); |
|
|
219 | my $nextProblem = $wwdb->getProblem($user, $setName, $problemNumber+1); |
|
|
220 | unshift @links, "Previous Problem" => "$root/$courseName/set$setName/prob".$prevProblem->id |
|
|
221 | if $prevProblem; |
|
|
222 | push @links, "Next Problem" => "$root/$courseName/set$setName/prob".$nextProblem->id |
|
|
223 | if $nextProblem; |
|
|
224 | |
|
|
225 | return $self->navMacro($args, @links); |
|
|
226 | } |
|
|
227 | |
| 170 | sub title { |
228 | sub title { |
| 171 | my $self = shift; |
229 | my $self = shift; |
| 172 | #return "Set " . $self->{set}->id . " problem " . $self->{problem}->id; |
230 | my $setName = $self->{set}->id; |
| 173 | return "hold on a sec"; |
231 | my $problemNumber = $self->{problem}->id; |
|
|
232 | |
|
|
233 | return "$setName : Problem $problemNumber"; |
| 174 | } |
234 | } |
| 175 | |
235 | |
| 176 | sub body { |
236 | sub body { |
| 177 | my $self = shift; |
237 | my $self = shift; |
| 178 | |
238 | |
| 179 | $self->prepare(@_); |
239 | #$self->prepare(@_); |
| 180 | |
240 | |
| 181 | # unpack some useful variables |
241 | # unpack some useful variables |
| 182 | my $r = $self->{r}; |
242 | my $r = $self->{r}; |
| 183 | my $wwdb = $self->{wwdb}; |
243 | my $wwdb = $self->{wwdb}; |
| 184 | my $set = $self->{set}; |
244 | my $set = $self->{set}; |
| 185 | my $problem = $self->{problem}; |
245 | my $problem = $self->{problem}; |
|
|
246 | my $permissionLevel = $self->{permissionLevel}; |
| 186 | my $submitAnswers = $self->{submitAnswers}; |
247 | my $submitAnswers = $self->{submitAnswers}; |
| 187 | my %will = %{ $self->{will} }; |
248 | my %will = %{ $self->{will} }; |
| 188 | my $pg = $self->{pg}; |
249 | my $pg = $self->{pg}; |
| 189 | |
250 | |
| 190 | ##### translation errors? ##### |
251 | ##### translation errors? ##### |
| 191 | |
252 | |
| 192 | if ($pg->{flags}->{error_flag}) { |
253 | if ($pg->{flags}->{error_flag}) { |
| 193 | print translationError($pg->{errors}, $pg->{body_text}); |
254 | print translationError($pg->{errors}, $pg->{body_text}); |
| … | |
… | |
| 212 | if ($will{recordAnswers}) { |
273 | if ($will{recordAnswers}) { |
| 213 | $problem->attempted(1); |
274 | $problem->attempted(1); |
| 214 | $problem->status($pg->{state}->{recorded_score}); |
275 | $problem->status($pg->{state}->{recorded_score}); |
| 215 | $problem->num_correct($pg->{state}->{num_of_correct_ans}); |
276 | $problem->num_correct($pg->{state}->{num_of_correct_ans}); |
| 216 | $problem->num_incorrect($pg->{state}->{num_of_incorrect_ans}); |
277 | $problem->num_incorrect($pg->{state}->{num_of_incorrect_ans}); |
| 217 | #warn "Would have stored the following:\n", |
|
|
| 218 | # $problem->toString, "\n"; |
|
|
| 219 | $wwdb->setProblem($problem); |
278 | $wwdb->setProblem($problem); |
| 220 | } |
279 | } |
| 221 | } |
280 | } |
| 222 | |
281 | |
| 223 | ##### output ##### |
282 | ##### output ##### |
| … | |
… | |
| 240 | $attemptsLeftNoun = "attempts"; |
299 | $attemptsLeftNoun = "attempts"; |
| 241 | } else { |
300 | } else { |
| 242 | $attemptsLeft = $problem->max_attempts - $attempts; |
301 | $attemptsLeft = $problem->max_attempts - $attempts; |
| 243 | $attemptsLeftNoun = $attemptsLeft == 1 ? "attempt" : "attempts"; |
302 | $attemptsLeftNoun = $attemptsLeft == 1 ? "attempt" : "attempts"; |
| 244 | } |
303 | } |
|
|
304 | my $setClosedMessage; |
|
|
305 | if (time < $set->open_date or time > $set->due_date) { |
|
|
306 | $setClosedMessage = "This problem set is closed."; |
|
|
307 | if ($permissionLevel > 0) { |
|
|
308 | $setClosedMessage .= " Since you are a privileged user, additional attempts will be recorded."; |
|
|
309 | } else { |
|
|
310 | $setClosedMessage .= " Additional attempts will not be recorded."; |
|
|
311 | } |
|
|
312 | } |
| 245 | print CGI::p( |
313 | print CGI::p( |
| 246 | "You have attempted this problem $attempts $attemptsNoun.", CGI::br(), |
314 | "You have attempted this problem $attempts $attemptsNoun.", CGI::br(), |
| 247 | $problem->attempted |
315 | $problem->attempted |
| 248 | ? "Your recorded score is $lastScore." . CGI::br() |
316 | ? "Your recorded score is $lastScore." . CGI::br() |
| 249 | : "", |
317 | : "", |
| 250 | "You have $attemptsLeft $attemptsLeftNoun remaining." |
318 | "You have $attemptsLeft $attemptsLeftNoun remaining.", CGI::br(), |
|
|
319 | $setClosedMessage, |
| 251 | ); |
320 | ); |
| 252 | |
321 | |
| 253 | # BY THE WAY.......... |
322 | # BY THE WAY.......... |
| 254 | # we have to figure out some way to tell the student if their NEW answer, |
323 | # we have to figure out some way to tell the student if their NEW answer, |
| 255 | # on THIS attempt, has been recorded. however, this is decided in part by |
324 | # on THIS attempt, has been recorded. however, this is decided in part by |
| … | |
… | |
| 338 | |
407 | |
| 339 | my $numCorrectNoun = $numCorrect == 1 ? "question" : "questions"; |
408 | my $numCorrectNoun = $numCorrect == 1 ? "question" : "questions"; |
| 340 | my $scorePercent = int ($problemResult->{score} * 100) . "\%"; |
409 | my $scorePercent = int ($problemResult->{score} * 100) . "\%"; |
| 341 | my $summary = "On this attempt, you answered $numCorrect $numCorrectNoun out of " |
410 | my $summary = "On this attempt, you answered $numCorrect $numCorrectNoun out of " |
| 342 | . scalar @answerNames . " correct, for a score of $scorePercent."; |
411 | . scalar @answerNames . " correct, for a score of $scorePercent."; |
| 343 | return CGI::table({-border=>1}, CGI::tr(\@tableRows)) . CGI::p($summary); |
412 | return CGI::table({-border=>1}, CGI::Tr(\@tableRows)) . CGI::p($summary); |
| 344 | } |
413 | } |
| 345 | |
414 | |
| 346 | sub viewOptions($) { |
415 | sub viewOptions($) { |
| 347 | my $self = shift; |
416 | my $self = shift; |
| 348 | my $displayMode = $self->{displayMode}; |
417 | my $displayMode = $self->{displayMode}; |