Parent Directory
|
Revision Log
Revision 982 - (view) (download) (as text)
| 1 : | gage | 860 | package WeBWorK::ContentGenerator::Instructor::ProblemSetEditor; |
| 2 : | use base qw(WeBWorK::ContentGenerator::Instructor); | ||
| 3 : | |||
| 4 : | =head1 NAME | ||
| 5 : | |||
| 6 : | WeBWorK::ContentGenerator::Instructor::ProblemSetEditor - Edit a set definition list | ||
| 7 : | |||
| 8 : | =cut | ||
| 9 : | |||
| 10 : | use strict; | ||
| 11 : | use warnings; | ||
| 12 : | use CGI qw(); | ||
| 13 : | malsyned | 976 | use WeBWorK::DB::Record::Problem; |
| 14 : | use WeBWorK::Utils qw(readFile formatDateTime parseDateTime list2hash readDirectory max); | ||
| 15 : | gage | 860 | |
| 16 : | gage | 869 | our $rowheight = 20; #controls the length of the popup menus. |
| 17 : | gage | 875 | our $libraryName; #library directory name |
| 18 : | malsyned | 935 | |
| 19 : | malsyned | 947 | use constant SET_FIELDS => [qw(open_date due_date answer_date set_header problem_header)]; |
| 20 : | malsyned | 957 | use constant PROBLEM_FIELDS =>[qw(source_file value max_attempts)]; |
| 21 : | malsyned | 959 | use constant PROBLEM_USER_FIELDS => [qw(problem_seed status num_correct num_incorrect)]; |
| 22 : | malsyned | 947 | |
| 23 : | malsyned | 935 | sub getSetName { |
| 24 : | my ($self, $pathSetName) = @_; | ||
| 25 : | if (ref $pathSetName eq "HASH") { | ||
| 26 : | $pathSetName = undef; | ||
| 27 : | } | ||
| 28 : | return $pathSetName; | ||
| 29 : | } | ||
| 30 : | |||
| 31 : | malsyned | 967 | # One wrinkle here: if $override is undefined, do the global thing, otherwise, it's truth value determines the checkbox. |
| 32 : | sub setRowHTML { | ||
| 33 : | my ($description, $fieldName, $fieldValue, $size, $override, $overrideValue) = @_; | ||
| 34 : | |||
| 35 : | my $attributeHash = {type=>"text", name=>$fieldName, value=>$fieldValue}; | ||
| 36 : | $attributeHash->{size} = $size if defined $size; | ||
| 37 : | |||
| 38 : | my $html = CGI::td({}, [$description, CGI::input($attributeHash)]); | ||
| 39 : | |||
| 40 : | if (defined $override) { | ||
| 41 : | $attributeHash->{name}="${fieldName}_override"; | ||
| 42 : | $attributeHash->{value}=($override ? $overrideValue : "" ); | ||
| 43 : | |||
| 44 : | $html .= CGI::td({}, [ | ||
| 45 : | CGI::checkbox({ | ||
| 46 : | type=>"checkbox", | ||
| 47 : | name=>"override", | ||
| 48 : | label=>"override with:", | ||
| 49 : | value=>$fieldName, | ||
| 50 : | checked=>($override ? 1 : 0) | ||
| 51 : | }), | ||
| 52 : | CGI::input($attributeHash) | ||
| 53 : | ]); | ||
| 54 : | } | ||
| 55 : | |||
| 56 : | return $html; | ||
| 57 : | |||
| 58 : | } | ||
| 59 : | |||
| 60 : | sub hiddenEditForUserFields { | ||
| 61 : | my @editForUser = @_; | ||
| 62 : | my $return = ""; | ||
| 63 : | foreach my $editUser (@editForUser) { | ||
| 64 : | $return .= CGI::input({type=>"hidden", name=>"editForUser", value=>$editUser}); | ||
| 65 : | } | ||
| 66 : | |||
| 67 : | return $return; | ||
| 68 : | } | ||
| 69 : | |||
| 70 : | sub problemElementHTML { | ||
| 71 : | my ($fieldName, $fieldValue, $size, $override, $overrideValue) = @_; | ||
| 72 : | my $attributeHash = {type=>"text",name=>$fieldName,value=>$fieldValue}; | ||
| 73 : | $attributeHash->{size} = $size if defined $size; | ||
| 74 : | |||
| 75 : | my $html = CGI::input($attributeHash); | ||
| 76 : | if (defined $override) { | ||
| 77 : | $attributeHash->{name} = "${fieldName}_override"; | ||
| 78 : | $attributeHash->{value} = ($override ? $overrideValue : ""); | ||
| 79 : | $html = "default:".CGI::br().$html.CGI::br() | ||
| 80 : | . CGI::checkbox({ | ||
| 81 : | type => "checkbox", | ||
| 82 : | name => "override", | ||
| 83 : | label => "override:", | ||
| 84 : | value => $fieldName, | ||
| 85 : | checked => ($override ? 1 : 0) | ||
| 86 : | }) | ||
| 87 : | . CGI::br() | ||
| 88 : | . CGI::input($attributeHash); | ||
| 89 : | } | ||
| 90 : | |||
| 91 : | return $html; | ||
| 92 : | } | ||
| 93 : | |||
| 94 : | malsyned | 976 | |
| 95 : | # pay no attention to the argument list. Here's what you pass: | ||
| 96 : | # directoryListHTML($level, $selected, $libraryRoot, @path) | ||
| 97 : | sub directoryListHTML { | ||
| 98 : | my ($level, $selected, @path) = @_; | ||
| 99 : | $selected = [$selected] unless ref $selected eq "ARRAY"; | ||
| 100 : | my $dirName = join "/", @path[0..$level]; | ||
| 101 : | my @contents = sort grep {m/\.pg$/ or -d $dirName.'/'.$_ and not m/^\.{1,2}$/} readDirectory($dirName); | ||
| 102 : | malsyned | 979 | my %contentsPretty = map {$_ => (-d $dirName.'/'.$_ ? $_.'/' : $_)} @contents; |
| 103 : | malsyned | 976 | |
| 104 : | my $html = ($level eq "0" ? "problem library" : $path[$level]) . CGI::br(); | ||
| 105 : | $html .= CGI::scrolling_list({ | ||
| 106 : | name=>"directory_level_$level", | ||
| 107 : | values=>\@contents, | ||
| 108 : | malsyned | 979 | labels=>\%contentsPretty, |
| 109 : | malsyned | 976 | default=>$selected, |
| 110 : | multiple=>'true', | ||
| 111 : | size=>"20", | ||
| 112 : | }); | ||
| 113 : | $html .= CGI::br() | ||
| 114 : | . CGI::input({type=>"submit", name=>"open_add_$level", value=>"Open/Add"}); | ||
| 115 : | } | ||
| 116 : | |||
| 117 : | gage | 860 | sub title { |
| 118 : | malsyned | 935 | my ($self, @components) = @_; |
| 119 : | return "Problem Set Editor - ".$self->{ce}->{courseName}." : ".$self->getSetName(@components); | ||
| 120 : | gage | 860 | } |
| 121 : | |||
| 122 : | malsyned | 960 | # Initialize does all of the form processing. It's extensive, and could probably be cleaned up and |
| 123 : | # consolidated with a little abstraction. | ||
| 124 : | malsyned | 935 | sub initialize { |
| 125 : | my ($self, @components) = @_; | ||
| 126 : | my $r = $self->{r}; | ||
| 127 : | my $db = $self->{db}; | ||
| 128 : | malsyned | 976 | my $ce = $self->{ce}; |
| 129 : | malsyned | 935 | my $setName = $self->getSetName(@components); |
| 130 : | my $setRecord = $db->getGlobalSet($setName); | ||
| 131 : | malsyned | 957 | my @editForUser = $r->param('editForUser'); |
| 132 : | # some useful booleans | ||
| 133 : | my $forUsers = scalar(@editForUser); | ||
| 134 : | my $forOneUser = $forUsers == 1; | ||
| 135 : | malsyned | 935 | |
| 136 : | malsyned | 967 | my %overrides = list2hash $r->param('override'); |
| 137 : | malsyned | 959 | # build a quick lookup table |
| 138 : | |||
| 139 : | # The set form was submitted | ||
| 140 : | malsyned | 936 | if (defined($r->param('submit_set_changes'))) { |
| 141 : | malsyned | 947 | foreach (@{SET_FIELDS()}) { |
| 142 : | malsyned | 935 | if (defined($r->param($_))) { |
| 143 : | if (m/_date$/) { | ||
| 144 : | $setRecord->$_(parseDateTime($r->param($_))); | ||
| 145 : | } else { | ||
| 146 : | $setRecord->$_($r->param($_)); | ||
| 147 : | } | ||
| 148 : | } | ||
| 149 : | } | ||
| 150 : | malsyned | 959 | $db->putGlobalSet($setRecord); |
| 151 : | malsyned | 957 | |
| 152 : | malsyned | 959 | if ($forOneUser) { |
| 153 : | |||
| 154 : | my $userSetRecord = $db->getUserSet($editForUser[0], $setName); | ||
| 155 : | foreach my $field (@{SET_FIELDS()}) { | ||
| 156 : | if (defined $r->param("${field}_override")) { | ||
| 157 : | if (exists $overrides{$field}) { | ||
| 158 : | if ($field =~ m/_date$/) { | ||
| 159 : | $userSetRecord->$field(parseDateTime($r->param("${field}_override"))); | ||
| 160 : | } else { | ||
| 161 : | $userSetRecord->$field($r->param("${field}_override")); | ||
| 162 : | } | ||
| 163 : | } else { | ||
| 164 : | $userSetRecord->$field(undef); | ||
| 165 : | } | ||
| 166 : | |||
| 167 : | $db->putUserSet($userSetRecord); | ||
| 168 : | } | ||
| 169 : | } | ||
| 170 : | } | ||
| 171 : | malsyned | 936 | } |
| 172 : | malsyned | 959 | # the Problem form was submitted |
| 173 : | malsyned | 936 | elsif (defined($r->param('submit_problem_changes'))) { |
| 174 : | malsyned | 967 | foreach my $problem ($r->param('deleteProblem')) { |
| 175 : | $db->deleteGlobalProblem($setName, $problem); | ||
| 176 : | } | ||
| 177 : | malsyned | 936 | my @problemList = $db->listGlobalProblems($setName); |
| 178 : | foreach my $problem (@problemList) { | ||
| 179 : | my $problemRecord = $db->getGlobalProblem($setName, $problem); | ||
| 180 : | malsyned | 959 | foreach my $field (@{PROBLEM_FIELDS()}) { |
| 181 : | my $paramName = "problem_${problem}_${field}"; | ||
| 182 : | malsyned | 936 | if (defined($r->param($paramName))) { |
| 183 : | malsyned | 959 | $problemRecord->$field($r->param($paramName)); |
| 184 : | malsyned | 936 | } |
| 185 : | } | ||
| 186 : | malsyned | 959 | $db->putGlobalProblem($problemRecord); |
| 187 : | |||
| 188 : | if ($forOneUser) { | ||
| 189 : | my $userProblemRecord = $db->getUserProblem($editForUser[0], $setName, $problem); | ||
| 190 : | foreach my $field (@{PROBLEM_USER_FIELDS()}) { | ||
| 191 : | my $paramName = "problem_${problem}_${field}"; | ||
| 192 : | if (defined($r->param($paramName))) { | ||
| 193 : | $userProblemRecord->$field($r->param($paramName)); | ||
| 194 : | } | ||
| 195 : | } | ||
| 196 : | malsyned | 960 | $userProblemRecord->attempted($userProblemRecord->num_correct + $userProblemRecord->num_incorrect); |
| 197 : | malsyned | 959 | foreach my $field (@{PROBLEM_FIELDS()}) { |
| 198 : | my $paramName = "problem_${problem}_${field}"; | ||
| 199 : | if (defined($r->param("${paramName}_override"))) { | ||
| 200 : | if (exists $overrides{$paramName}) { | ||
| 201 : | $userProblemRecord->$field($r->param("${paramName}_override")); | ||
| 202 : | } else { | ||
| 203 : | $userProblemRecord->$field(undef); | ||
| 204 : | } | ||
| 205 : | |||
| 206 : | $db->putUserProblem($userProblemRecord); | ||
| 207 : | } | ||
| 208 : | } | ||
| 209 : | |||
| 210 : | } | ||
| 211 : | malsyned | 935 | } |
| 212 : | malsyned | 976 | } elsif (defined $r->param('fileBrowsing')) { |
| 213 : | my $libraryRoot = $ce->{courseDirs}->{templates}; | ||
| 214 : | my $count = 0; | ||
| 215 : | my $done = 0; | ||
| 216 : | my @path = (); | ||
| 217 : | my $freeProblemID = max($db->listGlobalProblems($setName)) + 1; | ||
| 218 : | while (defined $r->param("directory_level_$count") and not $done) { | ||
| 219 : | my @selected = $r->param("directory_level_$count"); | ||
| 220 : | my $dirFound = 0; | ||
| 221 : | # If any directories are selected, "cd" into the first one and stop processing this level. | ||
| 222 : | foreach my $selected (@selected) { | ||
| 223 : | if (-d join "/", $libraryRoot, @path, $selected) { | ||
| 224 : | push @path, $selected; | ||
| 225 : | $dirFound = 1; | ||
| 226 : | last; | ||
| 227 : | } | ||
| 228 : | } | ||
| 229 : | # Otherwise, create a new global problem for each of the files selected | ||
| 230 : | unless ($dirFound) { | ||
| 231 : | foreach my $selected (@selected) { | ||
| 232 : | my $file = join "/", @path, $selected; | ||
| 233 : | my $problemRecord = new WeBWorK::DB::Record::Problem; | ||
| 234 : | $problemRecord->problem_id($freeProblemID++); | ||
| 235 : | $problemRecord->set_id($setName); | ||
| 236 : | $problemRecord->source_file($file); | ||
| 237 : | $problemRecord->value("1"); | ||
| 238 : | $problemRecord->max_attempts("-1"); | ||
| 239 : | $db->addGlobalProblem($problemRecord); | ||
| 240 : | } | ||
| 241 : | $done = 1; | ||
| 242 : | } | ||
| 243 : | |||
| 244 : | if (defined $r->param("open_add_$count")) { | ||
| 245 : | $done = 1; | ||
| 246 : | } | ||
| 247 : | $count++; | ||
| 248 : | } | ||
| 249 : | $self->{path} = [@path]; | ||
| 250 : | malsyned | 968 | } |
| 251 : | malsyned | 935 | } |
| 252 : | |||
| 253 : | malsyned | 947 | |
| 254 : | gage | 860 | sub body { |
| 255 : | malsyned | 935 | my ($self, @components) = @_; |
| 256 : | my $r = $self->{r}; | ||
| 257 : | my $db = $self->{db}; | ||
| 258 : | malsyned | 962 | my $ce = $self->{ce}; |
| 259 : | my $courseName = $ce->{courseName}; | ||
| 260 : | malsyned | 935 | my $setName = $self->getSetName(@components); |
| 261 : | my $setRecord = $db->getGlobalSet($setName); | ||
| 262 : | malsyned | 936 | my @editForUser = $r->param('editForUser'); |
| 263 : | malsyned | 947 | # some useful booleans |
| 264 : | malsyned | 957 | my $forUsers = scalar(@editForUser); |
| 265 : | my $forOneUser = $forUsers == 1; | ||
| 266 : | malsyned | 935 | |
| 267 : | malsyned | 959 | ## Set Form ## |
| 268 : | malsyned | 947 | my $userSetRecord; |
| 269 : | my %overrideArgs; | ||
| 270 : | if ($forOneUser) { | ||
| 271 : | $userSetRecord = $db->getUserSet($editForUser[0], $setName); | ||
| 272 : | foreach my $field (@{SET_FIELDS()}) { | ||
| 273 : | malsyned | 959 | $overrideArgs{$field} = [defined $userSetRecord->$field, ($field =~ /_date$/ ? formatDateTime($userSetRecord->$field) : $userSetRecord->$field)]; |
| 274 : | malsyned | 947 | } |
| 275 : | } else { | ||
| 276 : | foreach my $field (@{SET_FIELDS()}) { | ||
| 277 : | $overrideArgs{$field} = [undef, undef]; | ||
| 278 : | } | ||
| 279 : | } | ||
| 280 : | |||
| 281 : | malsyned | 959 | print CGI::h2({}, "Set Data"), "\n"; |
| 282 : | print CGI::start_form({method=>"post", action=>$r->uri}), "\n"; | ||
| 283 : | malsyned | 935 | print CGI::table({}, |
| 284 : | CGI::Tr({}, [ | ||
| 285 : | malsyned | 959 | setRowHTML("Open Date:", "open_date", formatDateTime($setRecord->open_date), undef, @{$overrideArgs{open_date}})."\n", |
| 286 : | setRowHTML("Due Date:", "due_date", formatDateTime($setRecord->due_date), undef, @{$overrideArgs{due_date}})."\n", | ||
| 287 : | setRowHTML("Answer Date:", "answer_date", formatDateTime($setRecord->answer_date), undef, @{$overrideArgs{answer_date}})."\n", | ||
| 288 : | setRowHTML("Set Header:", "set_header", $setRecord->set_header, undef, @{$overrideArgs{set_header}})."\n", | ||
| 289 : | setRowHTML("Problem Header:", "problem_header", $setRecord->problem_header, undef, @{$overrideArgs{problem_header}})."\n" | ||
| 290 : | malsyned | 935 | ]) |
| 291 : | ); | ||
| 292 : | |||
| 293 : | malsyned | 967 | print hiddenEditForUserFields(@editForUser); |
| 294 : | malsyned | 935 | print $self->hidden_authen_fields; |
| 295 : | malsyned | 936 | print CGI::input({type=>"submit", name=>"submit_set_changes", value=>"Save Set"}); |
| 296 : | malsyned | 935 | print CGI::end_form(); |
| 297 : | |||
| 298 : | malsyned | 959 | ## Problems Form ## |
| 299 : | malsyned | 979 | my @problemList = $db->listGlobalProblems($setName); |
| 300 : | malsyned | 976 | print CGI::a({name=>"problems"}); |
| 301 : | malsyned | 935 | print CGI::h2({}, "Problems"); |
| 302 : | malsyned | 979 | if (scalar(@problemList)) { |
| 303 : | print CGI::start_form({method=>"POST", action=>$r->uri.'#problems'}); | ||
| 304 : | print CGI::start_table({border=>1, cellpadding=>4}); | ||
| 305 : | print CGI::Tr({}, CGI::th({}, [ | ||
| 306 : | ($forUsers ? () : ("Delete?")), | ||
| 307 : | "Problem", | ||
| 308 : | ($forUsers ? ("Status", "Problem Seed") : ()), | ||
| 309 : | "Source File", "Max. Attempts", "Weight", | ||
| 310 : | ($forUsers ? ("Number Correct", "Number Incorrect") : ()) | ||
| 311 : | ])); | ||
| 312 : | foreach my $problem (sort {$a <=> $b} @problemList) { | ||
| 313 : | my $problemRecord = $db->getGlobalProblem($setName, $problem); | ||
| 314 : | my $problemID = $problemRecord->problem_id; | ||
| 315 : | my $userProblemRecord; | ||
| 316 : | my %problemOverrideArgs; | ||
| 317 : | |||
| 318 : | if ($forOneUser) { | ||
| 319 : | $userProblemRecord = $db->getUserProblem($editForUser[0], $setName, $problem); | ||
| 320 : | foreach my $field (@{PROBLEM_FIELDS()}) { | ||
| 321 : | $problemOverrideArgs{$field} = [defined $userProblemRecord->$field, $userProblemRecord->$field]; | ||
| 322 : | } | ||
| 323 : | # } elsif ($forUsers) { | ||
| 324 : | # foreach my $field (@{PROBLEM_FIELDS()}) { | ||
| 325 : | # $problemOverrideArgs{$field} = ["", ""]; | ||
| 326 : | # } | ||
| 327 : | } else { | ||
| 328 : | foreach my $field (@{PROBLEM_FIELDS()}) { | ||
| 329 : | $problemOverrideArgs{$field} = [undef, undef]; | ||
| 330 : | } | ||
| 331 : | malsyned | 957 | } |
| 332 : | malsyned | 979 | |
| 333 : | print CGI::Tr({}, | ||
| 334 : | CGI::td({}, [ | ||
| 335 : | ($forUsers ? () : (CGI::input({type=>"checkbox", name=>"deleteProblem", value=>$problemID}))), | ||
| 336 : | CGI::a({href=>"/webwork/$courseName/instructor/pgProblemEditor/".$setName.'/'.$problemID.'?'.$self->url_authen_args}, $problemID), | ||
| 337 : | ($forUsers ? ( | ||
| 338 : | problemElementHTML("problem_${problemID}_status", $userProblemRecord->status, "7"), | ||
| 339 : | problemElementHTML("problem_${problemID}_problem_seed", $userProblemRecord->problem_seed, "7"), | ||
| 340 : | ) : ()), | ||
| 341 : | problemElementHTML("problem_${problemID}_source_file", $problemRecord->source_file, "40", @{$problemOverrideArgs{source_file}}), | ||
| 342 : | problemElementHTML("problem_${problemID}_max_attempts",$problemRecord->max_attempts,"7", @{$problemOverrideArgs{max_attempts}}), | ||
| 343 : | problemElementHTML("problem_${problemID}_value",$problemRecord->value,"7", @{$problemOverrideArgs{value}}), | ||
| 344 : | ($forUsers ? ( | ||
| 345 : | problemElementHTML("problem_${problemID}_num_correct", $userProblemRecord->num_correct, "7"), | ||
| 346 : | problemElementHTML("problem_${problemID}_num_incorrect", $userProblemRecord->num_incorrect, "7") | ||
| 347 : | ) : ()) | ||
| 348 : | ]) | ||
| 349 : | |||
| 350 : | ) | ||
| 351 : | malsyned | 957 | } |
| 352 : | malsyned | 979 | print CGI::end_table(); |
| 353 : | print hiddenEditForUserFields(@editForUser); | ||
| 354 : | print $self->hidden_authen_fields; | ||
| 355 : | malsyned | 982 | print CGI::input({type=>"submit", name=>"submit_problem_changes", value=>"Save Problem Changes"}); |
| 356 : | malsyned | 979 | print CGI::end_form(); |
| 357 : | } else { | ||
| 358 : | print CGI::p("This set doesn't contain any problems yet."); | ||
| 359 : | malsyned | 935 | } |
| 360 : | |||
| 361 : | malsyned | 968 | unless ($forUsers) { |
| 362 : | malsyned | 976 | my $libraryRoot = $ce->{courseDirs}->{templates}; |
| 363 : | my @path = defined $self->{path} ? @{$self->{path}} : (); | ||
| 364 : | unshift @path, $libraryRoot; | ||
| 365 : | print CGI::a({name=>"addProblem"}); | ||
| 366 : | print CGI::h3({}, "Add Problem(s)"); | ||
| 367 : | print CGI::start_form({method=>"post", action=>$r->uri.'#addProblem'}); | ||
| 368 : | print CGI::input({type=>"hidden", name=>"fileBrowsing", value=>"Yes"}); | ||
| 369 : | print CGI::start_table(); | ||
| 370 : | malsyned | 982 | my $columns = ""; |
| 371 : | malsyned | 976 | for (my $counter = 0; $counter < scalar(@path); $counter++) { |
| 372 : | malsyned | 982 | $columns .= CGI::td(directoryListHTML ($counter, (exists $path[$counter+1] ? $path[$counter+1] : []), @path)); |
| 373 : | malsyned | 976 | } |
| 374 : | malsyned | 982 | print CGI::Tr($columns); |
| 375 : | malsyned | 976 | print CGI::end_table(); |
| 376 : | malsyned | 968 | print $self->hidden_authen_fields; |
| 377 : | print CGI::end_form(); | ||
| 378 : | } | ||
| 379 : | malsyned | 935 | return ""; |
| 380 : | } | ||
| 381 : | |||
| 382 : | gage | 860 | 1; |
| aubreyja at gmail dot com | ViewVC Help |
| Powered by ViewVC 1.0.9 |