| … | |
… | |
| 248 | |
248 | |
| 249 | my $wwdb = $self->{wwdb}; |
249 | my $wwdb = $self->{wwdb}; |
| 250 | my $effectiveUser = $self->{r}->param("effectiveUser"); |
250 | my $effectiveUser = $self->{r}->param("effectiveUser"); |
| 251 | my $tail = "&displayMode=".$self->{displayMode}; |
251 | my $tail = "&displayMode=".$self->{displayMode}; |
| 252 | |
252 | |
| 253 | my @links = ("Problem List" => "$root/$courseName/$setName"); |
253 | my @links = ("Problem List" , "$root/$courseName/$setName", "ProbList"); |
| 254 | |
254 | |
| 255 | my $prevProblem = $wwdb->getProblem($effectiveUser, $setName, $problemNumber-1); |
255 | my $prevProblem = $wwdb->getProblem($effectiveUser, $setName, $problemNumber-1); |
| 256 | my $nextProblem = $wwdb->getProblem($effectiveUser, $setName, $problemNumber+1); |
256 | my $nextProblem = $wwdb->getProblem($effectiveUser, $setName, $problemNumber+1); |
| 257 | unshift @links, "Previous Problem" => $prevProblem |
257 | unshift @links, "Previous Problem" , ($prevProblem |
| 258 | ? "$root/$courseName/$setName/".$prevProblem->id |
258 | ? "$root/$courseName/$setName/".$prevProblem->id |
| 259 | : ""; |
259 | : "") , "Prev"; |
| 260 | push @links, "Next Problem" => $nextProblem |
260 | push @links, "Next Problem" , ($nextProblem |
| 261 | ? "$root/$courseName/$setName/".$nextProblem->id |
261 | ? "$root/$courseName/$setName/".$nextProblem->id |
| 262 | : ""; |
262 | : "") , "Next"; |
| 263 | |
263 | |
| 264 | return $self->navMacro($args, $tail, @links); |
264 | return $self->navMacro($args, $tail, @links); |
| 265 | } |
265 | } |
| 266 | |
266 | |
| 267 | sub title { |
267 | sub title { |
| … | |
… | |
| 337 | ); |
337 | ); |
| 338 | } |
338 | } |
| 339 | } |
339 | } |
| 340 | |
340 | |
| 341 | ##### output ##### |
341 | ##### output ##### |
| 342 | |
342 | print CGI::start_div({class=>"problemHeader"}); |
| 343 | # attempt summary |
343 | # attempt summary |
| 344 | if ($submitAnswers or $will{showCorrectAnswers}) { |
344 | if ($submitAnswers or $will{showCorrectAnswers}) { |
| 345 | # print this if user submitted answers OR requested correct answers |
345 | # print this if user submitted answers OR requested correct answers |
| 346 | print $self->attemptResults($pg, $submitAnswers, |
346 | print $self->attemptResults($pg, $submitAnswers, |
| 347 | $will{showCorrectAnswers}, |
347 | $will{showCorrectAnswers}, |
| 348 | $pg->{flags}->{showPartialCorrectAnswers}, 0); |
348 | $pg->{flags}->{showPartialCorrectAnswers}, 1, 0); |
| 349 | } elsif ($checkAnswers) { |
349 | } elsif ($checkAnswers) { |
| 350 | # print this if user previewed answers |
350 | # print this if user previewed answers |
| 351 | print $self->attemptResults($pg, 1, 0, 1, 0); |
351 | print $self->attemptResults($pg, 1, 0, 1, 1, 0); |
| 352 | # show attempt answers |
352 | # show attempt answers |
| 353 | # don't show correct answers |
353 | # don't show correct answers |
| 354 | # show attempt results (correctness) |
354 | # show attempt results (correctness) |
| 355 | # don't show attempt previews |
355 | # don't show attempt previews |
| 356 | } elsif ($previewAnswers) { |
356 | } elsif ($previewAnswers) { |
| 357 | # print this if user previewed answers |
357 | # print this if user previewed answers |
| 358 | print $self->attemptResults($pg, 1, 0, 0, 1); |
358 | print $self->attemptResults($pg, 1, 0, 0, 0, 1); |
| 359 | # show attempt answers |
359 | # show attempt answers |
| 360 | # don't show correct answers |
360 | # don't show correct answers |
| 361 | # don't show attempt results (correctness) |
361 | # don't show attempt results (correctness) |
| 362 | # show attempt previews |
362 | # show attempt previews |
| 363 | } |
363 | } |
| 364 | |
364 | |
|
|
365 | print CGI::end_div(); |
|
|
366 | |
|
|
367 | print CGI::start_div({class=>"problem"}); |
|
|
368 | #print CGI::hr(); |
|
|
369 | # main form |
|
|
370 | print |
|
|
371 | CGI::startform("POST", $r->uri), |
|
|
372 | $self->hidden_authen_fields, |
|
|
373 | CGI::p($pg->{body_text}), |
|
|
374 | CGI::p($pg->{result}->{msg} ? CGI::b("Note: ") : "", CGI::i($pg->{result}->{msg})), |
|
|
375 | CGI::p( |
|
|
376 | ($can{recordAnswers} |
|
|
377 | ? CGI::submit(-name=>"submitAnswers", |
|
|
378 | -label=>"Submit Answers") |
|
|
379 | : ""), |
|
|
380 | ($can{checkAnswers} |
|
|
381 | ? CGI::submit(-name=>"checkAnswers", |
|
|
382 | -label=>"Check Answers") |
|
|
383 | : ""), |
|
|
384 | CGI::submit(-name=>"previewAnswers", |
|
|
385 | -label=>"Preview Answers"), |
|
|
386 | ); |
| 365 | # score summary |
387 | # score summary |
| 366 | my $attempts = $problem->num_correct + $problem->num_incorrect; |
388 | my $attempts = $problem->num_correct + $problem->num_incorrect; |
| 367 | my $attemptsNoun = $attempts != 1 ? "times" : "time"; |
389 | my $attemptsNoun = $attempts != 1 ? "times" : "time"; |
| 368 | my $lastScore = int ($problem->status * 100) . "%"; |
390 | my $lastScore = int ($problem->status * 100) . "%"; |
| 369 | my ($attemptsLeft, $attemptsLeftNoun); |
391 | my ($attemptsLeft, $attemptsLeftNoun); |
| … | |
… | |
| 373 | $attemptsLeftNoun = "attempts"; |
395 | $attemptsLeftNoun = "attempts"; |
| 374 | } else { |
396 | } else { |
| 375 | $attemptsLeft = $problem->max_attempts - $attempts; |
397 | $attemptsLeft = $problem->max_attempts - $attempts; |
| 376 | $attemptsLeftNoun = $attemptsLeft == 1 ? "attempt" : "attempts"; |
398 | $attemptsLeftNoun = $attemptsLeft == 1 ? "attempt" : "attempts"; |
| 377 | } |
399 | } |
|
|
400 | |
|
|
401 | my $setClosed = 0; |
| 378 | my $setClosedMessage; |
402 | my $setClosedMessage; |
| 379 | if (time < $set->open_date or time > $set->due_date) { |
403 | if (time < $set->open_date or time > $set->due_date) { |
|
|
404 | $setClosed = 1; |
| 380 | $setClosedMessage = "This problem set is closed."; |
405 | $setClosedMessage = "This problem set is closed."; |
| 381 | if ($permissionLevel > 0) { |
406 | if ($permissionLevel > 0) { |
| 382 | $setClosedMessage .= " Since you are a privileged user, additional attempts will be recorded."; |
407 | $setClosedMessage .= " Since you are a privileged user, additional attempts will be recorded."; |
| 383 | } else { |
408 | } else { |
| 384 | $setClosedMessage .= " Additional attempts will not be recorded."; |
409 | $setClosedMessage .= " Additional attempts will not be recorded."; |
| … | |
… | |
| 387 | print CGI::p( |
412 | print CGI::p( |
| 388 | "You have attempted this problem $attempts $attemptsNoun.", CGI::br(), |
413 | "You have attempted this problem $attempts $attemptsNoun.", CGI::br(), |
| 389 | $problem->attempted |
414 | $problem->attempted |
| 390 | ? "Your recorded score is $lastScore." . CGI::br() |
415 | ? "Your recorded score is $lastScore." . CGI::br() |
| 391 | : "", |
416 | : "", |
| 392 | "You have $attemptsLeft $attemptsLeftNoun remaining.", CGI::br(), |
417 | $setClosed ? $setClosedMessage : "You have $attemptsLeft $attemptsLeftNoun remaining." |
| 393 | $setClosedMessage, |
|
|
| 394 | ); |
418 | ); |
| 395 | |
419 | |
| 396 | print CGI::hr(); |
420 | |
| 397 | |
|
|
| 398 | # main form |
|
|
| 399 | print |
421 | print |
| 400 | CGI::startform("POST", $r->uri), |
|
|
| 401 | $self->hidden_authen_fields, |
|
|
| 402 | CGI::p(CGI::i($pg->{result}->{msg})), |
|
|
| 403 | CGI::p($pg->{body_text}), |
|
|
| 404 | CGI::p( |
|
|
| 405 | ($can{recordAnswers} |
|
|
| 406 | ? CGI::submit(-name=>"submitAnswers", |
|
|
| 407 | -label=>"Submit Answers") |
|
|
| 408 | : ""), |
|
|
| 409 | ($can{checkAnswers} |
|
|
| 410 | ? CGI::submit(-name=>"checkAnswers", |
|
|
| 411 | -label=>"Check Answers") |
|
|
| 412 | : ""), |
|
|
| 413 | CGI::submit(-name=>"previewAnswers", |
|
|
| 414 | -label=>"Preview Answers"), |
|
|
| 415 | ), |
|
|
| 416 | $self->viewOptions(), |
422 | $self->viewOptions(), |
| 417 | CGI::endform(); |
423 | CGI::endform(); |
| 418 | |
424 | |
|
|
425 | print CGI::end_div(); |
| 419 | # feedback form |
426 | # feedback form |
| 420 | my $ce = $self->{courseEnvironment}; |
427 | my $ce = $self->{courseEnvironment}; |
| 421 | my $root = $ce->{webworkURLs}->{root}; |
428 | my $root = $ce->{webworkURLs}->{root}; |
| 422 | my $courseName = $ce->{courseName}; |
429 | my $courseName = $ce->{courseName}; |
| 423 | my $feedbackURL = "$root/$courseName/feedback/"; |
430 | my $feedbackURL = "$root/$courseName/feedback/"; |
| … | |
… | |
| 462 | return ""; |
469 | return ""; |
| 463 | } |
470 | } |
| 464 | |
471 | |
| 465 | ##### output utilities ##### |
472 | ##### output utilities ##### |
| 466 | |
473 | |
| 467 | sub attemptResults($$$$$) { |
474 | sub attemptResults($$$$$$) { |
| 468 | my $self = shift; |
475 | my $self = shift; |
| 469 | my $pg = shift; |
476 | my $pg = shift; |
| 470 | my $showAttemptAnswers = shift; |
477 | my $showAttemptAnswers = shift; |
| 471 | my $showCorrectAnswers = shift; |
478 | my $showCorrectAnswers = shift; |
| 472 | my $showAttemptResults = $showAttemptAnswers && shift; |
479 | my $showAttemptResults = $showAttemptAnswers && shift; |
|
|
480 | my $showSummary = shift; |
| 473 | my $showAttemptPreview = shift || 0; |
481 | my $showAttemptPreview = shift || 0; |
| 474 | my $problemResult = $pg->{result}; # the overall result of the problem |
482 | my $problemResult = $pg->{result}; # the overall result of the problem |
| 475 | my @answerNames = @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} }; |
483 | my @answerNames = @{ $pg->{flags}->{ANSWER_ENTRY_ORDER} }; |
| 476 | |
484 | |
| 477 | my $showMessages = $showAttemptAnswers && grep { $pg->{answers}->{$_}->{ans_message} } @answerNames; |
485 | my $showMessages = $showAttemptAnswers && grep { $pg->{answers}->{$_}->{ans_message} } @answerNames; |
| 478 | |
486 | |
| 479 | my $header = CGI::th("part"); |
487 | my $header = CGI::th("Part"); |
| 480 | $header .= $showAttemptAnswers ? CGI::th("entered") : ""; |
488 | $header .= $showAttemptAnswers ? CGI::th("Entered") : ""; |
| 481 | $header .= $showAttemptPreview ? CGI::th("preview") : ""; |
489 | $header .= $showAttemptPreview ? CGI::th("Answer Preview") : ""; |
| 482 | $header .= $showCorrectAnswers ? CGI::th("correct") : ""; |
490 | $header .= $showCorrectAnswers ? CGI::th("Correct") : ""; |
| 483 | $header .= $showAttemptResults ? CGI::th("result") : ""; |
491 | $header .= $showAttemptResults ? CGI::th("Result") : ""; |
| 484 | $header .= $showMessages ? CGI::th("messages") : ""; |
492 | $header .= $showMessages ? CGI::th("messages") : ""; |
| 485 | my @tableRows = ( $header ); |
493 | my @tableRows = ( $header ); |
| 486 | my $numCorrect; |
494 | my $numCorrect; |
| 487 | foreach my $name (@answerNames) { |
495 | foreach my $name (@answerNames) { |
| 488 | my $answerResult = $pg->{answers}->{$name}; |
496 | my $answerResult = $pg->{answers}->{$name}; |
| … | |
… | |
| 508 | $row .= $showAttemptResults ? CGI::td($resultString) : ""; |
516 | $row .= $showAttemptResults ? CGI::td($resultString) : ""; |
| 509 | $row .= $answerMessage ? CGI::td($answerMessage) : ""; |
517 | $row .= $answerMessage ? CGI::td($answerMessage) : ""; |
| 510 | push @tableRows, $row; |
518 | push @tableRows, $row; |
| 511 | } |
519 | } |
| 512 | |
520 | |
| 513 | my $numCorrectNoun = $numCorrect == 1 ? "question" : "questions"; |
521 | my $numIncorrectNoun = scalar @answerNames == 1 ? "question" : "questions"; |
| 514 | my $scorePercent = int ($problemResult->{score} * 100) . "\%"; |
522 | my $scorePercent = int ($problemResult->{score} * 100) . "\%"; |
| 515 | my $summary = "On this attempt, you answered $numCorrect $numCorrectNoun out of " |
523 | my $summary = "On this attempt, you answered $numCorrect out of " |
| 516 | . scalar @answerNames . " correct, for a score of $scorePercent."; |
524 | . scalar @answerNames . " $numIncorrectNoun correct, for a score of $scorePercent."; |
| 517 | return CGI::table({-border=>1}, CGI::Tr(\@tableRows)) . CGI::p($summary); |
525 | return CGI::table({-class=>"attemptResults"}, CGI::Tr(\@tableRows)) . ($showSummary ? CGI::p($summary) : ""); |
| 518 | } |
526 | } |
| 519 | |
527 | |
| 520 | sub viewOptions($) { |
528 | sub viewOptions($) { |
| 521 | my $self = shift; |
529 | my $self = shift; |
| 522 | my $displayMode = $self->{displayMode}; |
530 | my $displayMode = $self->{displayMode}; |
| … | |
… | |
| 616 | # should use surePathToTmpFile, but we have to |
624 | # should use surePathToTmpFile, but we have to |
| 617 | # isolate it from the problem enivronment first |
625 | # isolate it from the problem enivronment first |
| 618 | my $targetURL = $ce->{courseURLs}->{html_temp} . $targetPathCommon; |
626 | my $targetURL = $ce->{courseURLs}->{html_temp} . $targetPathCommon; |
| 619 | |
627 | |
| 620 | # call dvipng to generate a preview |
628 | # call dvipng to generate a preview |
|
|
629 | warn $tex; |
| 621 | dvipng($wd, $latex, $dvipng, $tex, $targetPath); |
630 | dvipng($wd, $latex, $dvipng, $tex, $targetPath); |
| 622 | if (-e $targetPath) { |
631 | if (-e $targetPath) { |
| 623 | return "<img src=\"$targetURL\" alt=\"$tex\" />"; |
632 | return "<img src=\"$targetURL\" alt=\"$tex\" />"; |
| 624 | } else { |
633 | } else { |
| 625 | return "<b>[math2img failed]</b>"; |
634 | return "<b>[math2img failed]</b>"; |