Parent Directory
|
Revision Log
This commit was manufactured by cvs2svn to create branch 'rel-2-3-dev'.
1 ################################################################################ 2 # WeBWorK Online Homework Delivery System 3 # Copyright © 2000-2006 The WeBWorK Project, http://openwebwork.sf.net/ 4 # $CVSHeader: webwork2/lib/WeBWorK/ContentGenerator/ProblemSets.pm,v 1.79 2006/07/14 21:25:11 gage 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::ProblemSets; 18 use base qw(WeBWorK::ContentGenerator); 19 20 =head1 NAME 21 22 WeBWorK::ContentGenerator::ProblemSets - Display a list of built problem sets. 23 24 =cut 25 26 use strict; 27 use warnings; 28 #use CGI qw(-nosticky ); 29 use WeBWorK::CGI; 30 use WeBWorK::Debug; 31 use WeBWorK::Utils qw(readFile sortByName path_is_subdir); 32 33 # what do we consider a "recent" problem set? 34 use constant RECENT => 2*7*24*60*60 ; # Two-Weeks in seconds 35 36 sub info { 37 my ($self) = @_; 38 my $r = $self->r; 39 my $ce = $r->ce; 40 my $db = $r->db; 41 my $urlpath = $r->urlpath; 42 my $authz = $r->authz; 43 44 my $courseID = $urlpath->arg("courseID"); 45 my $user = $r->param("user"); 46 47 my $course_info = $ce->{courseFiles}->{course_info}; 48 49 if (defined $course_info and $course_info) { 50 my $course_info_path = $ce->{courseDirs}->{templates} . "/$course_info"; 51 52 print CGI::start_div({class=>"info-box", id=>"InfoPanel"}); 53 54 # deal with instructor crap 55 my $editorURL; 56 if ($authz->hasPermissions($user, "access_instructor_tools")) { 57 if (defined $r->param("editMode") and $r->param("editMode") eq "temporaryFile") { 58 $course_info_path = $r->param("sourceFilePath"); 59 $course_info_path = $ce->{courseDirs}{templates}.'/'.$course_info_path unless $course_info_path =~ m!^/!; 60 die "sourceFilePath is unsafe!" unless path_is_subdir($course_info_path, $ce->{courseDirs}->{templates}); 61 $self->addmessage(CGI::div({class=>'temporaryFile'}, "Viewing temporary file: ", $course_info_path)); 62 } 63 64 my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", courseID => $courseID); 65 $editorURL = $self->systemLink($editorPage, params => { file_type => "course_info" }); 66 } 67 68 if ($editorURL) { 69 print CGI::h2("Course Info", CGI::a({href=>$editorURL, target=>"WW_Editor"}, "[edit]")); 70 } else { 71 print CGI::h2("Course Info"); 72 } 73 74 if (-f $course_info_path) { #check that it's a plain file 75 my $text = eval { readFile($course_info_path) }; 76 if ($@) { 77 print CGI::div({class=>"ResultsWithError"}, 78 CGI::p("$@"), 79 ); 80 } else { 81 print $text; 82 } 83 } 84 85 print CGI::end_div(); 86 87 return ""; 88 } 89 } 90 sub help { # non-standard help, since the file path includes the course name 91 my $self = shift; 92 my $args = shift; 93 my $name = $args->{name}; 94 $name = lc('course home') unless defined($name); 95 $name =~ s/\s/_/g; 96 $self->helpMacro($name); 97 } 98 sub initialize { 99 100 101 102 # get result and send to message 103 my ($self) = @_; 104 my $r = $self->r; 105 my $authz = $r->authz; 106 my $urlpath = $r->urlpath; 107 108 my $user = $r->param("user"); 109 my $effectiveUser = $r->param("effectiveUser"); 110 if ($authz->hasPermissions($user, "access_instructor_tools")) { 111 # get result and send to message 112 my $status_message = $r->param("status_message"); 113 $self->addmessage(CGI::p("$status_message")) if $status_message; 114 115 116 } 117 } 118 sub body { 119 my ($self) = @_; 120 my $r = $self->r; 121 my $ce = $r->ce; 122 my $db = $r->db; 123 my $authz = $r->authz; 124 my $urlpath = $r->urlpath; 125 126 my $user = $r->param("user"); 127 my $effectiveUser = $r->param("effectiveUser"); 128 my $sort = $r->param("sort") || "status"; 129 130 my $courseName = $urlpath->arg("courseID"); 131 132 my $hardcopyPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Hardcopy", courseID => $courseName); 133 my $actionURL = $self->systemLink($hardcopyPage, authen => 0); # no authen info for form action 134 135 # we have to get sets and versioned sets separately 136 my @setIDs = $db->listUserSets($effectiveUser); 137 my @vSetIDs = $db->listUserSetVersions($effectiveUser); 138 139 my @userSetIDs = map {[$effectiveUser, $_]} @setIDs; 140 my @vUserSetIDs = map {[$effectiveUser, /(.*),v\d+$/, $_]} @vSetIDs; 141 debug("Begin collecting merged sets"); 142 my @sets = $db->getMergedSets( @userSetIDs ); 143 my @vSets = (@vSetIDs) ? $db->getMergedVersionedSets(@vUserSetIDs) : (); 144 145 debug("Begin fixing merged sets"); 146 147 # Database fix (in case of undefined published values) 148 # this may take some extra time the first time but should NEVER need to be run twice 149 # this is only necessary because some people keep holding to ww1.9 which did not have a published field 150 foreach my $set (@sets) { 151 # make sure published is set to 0 or 1 152 if ( $set and $set->published ne "0" and $set->published ne "1") { 153 my $globalSet = $db->getGlobalSet($set->set_id); 154 $globalSet->published("1"); # defaults to published 155 $db->putGlobalSet($globalSet); 156 $set = $db->getMergedSet($effectiveUser, $set->set_id); 157 } else { 158 die "set $set not defined" unless $set; 159 } 160 } 161 162 # gateways/versioned sets require dealing with output data slightly 163 # differently, so check for those here 164 debug("Begin set-type check"); 165 my $existVersions = 0; 166 my @gwSets = (); 167 my @nonGWsets = (); 168 foreach ( @sets ) { 169 if ( defined( $_->assignment_type() ) && 170 $_->assignment_type() =~ /gateway/ ) { 171 $existVersions = 1; 172 push( @gwSets, $_ ) 173 if ( $_->assignment_type() !~ /proctored/ || 174 $authz->hasPermissions($user,"view_proctored_tests") ); 175 } else { 176 push( @nonGWsets, $_ ); 177 } 178 } 179 180 # set sort method 181 $sort = "status" unless $sort eq "status" or $sort eq "name"; 182 183 # now set the headers for the table 184 my $nameHeader = $sort eq "name" 185 ? CGI::u("Name") 186 : CGI::a({href=>$self->systemLink($urlpath, params=>{sort=>"name"})}, "Name"); 187 my $statusHeader = $sort eq "status" 188 ? CGI::u("Status") 189 : CGI::a({href=>$self->systemLink($urlpath, params=>{sort=>"status"})}, "Status"); 190 # print the start of the form 191 192 print CGI::start_form(-method=>"POST",-action=>$actionURL), 193 $self->hidden_authen_fields; 194 195 # and send the start of the table 196 print CGI::start_table(); 197 if ( ! $existVersions ) { 198 print CGI::Tr({}, 199 CGI::th("Sel."), 200 CGI::th($nameHeader), 201 CGI::th($statusHeader), 202 ); 203 } else { 204 print CGI::Tr( 205 CGI::th("Sel."), 206 CGI::th($nameHeader), 207 CGI::th("Score"), 208 CGI::th("Date"), 209 CGI::th($statusHeader), 210 ); 211 } 212 213 debug("Begin sorting merged sets"); 214 215 if ( $sort eq 'name' ) { 216 @nonGWsets = sortByName("set_id", @nonGWsets); 217 @gwSets = sortByName("set_id", @gwSets); 218 } elsif ( $sort eq 'status' ) { 219 @nonGWsets = sort byUrgency @nonGWsets; 220 @gwSets = sort byUrgency @gwSets; 221 } 222 # we sort set versions by name; this at least in part relies on versions 223 # being finished by the time they show up on the list here. 224 @vSets = sortByName("set_id", @vSets); 225 226 # put together a complete list of sorted sets to consider 227 @sets = (@nonGWsets, @gwSets, @vSets); 228 229 debug("End preparing merged sets"); 230 231 foreach my $set (@sets) { 232 die "set $set not defined" unless $set; 233 234 if ($set->published || $authz->hasPermissions($user, "view_unpublished_sets")) { 235 print $self->setListRow($set, $authz->hasPermissions($user, "view_multiple_sets"), $authz->hasPermissions($user, "view_unopened_sets"),$existVersions,$db); 236 } 237 } 238 239 print CGI::end_table(); 240 my $pl = ($authz->hasPermissions($user, "view_multiple_sets") ? "s" : ""); 241 print CGI::p(CGI::submit(-name=>"hardcopy", -label=>"Download Hardcopy for Selected Set$pl")); 242 print CGI::endform(); 243 244 ## feedback form url 245 #my $feedbackPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Feedback", courseID => $courseName); 246 #my $feedbackURL = $self->systemLink($feedbackPage, authen => 0); # no authen info for form action 247 # 248 ##print feedback form 249 #print 250 # CGI::start_form(-method=>"POST", -action=>$feedbackURL),"\n", 251 # $self->hidden_authen_fields,"\n", 252 # CGI::hidden("module", __PACKAGE__),"\n", 253 # CGI::hidden("set", ''),"\n", 254 # CGI::hidden("problem", ''),"\n", 255 # CGI::hidden("displayMode", ''),"\n", 256 # CGI::hidden("showOldAnswers", ''),"\n", 257 # CGI::hidden("showCorrectAnswers", ''),"\n", 258 # CGI::hidden("showHints", ''),"\n", 259 # CGI::hidden("showSolutions", ''),"\n", 260 # CGI::p({-align=>"left"}, 261 # CGI::submit(-name=>"feedbackForm", -label=>"Email instructor") 262 # ), 263 # CGI::endform(),"\n"; 264 265 print $self->feedbackMacro( 266 module => __PACKAGE__, 267 set => "", 268 problem => "", 269 displayMode => "", 270 showOldAnswers => "", 271 showCorrectAnswers => "", 272 showHints => "", 273 showSolutions => "", 274 ); 275 276 return ""; 277 } 278 279 sub setListRow { 280 my ($self, $set, $multiSet, $preOpenSets, $existVersions, $db) = @_; 281 my $r = $self->r; 282 my $ce = $r->ce; 283 my $urlpath = $r->urlpath; 284 285 my $name = $set->set_id; 286 my $courseName = $urlpath->arg("courseID"); 287 288 my $problemSetPage; 289 290 if ( ! defined( $set->assignment_type() ) || 291 $set->assignment_type() !~ /gateway/ ) { 292 $problemSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", 293 courseID => $courseName, setID => $name); 294 } elsif( $set->assignment_type() !~ /proctored/ ) { 295 296 $problemSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::GatewayQuiz", 297 courseID => $courseName, setID => $name); 298 } else { 299 300 $problemSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::GatewayQuiz", 301 courseID => $courseName, setID => $name); 302 } 303 304 my $interactiveURL = $self->systemLink($problemSetPage, 305 params=>{ displayMode => $self->{displayMode}, 306 showOldAnswers => $self->{will}->{showOldAnswers} 307 } 308 ); 309 # check for gateway and template gateway assignments 310 my $gwtype = 0; 311 if ( defined( $set->assignment_type() ) && 312 $set->assignment_type() =~ /gateway/ ) { 313 if ( $name =~ /,v\d+$/ ) { 314 $gwtype = 1; 315 } else { 316 $gwtype = 2; 317 } 318 } 319 320 # the conditional here should be redundant. ah well. 321 $interactiveURL =~ s|/quiz_mode/|/proctored_quiz_mode/| if 322 ( defined( $set->assignment_type() ) && 323 $set->assignment_type() eq 'proctored_gateway' ); 324 325 my $control = ""; 326 if ($multiSet) { 327 if ( $gwtype < 2 ) { 328 $control = CGI::checkbox( 329 -name=>"selected_sets", 330 -value=>$name, 331 -label=>"", 332 ); 333 } else { 334 $control = ' '; 335 } 336 } else { 337 if ( $gwtype < 2 ) { 338 $control = CGI::radio_group( 339 -name=>"selected_sets", 340 -values=>[$name], 341 -default=>"-", 342 -labels=>{$name => ""}, 343 ); 344 } else { 345 $control = ' '; 346 } 347 } 348 349 $name =~ s/_/ /g; 350 my $interactive = CGI::a({-href=>$interactiveURL}, "$name"); 351 # edit this a bit for gateways 352 if ( $gwtype ) { 353 if ( $gwtype == 1 ) { 354 my $sname = $name; 355 $sname =~ s/,v(\d+)$//; 356 $interactive = CGI::a({-href=>$interactiveURL}, 357 "$sname (test$1)"); 358 } else { # this is the case of a template URL 359 $interactive = CGI::a({-href=>$interactiveURL}, 360 "Take new $name test"); 361 } 362 } 363 364 # for gateways, we aren't as verbose about open/closed status, because 365 # there's only one attempt and we default to showing answers once the 366 # test is done. 367 my $status; 368 if ( $gwtype ) { 369 if ( $gwtype == 1 ) { 370 $status = ' '; # for g/w, we only give one attempt per version, 371 # so by the time we're here it's closed 372 } else { 373 my $t = time(); 374 if ( $t < $set->open_date() ) { 375 $status = "will open on " . $self->formatDateTime($set->open_date); 376 $control = "" unless $preOpenSets; 377 $interactive = $name unless $preOpenSets; 378 } elsif ( $t < $set->due_date() ) { 379 $status = "open, due " . $self->formatDateTime($set->due_date); 380 } else { 381 $status = "closed"; 382 } 383 } 384 # old conditional 385 } elsif (time < $set->open_date) { 386 $status = "will open on " . $self->formatDateTime($set->open_date); 387 $control = "" unless $preOpenSets; 388 $interactive = $name unless $preOpenSets; 389 } elsif (time < $set->due_date) { 390 if ( $set->set_id() !~ /,v\d+$/ ) { 391 $status = "now open, due " . $self->formatDateTime($set->due_date); 392 } else { 393 $status = "now open (if version attempts remain), due " . $self->formatDateTime($set->due_date); 394 } 395 } elsif (time < $set->answer_date) { 396 $status = "closed, answers on " . $self->formatDateTime($set->answer_date); 397 } elsif ($set->answer_date <= time and time < $set->answer_date +RECENT ) { 398 $status = "closed, answers recently available"; 399 } else { 400 $status = "closed, answers available"; 401 } 402 403 my $publishedClass = ($set->published) ? "Published" : "Unpublished"; 404 405 $status = CGI::font({class=>$publishedClass}, $status) if $preOpenSets; 406 407 # check to see if we need to return a score and a date column 408 if ( ! $existVersions ) { 409 return CGI::Tr(CGI::td([ 410 $control, 411 $interactive, 412 $status, 413 ])); 414 } else { 415 my ( $startTime, $score ); 416 417 if ( defined( $set->assignment_type() ) && 418 $set->assignment_type() =~ /gateway/ && 419 $set->set_id() =~ /,v\d+$/ ) { 420 $startTime = localtime( $set->version_creation_time() ); 421 422 # find score 423 my @problemRecords = $db->getAllUserProblems( $set->user_id(), 424 $set->set_id() ); 425 my $possible = 0; 426 $score = 0; 427 foreach my $pRec ( @problemRecords ) { 428 if ( defined( $pRec ) && $score ne 'undef' ) { 429 $score += $pRec->status() || 0; 430 } else { 431 $score = 'undef'; 432 } 433 $possible++; 434 } 435 $score = "$score/$possible"; 436 } else { 437 $startTime = ' '; 438 $score = $startTime; 439 } 440 return CGI::Tr(CGI::td([ 441 $control, 442 $interactive, 443 $score, 444 $startTime, 445 $status, 446 ])); 447 } 448 } 449 450 sub byname { $a->set_id cmp $b->set_id; } 451 452 sub byUrgency { 453 my $mytime = time; 454 my @a_parts = ($a->answer_date + RECENT <= $mytime) ? (4, $a->open_date, $a->due_date, $a->set_id) 455 : ($a->answer_date <= $mytime and $mytime < $a->answer_date + RECENT) ? (3, $a-> answer_date, $a-> due_date, $a->set_id) 456 : ($a->due_date <= $mytime and $mytime < $a->answer_date ) ? (2, $a->answer_date, $a->due_date, $a->set_id) 457 : ($mytime < $a->open_date) ? (1, $a->open_date, $a->due_date, $a->set_id) 458 : (0, $a->due_date, $a->open_date, $a->set_id); 459 my @b_parts = ($b->answer_date + RECENT <= $mytime) ? (4, $b->open_date, $b->due_date, $b->set_id) 460 : ($b->answer_date <= $mytime and $mytime < $b->answer_date + RECENT) ? (3, $b-> answer_date, $b-> due_date, $b->set_id) 461 : ($b->due_date <= $mytime and $mytime < $b->answer_date ) ? (2, $b->answer_date, $b->due_date, $b->set_id) 462 : ($mytime < $b->open_date) ? (1, $b->open_date, $b->due_date, $b->set_id) 463 : (0, $b->due_date, $b->open_date, $b->set_id); 464 my $returnIt=0; 465 while (scalar(@a_parts) > 1) { 466 if ($returnIt = ( (shift @a_parts) <=> (shift @b_parts) ) ) { 467 return($returnIt); 468 } 469 } 470 return ( $a_parts[0] cmp $b_parts[0] ); 471 } 472 473 1;
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |