Parent Directory
|
Revision Log
updates merged from Trunk
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2007 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/ProblemSet.pm,v 1.94 2009/11/02 16:54:15 apizer Exp $ 5 # 6 # This program is free software; you can redistribute it and/or modify it under 7 # the terms of either: (a) the GNU General Public License as published by the 8 # Free Software Foundation; either version 2, or (at your option) any later 9 # version, or (b) the "Artistic License" which comes with this package. 10 # 11 # This program is distributed in the hope that it will be useful, but WITHOUT 12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 13 # FOR A PARTICULAR PURPOSE. See either the GNU General Public License or the 14 # Artistic License for more details. 15 ################################################################################ 16 17 package WeBWorK::ContentGenerator::ProblemSet; 18 use base qw(WeBWorK::ContentGenerator); 19 20 =head1 NAME 21 22 WeBWorK::ContentGenerator::ProblemSet - display an index of the problems in a 23 problem set. 24 25 =cut 26 27 use strict; 28 use warnings; 29 #use CGI qw(-nosticky *ul *li); 30 use WeBWorK::CGI; 31 use WeBWorK::PG; 32 use URI::Escape; 33 use WeBWorK::Debug; 34 use WeBWorK::Utils qw(sortByName path_is_subdir); 35 36 sub initialize { 37 my ($self) = @_; 38 my $r = $self->r; 39 my $db = $r->db; 40 my $urlpath = $r->urlpath; 41 my $authz = $r->authz; 42 43 my $setName = $urlpath->arg("setID"); 44 my $userName = $r->param("user"); 45 my $effectiveUserName = $r->param("effectiveUser"); 46 $self->{displayMode} = $r->param('displayMode') || $r->ce->{pg}->{options}->{displayMode}; 47 48 49 my $user = $db->getUser($userName); # checked 50 my $effectiveUser = $db->getUser($effectiveUserName); # checked 51 my $set = $db->getMergedSet($effectiveUserName, $setName); # checked 52 53 die "user $user (real user) not found." unless $user; 54 die "effective user $effectiveUserName not found. One 'acts as' the effective user." unless $effectiveUser; 55 56 # FIXME: some day it would be nice to take out this code and consolidate the two checks 57 58 # get result and send to message 59 my $status_message = $r->param("status_message"); 60 $self->addmessage(CGI::p("$status_message")) if $status_message; 61 62 # $self->{invalidSet} is set by ContentGenerator.pm 63 return if $self->{invalidSet}; 64 return unless defined($set); 65 66 # Database fix (in case of undefined published values) 67 # this is only necessary because some people keep holding to ww1.9 which did not have a published field 68 # make sure published is set to 0 or 1 69 70 if ($set->published ne "0" and $set->published ne "1") { 71 my $globalSet = $db->getGlobalSet($set->set_id); 72 $globalSet->published("1"); # defaults to published 73 $db->putGlobalSet($globalSet); 74 $set = $db->getMergedSet($effectiveUserName, $set->set_id); 75 } 76 77 # When a set is created enable_reduced_scoring is null, so we have to set it 78 if ($set->enable_reduced_scoring ne "0" and $set->enable_reduced_scoring ne "1") { 79 my $globalSet = $db->getGlobalSet($set->set_id); 80 $globalSet->enable_reduced_scoring("0"); # defaults to disabled 81 $db->putGlobalSet($globalSet); 82 $set = $db->getMergedSet($effectiveUserName, $set->set_id); 83 } 84 85 my $publishedText = ($set->published) ? "visible to students." : "hidden from students."; 86 my $publishedClass = ($set->published) ? "Published" : "Unpublished"; 87 $self->addmessage(CGI::span("This set is " . CGI::font({class=>$publishedClass}, $publishedText))) if $authz->hasPermissions($userName, "view_unpublished_sets"); 88 89 90 $self->{userName} = $userName; 91 $self->{user} = $user; 92 $self->{effectiveUser} = $effectiveUser; 93 $self->{set} = $set; 94 95 ##### permissions ##### 96 97 $self->{isOpen} = time >= $set->open_date || $authz->hasPermissions($userName, "view_unopened_sets"); 98 } 99 100 sub nav { 101 my ($self, $args) = @_; 102 my $r = $self->r; 103 my $urlpath = $r->urlpath; 104 105 my $courseID = $urlpath->arg("courseID"); 106 #my $problemSetsPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets", courseID => $courseID); 107 my $problemSetsPage = $urlpath->parent; 108 109 my @links = ("Homework Sets" , $r->location . $problemSetsPage->path, "navUp"); 110 # CRAP ALERT: this line relies on the hacky options() implementation in ContentGenerator. 111 # we need to find a better way to do this -- long range dependencies like this are dangerous! 112 #my $tail = "&displayMode=".$self->{displayMode}."&showOldAnswers=".$self->{will}->{showOldAnswers}; 113 # here is a hack to get some functionality back, but I don't even think it's that important to 114 # have this, since there are SO MANY PLACES where we lose the displayMode, etc. 115 # (oh boy, do we need a session table in the database!) 116 my $displayMode = $r->param("displayMode") || ""; 117 my $showOldAnswers = $r->param("showOldAnswers") || ""; 118 my $tail = "&displayMode=$displayMode&showOldAnswers=$showOldAnswers"; 119 return $self->navMacro($args, $tail, @links); 120 } 121 122 sub siblings { 123 my ($self) = @_; 124 my $r = $self->r; 125 my $db = $r->db; 126 my $authz = $r->authz; 127 my $urlpath = $r->urlpath; 128 129 130 my $courseID = $urlpath->arg("courseID"); 131 my $user = $r->param('user'); 132 my $eUserID = $r->param("effectiveUser"); 133 134 # note that listUserSets does not list versioned sets 135 # DBFIXME do filtering in WHERE clause, use iterator for results :) 136 my @setIDs = sortByName(undef, $db->listUserSets($eUserID)); 137 138 # do not show unpublished siblings unless user is allowed to view unpublished sets, and 139 # exclude gateway tests in all cases 140 if ( $authz->hasPermissions($user, "view_unpublished_sets") ) { 141 @setIDs = grep {my $gs = $db->getGlobalSet( $_ ); 142 $gs->assignment_type() !~ /gateway/} @setIDs; 143 144 } else { 145 @setIDs = grep {my $gs = $db->getGlobalSet( $_ ); 146 $gs->assignment_type() !~ /gateway/ && 147 ( defined($gs->published()) ? $gs->published() : 1 ) 148 } @setIDs; 149 } 150 151 print CGI::start_div({class=>"info-box", id=>"fisheye"}); 152 print CGI::h2("Sets"); 153 print CGI::start_ul(); 154 155 debug("Begin printing sets from listUserSets()"); 156 foreach my $setID (@setIDs) { 157 my $setPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", 158 courseID => $courseID, setID => $setID); 159 my $pretty_set_id = $setID; 160 $pretty_set_id =~ s/_/ /g; 161 print CGI::li( 162 CGI::a({ href=>$self->systemLink($setPage, 163 params=>{ 164 displayMode => $self->{displayMode}, 165 showOldAnswers => $self->{will}->{showOldAnswers}, 166 }, 167 ), 168 id=>$pretty_set_id, 169 }, $pretty_set_id) 170 ) ; 171 } 172 debug("End printing sets from listUserSets()"); 173 174 # FIXME: when database calls are faster, this will get rid of unpublished sibling links 175 #debug("Begin printing sets from getMergedSets()"); 176 #my @userSetIDs = map {[$eUserID, $_]} @setIDs; 177 #my @sets = $db->getMergedSets(@userSetIDs); 178 #foreach my $set (@sets) { 179 # my $setPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", courseID => $courseID, setID => $set->set_id); 180 # print CGI::li(CGI::a({href=>$self->systemLink($setPage)}, $set->set_id)) unless !(defined $set && ($set->published || $authz->hasPermissions($user, "view_unpublished_sets")); 181 #} 182 #debug("Begin printing sets from getMergedSets()"); 183 184 print CGI::end_ul(); 185 #print CGI::end_li(); 186 #print CGI::end_ul(); 187 print CGI::end_div(); 188 189 return ""; 190 } 191 192 sub info { 193 my ($self) = @_; 194 my $r = $self->r; 195 my $ce = $r->ce; 196 my $db = $r->db; 197 my $authz = $r->authz; 198 my $urlpath = $r->urlpath; 199 200 return "" if ( $self->{invalidSet} ); 201 202 my $courseID = $urlpath->arg("courseID"); 203 my $setID = $r->urlpath->arg("setID"); 204 205 my $userID = $r->param("user"); 206 my $eUserID = $r->param("effectiveUser"); 207 208 my $effectiveUser = $db->getUser($eUserID); # checked 209 my $set = $db->getMergedSet($eUserID, $setID); # checked 210 211 die "effective user $eUserID not found. One 'acts as' the effective user." unless $effectiveUser; 212 # FIXME: this was already caught in initialize() 213 die "set $setID for effectiveUser $eUserID not found." unless $set; 214 215 my $psvn = $set->psvn(); 216 my $screenSetHeader = ($set->set_header eq "defaultHeader") ? 217 $ce->{webworkFiles}->{screenSnippets}->{setHeader} : 218 $set->set_header; 219 220 my $displayMode = $r->param("displayMode") || $ce->{pg}->{options}->{displayMode}; 221 222 if ($authz->hasPermissions($userID, "modify_problem_sets")) { 223 if (defined $r->param("editMode") and $r->param("editMode") eq "temporaryFile") { 224 $screenSetHeader = $r->param('sourceFilePath'); 225 $screenSetHeader = $ce->{courseDirs}{templates}.'/'.$screenSetHeader unless $screenSetHeader =~ m!^/!; 226 die "sourceFilePath is unsafe!" unless path_is_subdir($screenSetHeader, $ce->{courseDirs}->{templates}); 227 $self->addmessage(CGI::div({class=>'temporaryFile'}, "Viewing temporary file: ", 228 $screenSetHeader)); 229 $displayMode = $r->param("displayMode") if $r->param("displayMode"); 230 } 231 } 232 233 return "" unless defined $screenSetHeader and $screenSetHeader; 234 235 # decide what to do about problem number 236 my $problem = WeBWorK::DB::Record::UserProblem->new( 237 problem_id => 0, 238 set_id => $set->set_id, 239 login_id => $effectiveUser->user_id, 240 source_file => $screenSetHeader, 241 # the rest of Problem's fields are not needed, i think 242 ); 243 244 my $pg = WeBWorK::PG->new( 245 $ce, 246 $effectiveUser, 247 $r->param('key'), 248 $set, 249 $problem, 250 $psvn, 251 {}, # no form fields! 252 { # translation options 253 displayMode => $displayMode, 254 showHints => 0, 255 showSolutions => 0, 256 processAnswers => 0, 257 }, 258 ); 259 260 my $editorURL; 261 if (defined($set) and $authz->hasPermissions($userID, "modify_problem_sets")) { 262 my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", 263 courseID => $courseID, setID => $set->set_id, problemID => 0); 264 $editorURL = $self->systemLink($editorPage, params => { file_type => 'set_header'}); 265 } 266 267 print CGI::start_div({class=>"info-box", id=>"InfoPanel"}); 268 269 if ($editorURL) { 270 print CGI::h2({},"Set Info", CGI::a({href=>$editorURL, target=>"WW_Editor"}, "[edit]")); 271 } else { 272 print CGI::h2("Set Info"); 273 } 274 275 if ($pg->{flags}->{error_flag}) { 276 print CGI::div({class=>"ResultsWithError"}, $self->errorOutput($pg->{errors}, $pg->{body_text})); 277 } else { 278 print $pg->{body_text}; 279 } 280 281 print CGI::end_div(); 282 283 return ""; 284 } 285 286 sub options { shift->optionsMacro } 287 288 sub body { 289 my ($self) = @_; 290 my $r = $self->r; 291 my $ce = $r->ce; 292 my $db = $r->db; 293 my $urlpath = $r->urlpath; 294 295 my $courseID = $urlpath->arg("courseID"); 296 my $setName = $urlpath->arg("setID"); 297 my $effectiveUser = $r->param('effectiveUser'); 298 299 my $set = $db->getMergedSet($effectiveUser, $setName); # checked 300 # FIXME: this was already caught in initialize() 301 # die "set $setName for user $effectiveUser not found" unless $set; 302 303 if ( $self->{invalidSet} ) { 304 return CGI::div({class=>"ResultsWithError"}, 305 CGI::p("The selected problem set ($setName) " . 306 "is not a valid set for $effectiveUser:"), 307 CGI::p($self->{invalidSet})); 308 } 309 310 #my $hardcopyURL = 311 # $ce->{webworkURLs}->{root} . "/" 312 # . $ce->{courseName} . "/" 313 # . "hardcopy/$setName/?" . $self->url_authen_args; 314 315 my $hardcopyPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Hardcopy", 316 courseID => $courseID, setID => $setName); 317 my $hardcopyURL = $self->systemLink($hardcopyPage); 318 319 print CGI::p(CGI::a({href=>$hardcopyURL}, "Download a hardcopy of this homework set.")); 320 321 322 my $enable_reduced_scoring = $set->enable_reduced_scoring; 323 my $reducedScoringPeriod = $ce->{pg}->{ansEvalDefaults}->{reducedScoringPeriod}; 324 if ($reducedScoringPeriod > 0 and $enable_reduced_scoring) { 325 my $dueDate = $self->formatDateTime($set->due_date()); 326 my $reducedScoringPeriodSec = $reducedScoringPeriod*60; # $reducedScoringPeriod is in minutes 327 my $reducedScoringValue = $ce->{pg}->{ansEvalDefaults}->{reducedScoringValue}; 328 my $reducedScoringPerCent = int(100*$reducedScoringValue+.5); 329 my $beginReducedScoringPeriod = $self->formatDateTime($set->due_date() - $reducedScoringPeriodSec); 330 if (time < $set->due_date()) { 331 print CGI::div({class=>"ResultsAlert"},"This assignment has a Reduced Credit Period that begins 332 $beginReducedScoringPeriod and ends on the due date, $dueDate. During this period all additional 333 work done counts $reducedScoringPerCent\% of the original."); 334 } else { 335 print CGI::div({class=>"ResultsAlert"},"This assignment had a Reduced Credit Period that began 336 $beginReducedScoringPeriod and ended on the due date, $dueDate. During that period all additional 337 work done counted $reducedScoringPerCent\% of the original."); 338 } 339 } 340 341 # DBFIXME use iterator 342 my @problemNumbers = $db->listUserProblems($effectiveUser, $setName); 343 344 if (@problemNumbers) { 345 print CGI::start_table(); 346 print CGI::Tr({}, 347 CGI::th("Name"), 348 CGI::th("Attempts"), 349 CGI::th("Remaining"), 350 CGI::th("Worth"), 351 CGI::th("Status"), 352 ); 353 354 foreach my $problemNumber (sort { $a <=> $b } @problemNumbers) { 355 my $problem = $db->getMergedProblem($effectiveUser, $setName, $problemNumber); # checked 356 die "problem $problemNumber in set $setName for user $effectiveUser not found." unless $problem; 357 print $self->problemListRow($set, $problem); 358 } 359 360 print CGI::end_table(); 361 } else { 362 print CGI::p("This homework set contains no problems."); 363 } 364 365 ## feedback form url 366 #my $feedbackPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Feedback", 367 # courseID => $courseID); 368 #my $feedbackURL = $self->systemLink($feedbackPage, authen => 0); # no authen info for form action 369 # 370 ##print feedback form 371 #print 372 # CGI::start_form(-method=>"POST", -action=>$feedbackURL),"\n", 373 # $self->hidden_authen_fields,"\n", 374 # CGI::hidden("module", __PACKAGE__),"\n", 375 # CGI::hidden("set", $self->{set}->set_id),"\n", 376 # CGI::hidden("problem", ''),"\n", 377 # CGI::hidden("displayMode", $self->{displayMode}),"\n", 378 # CGI::hidden("showOldAnswers", ''),"\n", 379 # CGI::hidden("showCorrectAnswers", ''),"\n", 380 # CGI::hidden("showHints", ''),"\n", 381 # CGI::hidden("showSolutions", ''),"\n", 382 # CGI::p({-align=>"left"}, 383 # CGI::submit(-name=>"feedbackForm", -label=>"Email instructor") 384 # ), 385 # CGI::endform(),"\n"; 386 387 print $self->feedbackMacro( 388 module => __PACKAGE__, 389 set => $self->{set}->set_id, 390 problem => "", 391 displayMode => $self->{displayMode}, 392 showOldAnswers => "", 393 showCorrectAnswers => "", 394 showHints => "", 395 showSolutions => "", 396 ); 397 398 return ""; 399 } 400 401 sub problemListRow($$$) { 402 my ($self, $set, $problem) = @_; 403 my $r = $self->r; 404 my $urlpath = $r->urlpath; 405 406 my $courseID = $urlpath->arg("courseID"); 407 my $setID = $set->set_id; 408 my $problemID = $problem->problem_id; 409 410 my $interactiveURL = $self->systemLink( 411 $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", 412 courseID => $courseID, setID => $setID, problemID => $problemID 413 ), 414 params=>{ displayMode => $self->{displayMode}, 415 showOldAnswers => $self->{will}->{showOldAnswers} 416 } 417 ); 418 419 my $interactive = CGI::a({-href=>$interactiveURL}, "Problem $problemID"); 420 my $attempts = $problem->num_correct + $problem->num_incorrect; 421 my $remaining = (($problem->max_attempts||-1) < 0) #a blank yields 'infinite' because it evaluates as false with out giving warnings about comparing non-numbers 422 ? "unlimited" 423 : $problem->max_attempts - $attempts; 424 my $rawStatus = $problem->status || 0; 425 my $status; 426 $status = eval{ sprintf("%.0f%%", $rawStatus * 100)}; # round to whole number 427 $status = 'unknown(FIXME)' if $@; # use a blank if problem status was not defined or not numeric. 428 # FIXME -- this may not cover all cases. 429 430 # my $msg = ($problem->value) ? "" : "(This problem will not count towards your grade.)"; 431 432 return CGI::Tr({}, 433 CGI::td({-nowrap=>1, -align=>"left"},$interactive), 434 CGI::td({-nowrap=>1, -align=>"center"}, 435 [ 436 $attempts, 437 $remaining, 438 $problem->value, 439 $status, 440 ])); 441 } 442 443 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |