You can subscribe to this list here.
2004 |
Jan
|
Feb
|
Mar
|
Apr
|
May
|
Jun
|
Jul
|
Aug
|
Sep
|
Oct
|
Nov
|
Dec
(58) |
---|---|---|---|---|---|---|---|---|---|---|---|---|
2005 |
Jan
(53) |
Feb
(56) |
Mar
|
Apr
|
May
(30) |
Jun
(78) |
Jul
(121) |
Aug
(155) |
Sep
(77) |
Oct
(61) |
Nov
(45) |
Dec
(94) |
2006 |
Jan
(116) |
Feb
(33) |
Mar
(11) |
Apr
(23) |
May
(60) |
Jun
(89) |
Jul
(130) |
Aug
(109) |
Sep
(124) |
Oct
(63) |
Nov
(82) |
Dec
(45) |
2007 |
Jan
(31) |
Feb
(35) |
Mar
(123) |
Apr
(36) |
May
(18) |
Jun
(134) |
Jul
(133) |
Aug
(241) |
Sep
(126) |
Oct
(31) |
Nov
(15) |
Dec
(5) |
2008 |
Jan
(11) |
Feb
(6) |
Mar
(16) |
Apr
(29) |
May
(43) |
Jun
(149) |
Jul
(27) |
Aug
(29) |
Sep
(37) |
Oct
(20) |
Nov
(4) |
Dec
(6) |
2009 |
Jan
(34) |
Feb
(30) |
Mar
(16) |
Apr
(6) |
May
(1) |
Jun
(32) |
Jul
(22) |
Aug
(7) |
Sep
(18) |
Oct
(50) |
Nov
(22) |
Dec
(8) |
2010 |
Jan
(17) |
Feb
(15) |
Mar
(10) |
Apr
(9) |
May
(67) |
Jun
(30) |
Jul
|
Aug
|
Sep
(2) |
Oct
|
Nov
(1) |
Dec
|
From: Mike G. v. a. <act...@de...> - 2004-12-19 01:08:11
|
Log Message: ----------- Changed unless $value =~ m/^[\w-.]*$/ to unless $value =~ m/^[.\w-]*$/ for some reason having the period at the end was causing the warning: False [] range "\w-" before HERE mark in regex m/^[\w- << HERE .]*$/ at /home/gage/webwork/webwork-modperl/lib/WeBWorK/DB.pm line 1974. Modified Files: -------------- webwork-modperl/lib/WeBWorK: DB.pm Revision Data ------------- Index: DB.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/DB.pm,v retrieving revision 1.60 retrieving revision 1.61 diff -Llib/WeBWorK/DB.pm -Llib/WeBWorK/DB.pm -u -r1.60 -r1.61 --- lib/WeBWorK/DB.pm +++ lib/WeBWorK/DB.pm @@ -1970,8 +1970,8 @@ croak "checkKeyfields: invalid characters in $keyfield field: $value (valid characters are [0-9])" unless $value =~ m/^\d*$/; } else { - croak "checkKeyfields: invalid characters in $keyfield field: $value (valid characters are [A-Za-z0-9_\.])" - unless $value =~ m/^[\w-\.]*$/; + croak "checkKeyfields: invalid characters in $keyfield field: $value (valid characters are [A-Za-z0-9_.])" + unless $value =~ m/^[.\w-]*$/; } } } |
From: Mike G. v. a. <act...@de...> - 2004-12-18 22:49:40
|
Log Message: ----------- Call $r->param("user") only in scalar context Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: Assigner.pm Revision Data ------------- Index: Assigner.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm,v retrieving revision 1.25 retrieving revision 1.26 diff -Llib/WeBWorK/ContentGenerator/Instructor/Assigner.pm -Llib/WeBWorK/ContentGenerator/Instructor/Assigner.pm -u -r1.25 -r1.26 --- lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm +++ lib/WeBWorK/ContentGenerator/Instructor/Assigner.pm @@ -46,13 +46,15 @@ my $db = $r->db; my $authz = $r->authz; my $ce = $r->ce; - + + my $user = $r->param('user'); + # Check permissions return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") - unless $authz->hasPermissions($r->param("user"), "access_instructor_tools"); + unless $authz->hasPermissions($user, "access_instructor_tools"); return CGI::div({class=>"ResultsWithError"}, "You are not authorized to assign problem sets.") - unless $authz->hasPermissions($r->param("user"), "assign_problem_sets"); + unless $authz->hasPermissions($user, "assign_problem_sets"); print CGI::p("Select one or more sets and one or more users below to assign/unassign" |
From: Mike G. v. a. <act...@de...> - 2004-12-18 22:36:06
|
Log Message: ----------- allow . in login names -- cross your fingers and hope this works. Modified Files: -------------- webwork-modperl/lib/WeBWorK: DB.pm Revision Data ------------- Index: DB.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/DB.pm,v retrieving revision 1.59 retrieving revision 1.60 diff -Llib/WeBWorK/DB.pm -Llib/WeBWorK/DB.pm -u -r1.59 -r1.60 --- lib/WeBWorK/DB.pm +++ lib/WeBWorK/DB.pm @@ -1970,8 +1970,8 @@ croak "checkKeyfields: invalid characters in $keyfield field: $value (valid characters are [0-9])" unless $value =~ m/^\d*$/; } else { - croak "checkKeyfields: invalid characters in $keyfield field: $value (valid characters are [A-Za-z0-9_])" - unless $value =~ m/^[\w-]*$/; + croak "checkKeyfields: invalid characters in $keyfield field: $value (valid characters are [A-Za-z0-9_\.])" + unless $value =~ m/^[\w-\.]*$/; } } } |
From: Mike G. v. a. <act...@de...> - 2004-12-18 22:35:20
|
Log Message: ----------- make sure that $r->param("users") is only used in a scalar context. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: AddUsers.pm Revision Data ------------- Index: AddUsers.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/AddUsers.pm,v retrieving revision 1.15 retrieving revision 1.16 diff -Llib/WeBWorK/ContentGenerator/Instructor/AddUsers.pm -Llib/WeBWorK/ContentGenerator/Instructor/AddUsers.pm -u -r1.15 -r1.16 --- lib/WeBWorK/ContentGenerator/Instructor/AddUsers.pm +++ lib/WeBWorK/ContentGenerator/Instructor/AddUsers.pm @@ -114,10 +114,10 @@ # Check permissions return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") - unless $authz->hasPermissions($r->param("user"), "access_instructor_tools"); + unless $authz->hasPermissions($user, "access_instructor_tools"); return CGI::div({class=>"ResultsWithError"}, "You are not authorized to modify student data.") - unless $authz->hasPermissions($r->param("user"), "modify_student_data"); + unless $authz->hasPermissions($user, "modify_student_data"); return join("", |
From: Mike G. v. a. <act...@de...> - 2004-12-18 22:26:01
|
Log Message: ----------- Make sure that $r->param("user") is only called in scalar context. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: Scoring.pm Revision Data ------------- Index: Scoring.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Scoring.pm,v retrieving revision 1.37 retrieving revision 1.38 diff -Llib/WeBWorK/ContentGenerator/Instructor/Scoring.pm -Llib/WeBWorK/ContentGenerator/Instructor/Scoring.pm -u -r1.37 -r1.38 --- lib/WeBWorK/ContentGenerator/Instructor/Scoring.pm +++ lib/WeBWorK/ContentGenerator/Instructor/Scoring.pm @@ -131,10 +131,10 @@ # Check permissions return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") - unless $authz->hasPermissions($r->param("user"), "access_instructor_tools"); + unless $authz->hasPermissions($user, "access_instructor_tools"); return CGI::div({class=>"ResultsWithError"}, "You are not authorized to score sets.") - unless $authz->hasPermissions($r->param("user"), "score_sets"); + unless $authz->hasPermissions($user, "score_sets"); print join("", CGI::start_form(-method=>"POST", -action=>$scoringURL),"\n", |
From: Mike G. v. a. <act...@de...> - 2004-12-18 22:07:41
|
Log Message: ----------- Changed comment to This answer is the same as the one you just submitted or previewed. This addreses bug #557 Modified Files: -------------- pg/macros: PGanswermacros.pl Revision Data ------------- Index: PGanswermacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGanswermacros.pl,v retrieving revision 1.27 retrieving revision 1.28 diff -Lmacros/PGanswermacros.pl -Lmacros/PGanswermacros.pl -u -r1.27 -r1.28 --- macros/PGanswermacros.pl +++ macros/PGanswermacros.pl @@ -2000,7 +2000,7 @@ sub { my $rh_ans = shift; if ( defined($rh_ans->{'ans_equals_prev_ans'}) and $rh_ans->{'ans_equals_prev_ans'} and $rh_ans->{score}==0) { - $rh_ans->{ans_message} = "This answer is the same as the one you just submitted."; + $rh_ans->{ans_message} = "This answer is the same as the one you just submitted or previewed."; } $rh_ans; } |
From: Mike G. v. a. <act...@de...> - 2004-12-18 21:37:17
|
Log Message: ----------- Modifications to make sure that problems with weight 0 print weight as 0 rather than as a blank. This addresses bug #730 Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: ProblemSetDetail.pm Revision Data ------------- Index: ProblemSetDetail.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm,v retrieving revision 1.14 retrieving revision 1.15 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -u -r1.14 -r1.15 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm @@ -268,9 +268,10 @@ my $choose = ($properties{type} eq "choose") && ($properties{override} ne "none"); my $globalValue = $globalRecord->{$field}; - $globalValue = $globalValue ? ($labels{$globalValue || ""} || $globalValue) : ""; + # use defined instead of value in order to allow 0 to printed, e.g. for the 'value' field + $globalValue = (defined($globalValue)) ? ($labels{$globalValue || ""} || $globalValue) : ""; my $userValue = $userRecord->{$field}; - $userValue = $userValue ? ($labels{$userValue || ""} || $userValue) : ""; + $userValue = (defined($userValue)) ? ($labels{$userValue || ""} || $userValue) : ""; if ($field =~ /_date/) { $globalValue = $self->formatDateTime($globalValue) if $globalValue; |
From: Mike G. v. a. <act...@de...> - 2004-12-18 20:56:08
|
Log Message: ----------- Added a line to check for empty records. The hope is that this fixes bug #733 Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator: Grades.pm Revision Data ------------- Index: Grades.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Grades.pm,v retrieving revision 1.10 retrieving revision 1.11 diff -Llib/WeBWorK/ContentGenerator/Grades.pm -Llib/WeBWorK/ContentGenerator/Grades.pm -u -r1.10 -r1.11 --- lib/WeBWorK/ContentGenerator/Grades.pm +++ lib/WeBWorK/ContentGenerator/Grades.pm @@ -75,7 +75,7 @@ $line.=$delimiter; # add 'A' to end of line so that # last field is never empty @lineArray = split(/\s*${delimiter}\s*/,$line); - $lineArray[0] =~s/^\s*//; # remove white space from first element + $lineArray[0] =~s/^\s*// if defined($lineArray[0]); # remove white space from first element @lineArray; } |
From: Mike G. v. a. <act...@de...> - 2004-12-18 20:46:38
|
Log Message: ----------- Added summary column for the total value of webwork problems done (sum of the weights of each of the problems correctly completed) Added column for percentage correct (total weight of correct problems divided by the total weight of all of the problems). This set still needs major dehackifying!!!!!! This fixes bug #747. I hope that I've put in enough checks to satisfy bug #746 as well. I will close that bug. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: Scoring.pm Revision Data ------------- Index: Scoring.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Scoring.pm,v retrieving revision 1.36 retrieving revision 1.37 diff -Llib/WeBWorK/ContentGenerator/Instructor/Scoring.pm -Llib/WeBWorK/ContentGenerator/Instructor/Scoring.pm -u -r1.36 -r1.37 --- lib/WeBWorK/ContentGenerator/Instructor/Scoring.pm +++ lib/WeBWorK/ContentGenerator/Instructor/Scoring.pm @@ -30,6 +30,10 @@ use WeBWorK::DB::Utils qw(initializeUserProblem); use WeBWorK::Timing; + +our @userInfoColumnHeadings = ("STUDENT ID", "login ID", "LAST NAME", "FIRST NAME", "SECTION", "RECITATION"); +our @userInfoFields = ("student_id", "user_id","last_name", "first_name", "section", "recitation"); + sub initialize { my ($self) = @_; my $r = $self->r; @@ -60,21 +64,21 @@ next unless $User; $Users{$User->user_id} = $User; } - my @sortedUserIDs = sort { $Users{$a}->student_id cmp $Users{$b}->student_id } + my @sortedUserIDs = sort { $Users{$a}->last_name cmp $Users{$b}->last_name } keys %Users; - my @userInfo = (\%Users, \@sortedUserIDs); + #my @userInfo = (\%Users, \@sortedUserIDs); $WeBWorK::timer->continue("done pre-fetching users") if defined($WeBWorK::timer); my $scoringType = ($recordSingleSetScores) ?'everything':'totals'; my (@everything, @normal,@full,@info,@totalsColumn); - @info = $self->scoreSet($selected[0], "info", undef, @userInfo) if defined($selected[0]); + @info = $self->scoreSet($selected[0], "info", undef, \%Users, \@sortedUserIDs) if defined($selected[0]); @totals = @info; my $showIndex = defined($r->param('includeIndex')) ? defined($r->param('includeIndex')) : 0; foreach my $setID (@selected) { next unless defined $setID; if ($scoringType eq 'everything') { - @everything = $self->scoreSet($setID, "everything", $showIndex, @userInfo); + @everything = $self->scoreSet($setID, "everything", $showIndex, \%Users, \@sortedUserIDs); @normal = $self->everything2normal(@everything); @full = $self->everything2full(@everything); @info = $self->everything2info(@everything); @@ -83,10 +87,12 @@ $self->writeCSV("$scoringDir/s${setID}scr.csv", @normal); $self->writeCSV("$scoringDir/s${setID}ful.csv", @full); } else { - @totalsColumn = $self->scoreSet($setID, "totals", $showIndex, @userInfo); + @totalsColumn = $self->scoreSet($setID, "totals", $showIndex, \%Users, \@sortedUserIDs); $self->appendColumns(\@totals, \@totalsColumn); } } + my @sum_scores = $self->sumScores(\@totals, $showIndex, \%Users, \@sortedUserIDs); + $self->appendColumns( \@totals,\@sum_scores); $self->writeCSV("$scoringDir/${courseName}_totals.csv", @totals); } @@ -134,26 +140,45 @@ CGI::start_form(-method=>"POST", -action=>$scoringURL),"\n", $self->hidden_authen_fields,"\n", CGI::hidden({-name=>'scoreSelected', -value=>1}), - $self->popup_set_form, - CGI::br(), - CGI::checkbox({ -name=>'includeIndex', - -value=>1, - -label=>'IncludeIndex', - -checked=>1, - }, - 'Include Index' - ), - CGI::br(), - CGI::checkbox({ -name=>'recordSingleSetScores', - -value=>1, - -label=>'Record Scores for Single Sets', - -checked=>0, - }, - 'Record Scores for Single Sets' - ), - CGI::br(), - CGI::input({type=>'submit',value=>'Score selected set(s)...',name=>'score-sets'}), + CGI::start_table({border=>1,}), + CGI::Tr( + CGI::td($self->popup_set_form), + CGI::td( + CGI::checkbox({ -name=>'includeIndex', + -value=>1, + -label=>'Include Index', + -checked=>0, + }, + ), + CGI::br(), + CGI::checkbox({ -name=>'includeTotals', + -value=>1, + -label=>'Include Total score column', + -checked=>1, + }, + ), + CGI::br(), + CGI::checkbox({ -name=>'includePercent', + -value=>1, + -label=>'Include Percent correct column', + -checked=>1, + }, + ), + CGI::br(), + CGI::checkbox({ -name=>'recordSingleSetScores', + -value=>1, + -label=>'Record Scores for Single Sets', + -checked=>0, + }, + 'Record Scores for Single Sets' + ), + ), + ), + CGI::Tr(CGI::td({colspan =>2,align=>'center'}, + CGI::input({type=>'submit',value=>'Score selected set(s)...',name=>'score-sets'}), + )), + CGI::end_table(), ); @@ -289,8 +314,6 @@ push @scoringData, []; } - my @userInfoColumnHeadings = ("STUDENT ID", "LAST NAME", "FIRST NAME", "SECTION", "RECITATION"); - my @userInfoFields = ("student_id", "last_name", "first_name", "section", "recitation"); #my @userKeys = sort keys %users; # list of "student IDs" NOT user IDs if ($scoringItems->{header}) { @@ -375,15 +398,6 @@ } $valueTotal += $globalProblem->value; - #my @userLoginIDs = $db->listUsers(); - #$WeBWorK::timer->continue("Begin getting user problems for set $setID, problem $problemIDs[$problem]") if defined($WeBWorK::timer); - ##my @userProblems = $db->getMergedProblems( map { [ $_, $setID, $problemIDs[$problem] ] } @userLoginIDs ); - #my @userProblems = $db->getUserProblems( map { [ $_, $setID, $problemIDs[$problem] ] } @userLoginIDs ); # checked - #my %userProblems; - #foreach my $item (@userProblems) { - # $userProblems{$item->user_id} = $item if ref $item; - #} - #$WeBWorK::timer->continue("End getting user problems for set $setID, problem $problemIDs[$problem]") if defined($WeBWorK::timer); for (my $user = 0; $user < @sortedUserIDs; $user++) { #my $userProblem = $userProblems{ $users{$userKeys[$user]}->user_id }; @@ -397,8 +411,8 @@ $userProblem->num_incorrect(0); } $userStatusTotals{$user} = 0 unless exists $userStatusTotals{$user}; - #$userStatusTotals{$user} += $userProblem->status * $userProblem->value; - $userStatusTotals{$user} += $userProblem->status * $globalProblem->value; + my $user_problem_status = ($userProblem->status =~/^[\d\.]+$/) ? $userProblem->status : 0; # ensure it's numeric + $userStatusTotals{$user} += $user_problem_status * $globalProblem->value; if ($scoringItems->{successIndex}) { $numberOfAttempts{$user} = 0 unless defined($numberOfAttempts{$user}); my $num_correct = $userProblem->num_correct; @@ -427,13 +441,20 @@ my $totalsColumn = $format eq "totals" ? 0 : 5 + @problemIDs * $columnsPerProblem; $scoringData[0][$totalsColumn] = ""; $scoringData[1][$totalsColumn] = $setRecord->set_id; - $scoringData[1][$totalsColumn+1] = $setRecord->set_id if $scoringItems->{successIndex}; $scoringData[2][$totalsColumn] = ""; $scoringData[3][$totalsColumn] = ""; $scoringData[4][$totalsColumn] = ""; $scoringData[5][$totalsColumn] = $valueTotal; $scoringData[6][$totalsColumn] = "total"; - $scoringData[6][$totalsColumn+1] = "index" if $scoringItems->{successIndex}; + if ($scoringItems->{successIndex}) { + $scoringData[0][$totalsColumn+1] = ""; + $scoringData[1][$totalsColumn+1] = $setRecord->set_id; + $scoringData[2][$totalsColumn+1] = ""; + $scoringData[3][$totalsColumn+1] = ""; + $scoringData[4][$totalsColumn+1] = ""; + $scoringData[5][$totalsColumn+1] = '100'; + $scoringData[6][$totalsColumn+1] = "index" ; + } for (my $user = 0; $user < @sortedUserIDs; $user++) { $scoringData[7+$user][$totalsColumn] = sprintf("%4.1f",$userStatusTotals{$user}); $scoringData[7+$user][$totalsColumn+1] = sprintf("%4.1f",$userSuccessIndex{$user}) if $scoringItems->{successIndex}; @@ -442,7 +463,50 @@ $WeBWorK::timer->continue("End set $setID") if defined($WeBWorK::timer); return @scoringData; } +sub sumScores { # Create a totals column for each student + my $self = shift; + my $r_totals = shift; + my $showIndex = shift; + my $r_users = shift; + my $r_sorted_user_ids =shift; + my $r = $self->r; + my $db = $r->db; + my @scoringData = (); + my $index_increment = ($showIndex) ? 2 : 1; + # This whole thing is a hack, but here goes. We're going to sum the appropriate columns of the totals file: + # I believe we have $r_totals->[rows]->[cols] -- the way it's printed out. + my $start_column = 6; #The problem column + my $last_column = $#{$r_totals->[1]}; # try to figure out the number of the last column in the array. + my $row_count = $#{$r_totals}; + + # Calculate total number of problems for the course. + my $totalPoints = 0; + my $problemValueRow = 5; + for( my $j = $start_column;$j<=$last_column;$j+= $index_increment) { + my $score = $r_totals->[$problemValueRow]->[$j]; + $totalPoints += ($score =~/^\s*[\d\.]+\s*$/)? $score : 0; + } + foreach my $i (0..$row_count) { + my $studentTotal = 0; + for( my $j = $start_column;$j<=$last_column;$j+= $index_increment) { + my $score = $r_totals->[$i]->[$j]; + $studentTotal += ($score =~/^\s*[\d\.]+\s*$/)? $score : 0; + + } + $scoringData[$i][0] =sprintf("%4.1f",$studentTotal); + $scoringData[$i][1] =($totalPoints) ?sprintf("%4.1f",100*$studentTotal/$totalPoints) : 0; + } + $scoringData[0] = ['','']; + $scoringData[1] = ['summary', '%score']; + $scoringData[2] = ['','']; + $scoringData[3] = ['','']; + $scoringData[4] = ['','']; + $scoringData[6] = ['','']; + + return @scoringData; + +} # Often it's more efficient to just get everything out of the database # and then pick out what you want later. Hence, these "everything2*" functions sub everything2info { @@ -618,7 +682,7 @@ my ($self, $string, $padTo) = @_; $string = '' unless defined $string; my $spaces = $padTo - length $string; - return $string . " "x$spaces; + return " "x$spaces.$string; } sub maxLength { |
From: Mike G. v. a. <act...@de...> - 2004-12-18 16:26:57
|
Log Message: ----------- Added an option to rename the "Change Display Settings" submit buttton. Modified Files: -------------- webwork-modperl/lib/WeBWorK/HTML: ScrollingRecordList.pm Revision Data ------------- Index: ScrollingRecordList.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/HTML/ScrollingRecordList.pm,v retrieving revision 1.6 retrieving revision 1.7 diff -Llib/WeBWorK/HTML/ScrollingRecordList.pm -Llib/WeBWorK/HTML/ScrollingRecordList.pm -u -r1.6 -r1.7 --- lib/WeBWorK/HTML/ScrollingRecordList.pm +++ lib/WeBWorK/HTML/ScrollingRecordList.pm @@ -81,6 +81,8 @@ my @ids = (); my %labels = (); + my $refresh_button_name = defined($options{refresh_button_name}) ? $options{refresh_button_name}:"Change Display Settings"; + my @selected_records = $r->param("$name"); if (@Records) { @@ -166,7 +168,7 @@ CGI::td(CGI::scrolling_list(%filter_options)), ]), ), - CGI::submit("$name!refresh", "Change Display Settings"), CGI::br(), + CGI::submit("$name!refresh", $refresh_button_name), CGI::br(), CGI::scrolling_list(%list_options), ); } |
From: Mike G. v. a. <act...@de...> - 2004-12-18 16:25:54
|
Log Message: ----------- Removed the student progress lines from the Stats.pm page. They are still available in the StudentProgress.pm module Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: Stats.pm Revision Data ------------- Index: Stats.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/Stats.pm,v retrieving revision 1.47 retrieving revision 1.48 diff -Llib/WeBWorK/ContentGenerator/Instructor/Stats.pm -Llib/WeBWorK/ContentGenerator/Instructor/Stats.pm -u -r1.47 -r1.48 --- lib/WeBWorK/ContentGenerator/Instructor/Stats.pm +++ lib/WeBWorK/ContentGenerator/Instructor/Stats.pm @@ -533,51 +533,51 @@ print CGI::end_table(); ##################################################################################### # construct header - my $problem_header = ''; - - foreach my $i (1..$max_num_problems) { - $problem_header .= CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>"p$i"})},threeSpaceFill($i) ); - } - print - CGI::p("Details:",CGI::i('The success indicator for each student is calculated as'),CGI::br(), - '(totalNumberOfCorrectProblems / totalNumberOfProblems)^2/ AvgNumberOfAttemptsPerProblem)',CGI::br(), - CGI::i('or 0 if there are no attempts.') - ), - "Click heading to sort table: ", - defined($sort_method_name) ?" sort method is $sort_method_name":"", - CGI::start_table({-border=>5,style=>'font-size:smaller'}), - CGI::Tr(CGI::td( {-align=>'left'}, - [CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>'name' })},'Name'), - CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>'score'})},'Score'), - 'Out'.CGI::br().'Of', - CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>'index'})},'Ind'), - 'Problems'.CGI::br().$problem_header, - CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>'section'})},'Section'), - 'Recitation', - 'login_name', - ]) - - ); - - foreach my $rec (@augmentedUserRecords) { - my $fullName = join("", $rec->{first_name}," ", $rec->{last_name}); - my $email = $rec->{email_address}; - my $twoString = $rec->{twoString}; - print CGI::Tr( - CGI::td(CGI::a({-href=>$rec->{act_as_student}},$fullName), CGI::br(), CGI::a({-href=>"mailto:$email"},$email)), - CGI::td( sprintf("%0.2f",$rec->{score}) ), # score - CGI::td($rec->{total}), # out of - CGI::td(sprintf("%0.0f",100*($rec->{index}) )), # indicator - CGI::td($rec->{problemString}), # problems - CGI::td($self->nbsp($rec->{section})), - CGI::td($self->nbsp($rec->{recitation})), - CGI::td($rec->{user_id}), - - ); - } - - print CGI::end_table(); - +# my $problem_header = ''; +# +# foreach my $i (1..$max_num_problems) { +# $problem_header .= CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>"p$i"})},threeSpaceFill($i) ); +# } +# print +# CGI::p("Details:",CGI::i('The success indicator for each student is calculated as'),CGI::br(), +# '(totalNumberOfCorrectProblems / totalNumberOfProblems)^2/ AvgNumberOfAttemptsPerProblem)',CGI::br(), +# CGI::i('or 0 if there are no attempts.') +# ), +# "Click heading to sort table: ", +# defined($sort_method_name) ?" sort method is $sort_method_name":"", +# CGI::start_table({-border=>5,style=>'font-size:smaller'}), +# CGI::Tr(CGI::td( {-align=>'left'}, +# [CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>'name' })},'Name'), +# CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>'score'})},'Score'), +# 'Out'.CGI::br().'Of', +# CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>'index'})},'Ind'), +# 'Problems'.CGI::br().$problem_header, +# CGI::a({"href"=>$self->systemLink($setStatsPage,params=>{sort=>'section'})},'Section'), +# 'Recitation', +# 'login_name', +# ]) +# +# ); +# +# foreach my $rec (@augmentedUserRecords) { +# my $fullName = join("", $rec->{first_name}," ", $rec->{last_name}); +# my $email = $rec->{email_address}; +# my $twoString = $rec->{twoString}; +# print CGI::Tr( +# CGI::td(CGI::a({-href=>$rec->{act_as_student}},$fullName), CGI::br(), CGI::a({-href=>"mailto:$email"},$email)), +# CGI::td( sprintf("%0.2f",$rec->{score}) ), # score +# CGI::td($rec->{total}), # out of +# CGI::td(sprintf("%0.0f",100*($rec->{index}) )), # indicator +# CGI::td($rec->{problemString}), # problems +# CGI::td($self->nbsp($rec->{section})), +# CGI::td($self->nbsp($rec->{recitation})), +# CGI::td($rec->{user_id}), +# +# ); +# } +# +# print CGI::end_table(); +# |
From: Mike G. v. a. <act...@de...> - 2004-12-18 16:24:34
|
Log Message: ----------- Changed some UI features. In particular the scrolling list is now used both for selecting students to send mail to AND for selecting the preview message (if multiple students are selected the first is used as the preview). The "command table" is now one line taller than it was, but it it now effectively narrower (just two columns) -- the last column has only the row and column options and is not used that much. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: SendMail.pm Revision Data ------------- Index: SendMail.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/SendMail.pm,v retrieving revision 1.38 retrieving revision 1.39 diff -Llib/WeBWorK/ContentGenerator/Instructor/SendMail.pm -Llib/WeBWorK/ContentGenerator/Instructor/SendMail.pm -u -r1.38 -r1.39 --- lib/WeBWorK/ContentGenerator/Instructor/SendMail.pm +++ lib/WeBWorK/ContentGenerator/Instructor/SendMail.pm @@ -31,7 +31,8 @@ use WeBWorK::HTML::ScrollingRecordList qw/scrollingRecordList/; use WeBWorK::Utils::FilterRecords qw/filterRecords/; -my $REFRESH_RESIZE_BUTTON = "Set preview to: "; # handle submit value idiocy +#my $REFRESH_RESIZE_BUTTON = "Set preview to: "; # handle submit value idiocy +my $UPDATE_SETTINGS_BUTTON = "Update settings and refresh page"; # handle submit value idiocy sub initialize { my ($self) = @_; my $r = $self->r; @@ -79,8 +80,10 @@ $self->{default_msg_file} = $default_msg_file; $self->{old_default_msg_file} = $old_default_msg_file; $self->{merge_file} = (defined($r->param('merge_file' ))) ? $r->param('merge_file') : 'None'; - $self->{preview_user} = (defined($r->param('preview_user'))) ? $r->param('preview_user') : $user; - + #$self->{preview_user} = (defined($r->param('preview_user'))) ? $r->param('preview_user') : $user; + # an expermiment -- share the scrolling list for preivew and sendTo actions. + my @classList = (defined($r->param('classList'))) ? $r->param('classList') : ($user); + $self->{preview_user} = $classList[0] || $user; ############################################################################################# # gather database data @@ -303,8 +306,8 @@ my $script_action = ''; - if(not defined($action) or $action eq 'Open' or $action eq $REFRESH_RESIZE_BUTTON or $action eq 'Sort by' - or $action eq 'Set merge file to:' ){ + if(not defined($action) or $action eq 'Open' + or $action eq $UPDATE_SETTINGS_BUTTON ){ return ''; } @@ -509,7 +512,7 @@ my $permissionLevelTemplate = $db->newPermissionLevel; # This code will require changing if the permission and user tables ever have different keys. - my @users = @{ $self->{ra_users} }; + my @users = sort @{ $self->{ra_users} }; my $ra_user_records = $self->{ra_user_records}; my %classlistLabels = ();# %$hr_classlistLabels; foreach my $ur (@{ $ra_user_records }) { @@ -525,6 +528,7 @@ default_filters => ["all"], size => 5, multiple => 1, + refresh_button_name =>'Update settings and refresh page', }, @{$ra_user_records}); ############################################################################################################## @@ -563,17 +567,31 @@ # first column ############################################################################################# - CGI::td(CGI::strong("Message file: $input_file"),"\n",CGI::br(), + CGI::td(CGI::strong("Message file: "), $input_file,"\n",CGI::br(), CGI::submit(-name=>'action', -value=>'Open'), ' ',"\n", CGI::popup_menu(-name=>'openfilename', -values=>\@sorted_messages, -default=>$input_file - ), "\n",CGI::br(), - - "Save file to: $output_file","\n",CGI::br(), - "\n", 'From:',' ', CGI::textfield(-name=>"from", -size=>30, -value=>$from, -override=>1), - "\n", CGI::br(),'Reply-To: ', CGI::textfield(-name=>"replyTo", -size=>30, -value=>$replyTo, -override=>1), - "\n", CGI::br(),'Subject: ', CGI::br(), CGI::textarea(-name=>'subject', -default=>$subject, -rows=>3,-columns=>30, -override=>1), + ), + "\n",CGI::br(), + CGI::strong("Save file to: "), $output_file, + "\n",CGI::br(), + CGI::strong('Merge file: '), $merge_file, + CGI::br(), + CGI::popup_menu(-name=>'merge_file', + -values=>\@sorted_merge_files, + -default=>$merge_file, + ), "\n", + "\n", + #CGI::hr(), + CGI::div({style=>"background-color: #CCCCCC"}, + "\n", 'From:',' ', CGI::textfield(-name=>"from", -size=>30, -value=>$from, -override=>1), + "\n", CGI::br(),'Reply-To: ', CGI::textfield(-name=>"replyTo", -size=>30, -value=>$replyTo, -override=>1), + "\n", CGI::br(),'Subject: ', CGI::br(), CGI::textarea(-name=>'subject', -default=>$subject, -rows=>3,-columns=>30, -override=>1), + ), + #CGI::hr(), + CGI::submit(-name=>'action', -value=>$UPDATE_SETTINGS_BUTTON), + ), ############################################################################################# # second column @@ -591,7 +609,11 @@ CGI::radio_group(-name=>'radio', -values=>['all_students','studentID'], -labels=>{all_students=>'All students in course',studentID => 'Selected students'}, -default=>'studentID', -linebreak=>0), - CGI::br(),$scrolling_user_list), + CGI::br(),$scrolling_user_list, + CGI::i("Preview set to: "), $preview_record->last_name, + CGI::submit(-name=>'action', -value=>'preview',-label=>'Preview message'),' ', + ), + ## Edit here to insert filtering ## be sure to fail GRACEFULLY! # @@ -620,28 +642,30 @@ # third column ############################################################################################# CGI::td({align=>'left'}, - "<b>Merge file:</b> $merge_file", CGI::br(), - CGI::submit(-name=>'action', -value=>'Set merge file to:'),CGI::br(), - CGI::popup_menu(-name=>'merge_file', - -values=>\@sorted_merge_files, - -default=>$merge_file, - ), "\n",CGI::hr(), - CGI::b("Viewing email for: "), "$preview_user",CGI::br(), - CGI::submit(-name=>'action', -value=>'resize', -label=>$REFRESH_RESIZE_BUTTON),' ', - CGI::popup_menu(-name=>'preview_user', - -values=>\@users, - #-labels=>\%classlistLabels, - -default=>$preview_user, - ), - CGI::br(), - CGI::submit(-name=>'action', -value=>'preview',-label=>'Preview message'),' ', - - CGI::br(), - - CGI::hr(), +# "<b>Merge file:</b> $merge_file", CGI::br(), +# CGI::submit(-name=>'action', -value=>'Set merge file to:'),CGI::br(), +# CGI::popup_menu(-name=>'merge_file', +# -values=>\@sorted_merge_files, +# -default=>$merge_file, +# ), "\n", +# CGI::hr(), +# CGI::b("Viewing email for: "), "$preview_user",CGI::br(), +# CGI::submit(-name=>'action', -value=>'resize', -label=>$REFRESH_RESIZE_BUTTON),' ', +# CGI::popup_menu(-name=>'preview_user', +# -values=>\@users, +# #-labels=>\%classlistLabels, +# -default=>$preview_user, +# ), +# CGI::br(), +# CGI::submit(-name=>'action', -value=>'preview',-label=>'Preview message'),' ', +# +# CGI::br(), +# +# CGI::hr(), " Rows: ", CGI::textfield(-name=>'rows', -size=>3, -value=>$rows), " Columns: ", CGI::textfield(-name=>'columns', -size=>3, -value=>$columns), - CGI::br(),CGI::i('Press any action button to update display'),CGI::br(), + CGI::br(), +# CGI::i('Press any action button to update display'),CGI::br(), #show available macros CGI::popup_menu( -name=>'dummyName', |
From: Mike G. v. a. <act...@de...> - 2004-12-17 18:21:29
|
Log Message: ----------- Removed a warn statement Made sure that r->param('user') is only called in scalar context. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: PGProblemEditor.pm Revision Data ------------- Index: PGProblemEditor.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v retrieving revision 1.49 retrieving revision 1.50 diff -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -u -r1.49 -r1.50 --- lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -323,7 +323,6 @@ my $sourceFilePath = $self->{problemPath}; # strip off template directory prefix $sourceFilePath =~ s|^$ce->{courseDirs}->{templates}/||; - warn "setting sourceFilePath to $sourceFilePath"; if ($action eq 'save_as') { # redirect to myself my $edit_level = $r->param("edit_level") || 0; $edit_level++; @@ -537,10 +536,10 @@ # Check permissions return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") - unless $authz->hasPermissions($r->param("user"), "access_instructor_tools"); + unless $authz->hasPermissions($user, "access_instructor_tools"); return CGI::div({class=>"ResultsWithError"}, "You are not authorized to modify problems.") - unless $authz->hasPermissions($r->param("user"), "modify_student_data"); + unless $authz->hasPermissions($user, "modify_student_data"); # Gathering info |
From: Mike G. v. a. <act...@de...> - 2004-12-17 17:26:23
|
Log Message: ----------- Added a check to handle the case when the $errors flag was not defined. Modified Files: -------------- pg/macros: PGanswermacros.pl Revision Data ------------- Index: PGanswermacros.pl =================================================================== RCS file: /webwork/cvs/system/pg/macros/PGanswermacros.pl,v retrieving revision 1.26 retrieving revision 1.27 diff -Lmacros/PGanswermacros.pl -Lmacros/PGanswermacros.pl -u -r1.26 -r1.27 --- macros/PGanswermacros.pl +++ macros/PGanswermacros.pl @@ -3453,7 +3453,7 @@ push(@differences, $diff); push(@instructorVals,$instructorVal); } - if ($errors eq '' or $options{error_msg_flag} ) { + if (( not defined($errors) ) or $errors eq '' or $options{error_msg_flag} ) { $rh_ans ->{$options{stdout}} = \@differences; $rh_ans ->{ra_student_values} = \@student_values; $rh_ans ->{ra_adjusted_student_values} = \@adjusted_student_values; |
From: Mike G. v. a. <act...@de...> - 2004-12-17 17:24:41
|
Log Message: ----------- Added some styles to use when editing a temporary file. This alerts the user that the file has not yet been permanently saved. Modified Files: -------------- webwork-modperl/conf/templates: ur.template Revision Data ------------- Index: ur.template =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/conf/templates/ur.template,v retrieving revision 1.36 retrieving revision 1.37 diff -Lconf/templates/ur.template -Lconf/templates/ur.template -u -r1.36 -r1.37 --- conf/templates/ur.template +++ conf/templates/ur.template @@ -89,6 +89,12 @@ font.Published { font-style: normal; font-weight: normal; color: #000000; } /* black */ font.Unpublished { font-style: italic; font-weight: normal; color: #aaaaaa; } /* light grey */ +/* styles used when editing a temporary file */ +div.temporaryFile { background-color: #ff9900 ; font-style: italic; } +p.temporaryFile { background-color: #ff9900 ; font-style: italic; } + + + /* text colors for Auditing, Current, and Dropped students */ div.Audit { font-style: normal; color: purple; } div.Enrolled { font-weight: normal; color: black; } @@ -275,4 +281,5 @@ </tr> </table> </body> + </html> |
From: Mike G. v. a. <act...@de...> - 2004-12-17 17:23:15
|
Log Message: ----------- Massive overhaul of this file to put dehackify the code, at least somewhat. The cases for handling each file type and each action are pretty cleanly laid out. The next reasonable step would be to reorganize the code by subclassing it. Since in almost all cases the link knows what file type it wants to be edited when it calls the editor, the links could specifically call the subclass editor. This would either reduce or eliminate the need for the file_type parameter. Properly done subclassing would make it easier to add a new file type. Currently you need to add code in about four places: UI code in body, action code in saveFileChanges, path finding code in getFilePaths, and redirect code in pre_header_initialize. For a subclass this code could all be adjacent. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: PGProblemEditor.pm Revision Data ------------- Index: PGProblemEditor.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm,v retrieving revision 1.48 retrieving revision 1.49 diff -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -Llib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm -u -r1.48 -r1.49 --- lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -30,6 +30,7 @@ use WeBWorK::Utils qw(readFile surePathToFile); use Apache::Constants qw(:common REDIRECT); use HTML::Entities; +use URI::Escape; use WeBWorK::Utils::Tasks qw(fake_set fake_problem); ########################################################### @@ -41,144 +42,454 @@ # The course information and problems are located in the course templates directory. # Course information has the name defined by courseFiles->{course_info} # -# Only files under the template directory ( or linked to this location) can be edited. # # editMode = temporaryFile (view the temp file defined by course_info.txt.user_name.tmp # instead of the file course_info.txt) -# The editFileSuffix is "user_name.tmp" by default. It's definition should be moved to Instructor.pm #FIXME +# this flag is read by Problem.pm and ProblemSet.pm, perhaps others +# The TEMPFILESUFFIX is "user_name.tmp" by default. It's definition should be moved to Instructor.pm #FIXME ########################################################### +########################################################### +# The behavior of this module is essentially defined +# by the values of $file_type and the submit button which is placed in $action +############################################################# +# File types which can be edited +# +# file_type eq 'problem' +# this is the most common type -- this editor can be called by an instructor when viewing any problem. +# the information for retrieving the source file is found using the problemID in order to look +# look up the source file path. +# +# file_type eq 'problem_with_source' +# This is the same as the 'problem' file type except that the source for the problem is found in +# the parameter $r->param('sourceFilePath'). +# +# file_type eq 'set_header' +# This is a special case of editing the problem. The set header is often listed as problem 0 in the set's list of problems. +# +# file_type eq 'hardcopy_header' +# This is a special case of editing the problem. The hardcopy_header is often listed as problem 0 in the set's list of problems. +# But it is used instead of set_header when producing a hardcopy of the problem set in the TeX format, instead of producing HTML +# formatted version for use on the computer screen. +# +# filte_type eq 'course_info +# This allows editing of the course_info.txt file which gives general information about the course. It is called from the +# ProblemSets.pm module. +# +# file_type eq 'blank_problem' +# This is a special call which allows one to create and edit a new PG problem. The "stationery" source for this problem is +# stored in the conf/snippets directory and defined in global.conf by $webworkFiles{screenSnippets}{blankProblem} +############################################################# +# submit button actions -- these and the file_type determine the state of the module +# Save ---- action = save +# Save as ---- action = save_as +# View Problem ---- action = refresh +# Add this problem to: ---- action = add_problem_to_set +# Make this set header for: ---- action = add_set_header_to_set +# Revert ---- action = revert +# no submit button defined ---- action = fresh_edit +################################################### +# +# Determining which is the correct path to the file is a mess!!! FIXME +# The path to the file to be edited is eventually put in tempFilePath +# +# $problemPath is also used as is editFilePath. let's try to regularize these. +#(sourceFile) (problemPath)(tempFilePath)(editFilePath)(forcedSourceFile)(problemPath) +#input parameter can be: sourceFilePath +################################################################# +# params read +# user +# effectiveUser +# submit +# file_type +# problemSeed +# displayMode +# edit_level +# make_local_copy +# sourceFilePath +# problemContents +# save_to_new_file +# + #our $libraryName; #our $rowheight; +our $TEMPFILESUFFIX; sub pre_header_initialize { - my ($self) = @_; - my $r = $self->r; - my $ce = $r->ce; - my $urlpath = $r->urlpath; - my $authz = $r->authz; - my $user = $r->param('user'); - - my $submit_button = $r->param('submit'); # obtain submit command from form - my $file_type = $r->param("file_type") || ''; - + my ($self) = @_; + my $r = $self->r; + my $ce = $r->ce; + my $urlpath = $r->urlpath; + my $authz = $r->authz; + my $user = $r->param('user'); + $TEMPFILESUFFIX = $user.'.tmp'; + + my $submit_button = $r->param('submit'); # obtain submit command from form + my $file_type = $r->param("file_type") || ''; + my $setName = $r->urlpath->arg("setID") ; # using $r->urlpath->arg("setID") ||'' causes trouble with set 0!!! + my $problemNumber = $r->urlpath->arg("problemID"); + # Check permissions return unless ($authz->hasPermissions($user, "access_instructor_tools")); return unless ($authz->hasPermissions($user, "modify_problem_sets")); + + ############################################################################# + # Save file to permanent or temporary file, then redirect for viewing + ############################################################################# + # + # Any file "saved as" should be assigned to "Undefined_Set" and redirectoed to be viewed again in the editor + # + # Problems "saved" or 'refreshed' are to be redirected to the Problem.pm module + # Set headers which are "saved" are to be redirected to the ProblemSet.pm page + # Hardcopy headers which are "saved" are aso to be redirected to the ProblemSet.pm page + # Course_info files are redirected to the ProblemSets.pm page + ############################################################################## + - # Save problem to permanent or temporary file, then redirect for viewing - if (defined($submit_button) and - ($submit_button eq 'Save' or $submit_button eq 'Refresh' - or ($submit_button eq 'Save as' and $file_type eq 'problem'))) { - my $setName = $r->urlpath->arg("setID"); - my $problemNumber = $r->urlpath->arg("problemID"); - - # write the necessary files - # return file path for viewing problem in $self->{currentSourceFilePath} - # obtain the appropriate seed - $self->saveFileChanges($setName, $problemNumber); - - ##### calculate redirect URL based on file type ##### - - # get some information - #my $hostname = $r->hostname(); - #my $port = $r->get_server_port(); - #my $uri = $r->uri; - my $courseName = $urlpath->arg("courseID"); - my $problemSeed = ($r->param('problemSeed')) ? $r->param('problemSeed') : ''; - my $displayMode = ($r->param('displayMode')) ? $r->param('displayMode') : ''; - - my $viewURL = ''; - - if($self->{file_type} eq 'problem') { - if($submit_button eq 'Save as') { # redirect to myself - my $sourceFile = $self->{problemPath}; - # strip off template directory prefix + + ###################################### + # Insure that file_type is defined + ###################################### + # We have already read in the file_type parameter from the form + # + # If this has not been defined we are dealing with a set header + # or regular problem + if (defined($file_type) and ($file_type =~/\S/)) { #file_type is defined and is not blank + # file type is already defined -- do nothing + } else { + # if "sourcFilePath" is defined in the form, then we are getting the path directly. + # if the problem number is defined and is 0 + # then we are dealing with some kind of + # header file. The default is 'set_header' which prints properly + # to the screen. + # If the problem number is not zero, we are dealing with a real problem + ###################################### + if ( defined($r->param('sourceFilePath') and $r->param('sourceFilePath') =~/\S/) ) { + $file_type ='source_path_for_problem_file'; + } elsif ( defined($problemNumber) ) { + if ( $problemNumber =~/^\d+$/ and $problemNumber == 0 ) { # if problem number is numeric and zero + $file_type = 'set_header' unless $file_type eq 'set_header' + or $file_type eq 'hardcopy_header'; + } else { + $file_type = 'problem'; + } + + } + } + die "The file_type variable has not been defined or is blank." unless defined($file_type) and $file_type =~/\S/; + $self->{file_type} = $file_type; + + ########################################## + # File type is one of: blank_problem course_info problem set_header hardcopy_header problem_with_source + ########################################## + # + # Determine the path to the file + # + ########################################### + $self->getFilePaths($setName, $problemNumber, $file_type,$TEMPFILESUFFIX); + # result stored in $self->{editFilePath}, and $self->{tempFilePath} + ########################################## + # + # Determine action + # + ########################################### + # Submit button is one of: "add this problem to" , "add this set header to ", "Refresh" "Revert" "Save" "Save As" + $submit_button = $r->param('submit'); + SUBMIT_CASE: { + (! defined($submit_button) ) and do { # fresh problem to edit + $self->{action} = 'fresh_edit'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Add this problem to: ') and do { + $self->{action} = 'add_problem_to_set'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Make this the set header for: ') and do { + $self->{action} = 'add_set_header_to_set'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'View problem') and do { + $self->{action} ='refresh'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Revert') and do { + $self->{action} = 'revert'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Save') and do { + $self->{action} = 'save'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Save as') and do { + $self->{action} = 'save_as'; + last SUBMIT_CASE; + }; + + ($submit_button eq 'Add problem to: ') and do { + $self->{action} = 'add_problem_to_set'; + last SUBMIT_CASE; + }; + # else + die "Unrecognized submit command: |$submit_button|"; + + } # END SUBMIT_CASE + + + ########################################### + # Save file + ###################################### + + # The subroutine below writes the necessary files and obtains the appropriate seed. + # and returns + # $self->{problemPath} --- file path for viewing problem in $self->{problemPath} + # $self->{failure} + + + $self->saveFileChanges($setName, $problemNumber, $file_type,$TEMPFILESUFFIX); + + ############################################################################## + # displayMode and problemSeed + # + # Determine the display mode + # If $self->{problemSeed} was obtained within saveFileChanges from the problem_record + # then it can be overridden by the value obtained from the form. + # Insure that $self->{problemSeed} has some non-empty value + # displayMode and problemSeed + # will be needed for viewing the problem via redirect. + # They are also two of the parameters which can be set by the editor + ############################################################################## + + if (defined $r->param('displayMode')) { + $self->{displayMode} = $r->param('displayMode'); + } else { + $self->{displayMode} = $ce->{pg}->{options}->{displayMode}; + } + + # form version of problemSeed overrides version obtained from the the problem_record + # inside saveFileChanges + $self->{problemSeed} = $r->param('problemSeed') if (defined $r->param('problemSeed')); + # Make sure that the problem seed has some value + $self->{problemSeed} = '123456' unless defined $self->{problemSeed} and $self->{problemSeed} =~/\S/; + + ############################################################################## + # Return + # If file saving fails or + # if no redirects are required. No further processing takes place in this subroutine. + # Redirects are required only for the following submit values + # 'Save' + # 'Save as' + # 'Refresh' + # add problem to set + # add set header to set + # + ######################################### + + return if $self->{failure}; + # FIXME: even with an error we still open a new page because of the target specified in the form + + + # Some cases do not need a redirect revert, no fresh_edit + my $action = $self->{action}; + + return unless $action eq 'save' + or $action eq 'refresh' + or $action eq 'save_as' + or $action eq 'add_problem_to_set' + or $action eq 'add_set_header_to_set'; + + + ###################################### + # calculate redirect URL based on file type + ###################################### + my $courseName = $urlpath->arg("courseID"); + my $problemSeed = ($r->param('problemSeed')) ? $r->param('problemSeed') : ''; + my $displayMode = ($r->param('displayMode')) ? $r->param('displayMode') : ''; + + my $viewURL = ''; + + ###################################### + # problem file_type + # redirect to Problem.pm with setID = "Undefined_Set if "Save As" option is chosen + # redirect to Problem.pm with setID = current $setID if "Save" or "Revert" or "Refresh is chosen" + ###################################### + REDIRECT_CASES: { + ($file_type eq 'problem' or $file_type eq 'source_path_for_problem_file' or $file_type eq 'blank_problem') and do { + my $sourceFilePath = $self->{problemPath}; + # strip off template directory prefix + $sourceFilePath =~ s|^$ce->{courseDirs}->{templates}/||; + warn "setting sourceFilePath to $sourceFilePath"; + if ($action eq 'save_as') { # redirect to myself my $edit_level = $r->param("edit_level") || 0; $edit_level++; - $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", - courseID => $courseName, setID => 'Undefined_Set', problemID => $problemNumber); - $viewURL = $self->systemLink($problemPage, params=>{sourceFilePath => $sourceFile, edit_level=>$edit_level}); - } else { # other problems redirect to Problem.pm + courseID => $courseName, setID => 'Undefined_Set', problemID => 'Undefined_Problem' + ); + $viewURL = $self->systemLink($problemPage, + params=>{ + sourceFilePath => $sourceFilePath, + edit_level => $edit_level, + file_type => 'source_path_for_problem_file', + status_message => uri_escape($self->{status_message}) + + } + ); + + + } elsif ( $action eq 'add_problem_to_set') { + + my $targetSetName = $r->param('target_set'); my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", - courseID => $courseName, setID => $setName, problemID => $problemNumber); - $self->{currentSourceFilePath} =~ s|^$ce->{courseDirs}->{templates}/||; + courseID => $courseName, setID => $targetSetName, problemID => scalar($r->db->listGlobalProblems($targetSetName)) + ); + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + problemSeed => $problemSeed, + editMode => "savedFile", + sourceFilePath => $sourceFilePath, + status_message => uri_escape($self->{status_message}) + + } + ); + } elsif ( $action eq 'add_set_header_to_set') { + my $targetSetName = $r->param('target_set'); + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", + courseID => $courseName, setID => $targetSetName + ); + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + editMode => "savedFile", + status_message => uri_escape($self->{status_message}) + } + ); + } else { # saved problems and refreshed problems redirect to Problem.pm + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Problem", + courseID => $courseName, setID => $setName, problemID => $problemNumber + ); $viewURL = $self->systemLink($problemPage, params => { displayMode => $displayMode, problemSeed => $problemSeed, - editMode => ($submit_button eq "Save" ? "savedFile" : "temporaryFile"), - sourceFilePath => $self->{currentSourceFilePath}, - success => $self->{sucess}, - failure => $self->{failure}, + editMode => ($action eq "save" ? "savedFile" : "temporaryFile"), + sourceFilePath => $sourceFilePath, + status_message => uri_escape($self->{status_message}) + } ); } - } + last REDIRECT_CASES; + }; + ###################################### + # blank_problem file_type + # redirect to Problem.pm + ###################################### - # set headers redirect to ProblemSet.pm - $self->{file_type} eq 'set_header' and do { - my $problemSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", - courseID => $courseName, setID => $setName); - $viewURL = $self->systemLink($problemSetPage, - params => { - displayMode => $displayMode, - problemSeed => $problemSeed, - editMode => ($submit_button eq "Save" ? "savedFile" : "temporaryFile"), - } - ); + $file_type eq 'blank_problem' and do { + return; # no redirect is needed }; - # course info redirects to ProblemSets.pm - $self->{file_type} eq 'course_info' and do { + ###################################### + # set headers file_type + # redirect to ProblemSet.pm + ###################################### + + $file_type eq 'set_header' and do { + if ($action eq 'save_as') { # redirect to myself + my $sourceFilePath = $self->{problemPath}; + # strip off template directory prefix + $sourceFilePath =~ s|^$ce->{courseDirs}->{templates}/||; + + my $edit_level = $r->param("edit_level") || 0; + $edit_level++; + + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", + courseID => $courseName, setID => 'Undefined_Set', problemID => 'Undefined_Problem' + ); + $viewURL = $self->systemLink($problemPage, + params=>{ + sourceFilePath => $sourceFilePath, + edit_level => $edit_level, + file_type => 'source_path_for_problem_file', + status_message => uri_escape($self->{status_message}) + } + ); + } elsif ( $action eq 'add_set_header_to_set') { + my $targetSetName = $r->param('target_set'); + my $problemPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", + courseID => $courseName, setID => $targetSetName + ); + $viewURL = $self->systemLink($problemPage, + params => { + displayMode => $displayMode, + editMode => "savedFile", + status_message => uri_escape($self->{status_message}) + } + ); + } else { + my $problemSetPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSet", + courseID => $courseName, setID => $setName); + $viewURL = $self->systemLink($problemSetPage, + params => { + displayMode => $displayMode, + problemSeed => $problemSeed, + editMode => ($action eq "save" ? "savedFile" : "temporaryFile"), + status_message => uri_escape($self->{status_message}) + } + ); + } + last REDIRECT_CASES; + }; + ###################################### + # course_info file type + # redirect to ProblemSets.pm + ###################################### + $file_type eq 'course_info' and do { my $problemSetsPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::ProblemSets", courseID => $courseName); $viewURL = $self->systemLink($problemSetsPage, params => { - editMode => ($submit_button eq "Save" ? "savedFile" : "temporaryFile"), + editMode => ($action eq "save" ? "savedFile" : "temporaryFile"), + status_message => uri_escape($self->{status_message}) } ); + last REDIRECT_CASES; }; - - # don't redirect on bad save attempts - # FIXME: even with an error we still open a new page because of the target specified in the form - return if $self->{failure}; - - if ($viewURL) { - $self->reply_with_redirect($viewURL); - } else { - die "Invalid file_type ", $self->{file_type}, " specified by saveFileChanges"; - } + # else if no redirect needed -- there must be an error. + die "The file_type $file_type does not have a defined redirect procedure."; + } # End REDIRECT_CASES + + if ($viewURL) { + $self->reply_with_redirect($viewURL); + } else { + die "Invalid file_type $file_type specified by saveFileChanges"; } } + sub initialize { my ($self) = @_; my $r = $self->r; my $authz = $r->authz; my $user = $r->param('user'); - my $setName = $r->urlpath->arg("setID"); - my $problemNumber = $r->urlpath->arg("problemID"); - # Check permissions return unless ($authz->hasPermissions($user, "access_instructor_tools")); return unless ($authz->hasPermissions($user, "modify_problem_sets")); - - # if we got to initialize(), then saveFileChanges was not called in pre_header_initialize(). - # therefore we call it here unless there has been an error already: - $self->saveFileChanges($setName, $problemNumber) unless $self->{failure}; - # this seems like a good place to deal with the add to set - my $submit_button = $r->param('submit') || ''; - if($submit_button eq 'Add this problem to: ') { - my $ce = $r->ce; - my $sourcePath = $self->{problemPath}; - $sourcePath =~ s|^$ce->{courseDirs}->{templates}/||; - $self->addProblemToSet(setName => $r->param('target_set'), - sourceFile => $sourcePath); - $self->addgoodmessage("Added $sourcePath to ". $r->param('target_set') ); - } + + my $tempFilePath = $self->{tempFilePath}; # path to the file currently being worked with (might be a .tmp file) + my $inputFilePath = $self->{inputFilePath}; # path to the file for input, (might be a .tmp file) + my $protected_file = not -w $inputFilePath; + $self->addbadmessage("Changes in this file have not yet been permanently saved.") if -r $tempFilePath; + $self->addbadmessage("This file protected! To edit this text you must first use 'Save As' to save it to another file.") if $protected_file; + } sub path { @@ -197,11 +508,6 @@ "$problemNumber", $r->location."/$courseName/$setName/$problemNumber", "Editor", "" ); -# do { -# unshift @path, $urlpath->name, $r->location . $urlpath->path; -# } while ($urlpath = $urlpath->parent); -# -# $path[$#path] = ""; # we don't want the last path element to be a link #print "\n<!-- BEGIN " . __PACKAGE__ . "::path -->\n"; print $self->pathMacro($args, @path); @@ -228,7 +534,7 @@ my $authz = $r->authz; my $user = $r->param('user'); my $make_local_copy = $r->param('make_local_copy'); - + # Check permissions return CGI::div({class=>"ResultsWithError"}, "You are not authorized to access the Instructor tools.") unless $authz->hasPermissions($r->param("user"), "access_instructor_tools"); @@ -238,18 +544,42 @@ # Gathering info - my $editFilePath = $self->{problemPath}; # path to the permanent file to be edited - my $inputFilePath = $self->{inputFilePath}; # path to the file currently being worked with (might be a .tmp file) - - my $header = CGI::i("Editing problem: $inputFilePath"); - + my $editFilePath = $self->{editFilePath}; # path to the permanent file to be edited + my $tempFilePath = $self->{tempFilePath}; # path to the file currently being worked with (might be a .tmp file) + my $inputFilePath = $self->{inputFilePath}; # path to the file for input, (might be a .tmp file) + my $setName = $r->urlpath->arg("setID") ; + my $problemNumber = $r->urlpath->arg("problemID") ; + + ######################################################################### # Find the text for the problem, either in the tmp file, if it exists # or in the original file in the template directory + # or in the problem contents gathered in the initialization phase. ######################################################################### my $problemContents = ${$self->{r_problemContents}}; + unless ( $problemContents =~/\S/) { # non-empty contents + if (-r $tempFilePath and not -d $tempFilePath) { + eval { $problemContents = WeBWorK::Utils::readFile($tempFilePath) }; + $problemContents = $@ if $@; + $inputFilePath = $tempFilePath; + } elsif (-r $editFilePath and not -d $editFilePath) { + eval { $problemContents = WeBWorK::Utils::readFile($editFilePath) }; + $problemContents = $@ if $@; + $inputFilePath = $editFilePath; + } else { # file not existing is not an error + #warn "No file exists"; + $problemContents = ''; + } + } else { + #warn "obtaining input from r_problemContents"; + } + + my $protected_file = not -w $inputFilePath; + my $header = CGI::i("Editing problem".CGI::b("set $setName/ problem $problemNumber</emphasis>").CGI::br()." in file $inputFilePath"); + $header = ($inputFilePath =~ /$TEMPFILESUFFIX/) ? CGI::div({class=>'temporaryFile'},$header) : $header; # use colors if temporary file + ######################################################################### # Format the page ######################################################################### @@ -257,13 +587,14 @@ # Define parameters for textarea # FIXME # Should the seed be set from some particular user instance?? - my $rows = 20; - my $columns = 80; - my $mode_list = $ce->{pg}->{displayModes}; - my $displayMode = $self->{displayMode}; - my $problemSeed = $self->{problemSeed}; - my $uri = $r->uri; - my $edit_level = $r->param('edit_level') || 0; + my $rows = 20; + my $columns = 80; + my $mode_list = $ce->{pg}->{displayModes}; + my $displayMode = $self->{displayMode}; + my $problemSeed = $self->{problemSeed}; + my $uri = $r->uri; + my $edit_level = $r->param('edit_level') || 0; + my $file_type = $self->{file_type}; my $force_field = defined($r->param('sourceFilePath')) ? CGI::hidden(-name=>'sourceFilePath', @@ -274,25 +605,30 @@ $allSetNames[$j] =~ s|^set||; $allSetNames[$j] =~ s|\.def||; } - # next, the content of our "add to stuff", which only appears if we are a problem - my $add_to_stuff = ''; - if($self->{file_type} eq 'problem') { - # second form which does not open a new window - $add_to_stuff = CGI::start_form(-method=>"POST", -action=>"$uri"). + my $target = "problem$edit_level"; + # Prepare Preview button + my $view_problem_form = CGI::start_form({method=>"POST", name=>"editor", action=>"$uri", target=>$target, enctype=>"application/x-www-form-urlencoded"}). $self->hidden_authen_fields. $force_field. - CGI::hidden(-name=>'file_type',-default=>$self->{file_type}). - CGI::hidden(-name=>'problemSeed',-default=>$problemSeed). - CGI::hidden(-name=>'displayMode',-default=>$displayMode). - CGI::hidden(-name=>'problemContents',-default=>$problemContents). - CGI::p( - CGI::submit(-value=>'Add this problem to: ',-name=>'submit'), - CGI::popup_menu(-name=>'target_set',-values=>\@allSetNames) - ). + CGI::hidden(-name=>'file_type',-default=>$self->{file_type}). + CGI::hidden(-name=>'problemSeed',-default=>$problemSeed). + CGI::hidden(-name=>'displayMode',-default=>$displayMode). + CGI::hidden(-name=>'problemContents',-default=>$problemContents). + CGI::submit(-value=>'View problem',-name=>'submit'). CGI::end_form(); + # Prepare add to set buttons + my $add_files_to_set_buttons = ''; + if ($file_type eq 'problem' or $file_type eq 'source_path_for_problem_file' ) { + $add_files_to_set_buttons .= CGI::submit(-value=>'Add problem to: ',-name=>'submit' ) ; + } + if ($file_type eq 'set_header' # set header or the problem number is not a regular positive number + or ( $file_type =~ /problem/ and ($problemNumber =~ /\D|^0$|^$/ )) ){ + $add_files_to_set_buttons .=CGI::submit(-value=>'Make this the set header for: ',-name=>'submit' ); } - - my $target = "problem$edit_level"; + # Add pop-up menu for the target set if either of these buttons has been revealed. + $add_files_to_set_buttons .= CGI::popup_menu(-name=>'target_set',-values=>\@allSetNames) if $add_files_to_set_buttons; + + return CGI::p($header), CGI::start_form({method=>"POST", name=>"editor", action=>"$uri", target=>$target, enctype=>"application/x-www-form-urlencoded"}), $self->hidden_authen_fields, @@ -314,14 +650,18 @@ ), ), CGI::p( - CGI::submit(-value=>'Refresh',-name=>'submit'), - $make_local_copy ? CGI::submit(-value=>'Save',-name=>'submit', -disabled=>1) : CGI::submit(-value=>'Save',-name=>'submit'), + $add_files_to_set_buttons, + CGI::br(), + CGI::submit(-value=>'View problem',-name=>'submit'), + $protected_file ? CGI::submit(-value=>'Save',-name=>'submit', -disabled=>1) : CGI::submit(-value=>'Save',-name=>'submit'), CGI::submit(-value=>'Revert', -name=>'submit'), CGI::submit(-value=>'Save as',-name=>'submit'), CGI::textfield(-name=>'save_to_new_file', -size=>40, -value=>""), + ), - CGI::end_form(), - $add_to_stuff; + CGI::end_form(); + + } ################################################################################ @@ -334,228 +674,316 @@ # # it actually does a lot more than save changes to the file being edited, and # sometimes less. - -sub saveFileChanges { - my ($self, $setName, $problemNumber) = @_; +sub getFilePaths { + my ($self, $setName, $problemNumber, $file_type, $TEMPFILESUFFIX) = @_; my $r = $self->r; my $ce = $r->ce; my $db = $r->db; my $urlpath = $r->urlpath; - my $courseName = $urlpath->arg("courseID"); my $user = $r->param('user'); my $effectiveUserName = $r->param('effectiveUser'); - + $setName = '' unless defined $setName; $problemNumber = '' unless defined $problemNumber; - - ##### Determine path to the file to be edited. ##### - + die 'Internal error to PGProblemEditor -- file type is not defined' unless defined $file_type; + + ########################################################## + # Determine path to the input file to be edited. + # set EditFilePath to this value + # + # There are potentially four files in play + # The permanent path of the input file == $editFilePath == $self->{problemPath} + # A temporary path to the input file == $tempFilePath== "$editFilePath.$TEMPFILESUFFIX"== $self->{problemPath} + ########################################################## + # Relevant parameters + # $r->param("displayMode") + # $r->param('problemSeed') + # $r->param('submit') + # $r->param('make_local_copy') + # $r->param('sourceFilePath') + # $r->param('problemContents') + # $r->param('save_to_new_file') + ########################################################################## + # Define the following variables + # path to regular file -- $self->{problemPath} = $editFilePath; + # path to file being read (temporary or permanent) + # --- $self->{problemPath} = $problemPath; + # contents of the file being read --- $problemContents + # $self->{r_problemContents} = \$problemContents; + # $self->{TEMPFILESUFFIX} = $TEMPFILESUFFIX; + ########################################################################### + my $editFilePath = $ce->{courseDirs}->{templates}; - my $problem_record = undef; - my $file_type = $r->param("file_type") || ''; - - if ($file_type eq 'course_info') { - # we are editing the course_info file - $self->{file_type} = 'course_info'; + ########################################################################## + # Determine path to regular file, place it in $editFilePath + # problemSeed is defined for the file_type = 'problem' and 'source_path_to_problem' + ########################################################################## + CASE: + { + ($file_type eq 'course_info') and do { + # we are editing the course_info file + # value of courseFiles::course_info is relative to templates directory + $editFilePath .= '/' . $ce->{courseFiles}->{course_info}; + last CASE; + }; - # value of courseFiles::course_info is relative to templates directory - $editFilePath .= '/' . $ce->{courseFiles}->{course_info}; - } else { - # we are editing a problem file or a set header file + ($file_type eq 'blank_problem') and do { + $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{blankProblem}; + $self->addbadmessage("$editFilePath is blank problem template file and cannot be edited directly."); + $self->addbadmessage("Any changes you make will have to be saved as another file."); + last CASE; + }; - # FIXME there is a discrepancy in the way that the problems are found. - # FIXME more error checking is needed in case the problem doesn't exist. - # (i wonder what the above comments mean... -sam) - - if (defined $problemNumber) { - if ($problemNumber == 0) { - # we are editing a header file - if ($file_type eq 'set_header' or $file_type eq 'hardcopy_header') { - $self->{file_type} = $file_type - } else { - $self->{file_type} = 'set_header'; - } - - # first try getting the merged set for the effective user - my $set_record = $db->getMergedSet($effectiveUserName, $setName); # checked - - # if that doesn't work (the set is not yet assigned), get the global record - $set_record = $db->getGlobalSet($setName); # checked - - # bail if no set is found - die "Cannot find a set record for set $setName" unless defined($set_record); - - my $header_file = ""; - $header_file = $set_record->{$self->{file_type}}; - if ($header_file && $header_file ne "") { + ($file_type eq 'set_header' or $file_type eq 'hardcopy_header') and do { + # first try getting the merged set for the effective user + my $set_record = $db->getMergedSet($effectiveUserName, $setName); # checked + # if that doesn't work (the set is not yet assigned), get the global record + $set_record = $db->getGlobalSet($setName); # checked + # bail if no set is found + die "Cannot find a set record for set $setName" unless defined($set_record); + + my $header_file = ""; + $header_file = $set_record->{$file_type}; + if ($header_file && $header_file ne "") { $editFilePath .= '/' . $header_file; - } else { + } else { # if the set record doesn't specify the filename - # then the set uses the deafult from snippets + # then the set uses the default from snippets # so we'll load that file, but change where it will be saved # to and grey out the "Save" button - if ($r->param('make_local_copy')) { - $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{setHeader} if $self->{file_type} eq 'set_header'; - $editFilePath = $ce->{webworkFiles}->{hardcopySnippets}->{setHeader} if $self->{file_type} eq 'hardcopy_header'; + # FIXME why does the make_local_copy variable need to be checked? + # Isn't it automatic that a local copy has to be made? + #if ($r->param('make_local_copy')) { + $editFilePath = $ce->{webworkFiles}->{screenSnippets}->{setHeader} if $file_type eq 'set_header'; + $editFilePath = $ce->{webworkFiles}->{hardcopySnippets}->{setHeader} if $file_type eq 'hardcopy_header'; $self->addbadmessage("$editFilePath is the default header file and cannot be edited directly."); $self->addbadmessage("Any changes you make will have to be saved as another file."); - } - } - - - } else { - # we are editing a "real" problem - $self->{file_type} = 'problem'; - - # first try getting the merged problem for the effective user - $problem_record = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked - - # if that doesn't work (the problem is not yet assigned), get the global record - $problem_record = $db->getGlobalProblem($setName, $problemNumber) unless defined($problem_record); # checked - - - if(not defined($problem_record)) { - my $forcedSourceFile = $r->param('sourceFilePath'); - # bail if no problem is found and we aren't faking it - die "Cannot find a problem record for set $setName / problem $problemNumber" unless defined($forcedSourceFile); - $problem_record = fake_problem($db); - $problem_record->problem_id($problemNumber); - $problem_record->source_file($forcedSourceFile); - } - - - $editFilePath .= '/' . $problem_record->source_file; + #} } - } - } + last CASE; + }; #end 'set_header, hardcopy_header' case + + ($file_type eq 'problem') and do { + + # first try getting the merged problem for the effective user + my $problem_record = $db->getMergedProblem($effectiveUserName, $setName, $problemNumber); # checked + + # if that doesn't work (the problem is not yet assigned), get the global record + $problem_record = $db->getGlobalProblem($setName, $problemNumber) unless defined($problem_record); # checked + # bail if no source path for the problem is found ; + die "Cannot find a problem record for set $setName / problem $problemNumber" unless defined($problem_record); + $editFilePath .= '/' . $problem_record->source_file; + # define the problem seed for later use + $self->{problemSeed}= $problem_record->problem_seed if defined($problem_record) and $problem_record->can('problem_seed') ; + last CASE; + }; # end 'problem' case + + ($file_type eq 'source_path_for_problem_file') and do { + my $forcedSourceFile = $r->param('sourceFilePath'); + # bail if no source path for the problem is found ; + die "Cannot find a file path to save to" unless( defined($forcedSourceFile) and ($forcedSourceFile =~ /\S/) ); + $self->{problemSeed} = 1234; + $editFilePath .= '/' . $forcedSourceFile; + last CASE; + }; # end 'source_path_for_problem_file' case + } # end CASE: statement + # if a set record or problem record contains an empty blank for a header or problem source_file # we could find ourselves trying to edit /blah/templates/.toenail.tmp or something similar # which is almost undoubtedly NOT desirable if (-d $editFilePath) { - $self->{failure} = "The file $editFilePath is a directory!"; - $self->addbadmessage("The file $editFilePath is a directory!"); - } - - - my $editFileSuffix = $user.'.tmp'; - my $submit_button = $r->param('submit'); - - ############################################################################## - # Determine the display mode - # try to get problem seed from the input parameter, or from the problem record - # This will be needed for viewing the problem via redirect. - # They are also two of the parameters which can be set by the editor - ############################################################################## - - my $displayMode; - if (defined $r->param('displayMode')) { - $displayMode = $r->param('displayMode'); - } else { - $displayMode = $ce->{pg}->{options}->{displayMode}; + my $msg = "The file $editFilePath is a directory!"; + $self->{failure} = 1; + $self->addbadmessage($msg); } - - my $problemSeed; - if (defined $r->param('problemSeed')) { - $problemSeed = $r->param('problemSeed'); - } elsif (defined($problem_record) and $problem_record->can('problem_seed')) { - $problemSeed = $problem_record->problem_seed; + if (-e $editFilePath and not -r $editFilePath) { #it's ok if the file doesn't exist, perhaps we're going to create it + # with save as + my $msg = "The file $editFilePath cannot be read!"; + $self->{failure} = 1; + $self->addbadmessage($msg); } + ################################################# + # The path to the permanent file is now verified and stored in $editFilePath + # Whew!!! + ################################################# + + my $tempFilePath = "$editFilePath.$TEMPFILESUFFIX"; + $self->{editFilePath} = $editFilePath; + $self->{tempFilePath} = $tempFilePath; + $self->{inputFilePath} = (-r "$editFilePath.$TEMPFILESUFFIX") ? $tempFilePath : $editFilePath; + +} +sub saveFileChanges { + my ($self, $setName, $problemNumber, $file_type, $TEMPFILESUFFIX) = @_; + my $r = $self->r; + my $ce = $r->ce; + my $db = $r->db; + my $urlpath = $r->urlpath; - # make absolutely sure that the problem seed is defined, if it hasn't been. - $problemSeed = '123456' unless defined $problemSeed and $problemSeed =~/\S/; - + my $courseName = $urlpath->arg("courseID"); + my $user = $r->param('user'); + my $effectiveUserName = $r->param('effectiveUser'); + + $setName = '' unless defined $setName; + $problemNumber = '' unless defined $problemNumber; + $file_type = '' unless defined $file_type; + + my $action = $self->{action}; + my $editFilePath = $self->{editFilePath}; + my $tempFilePath = $self->{tempFilePath}; ############################################################################## # read and update the targetFile and targetFile.tmp files in the directory # if a .tmp file already exists use that, unless the revert button has been pressed. # These .tmp files are # removed when the file is finally saved. + # Place the path of the file to be read in $problemPath. ############################################################################## + my $problemContents = ''; - my $currentSourceFilePath = ''; + my $outputFilePath = undef; # this is actually the output file for this subroutine + # it is then read in as source in the body of this module + my $do_not_save = 0; # flag to prevent saving of file my $editErrors = ''; - - my $inputFilePath; - if (-r "$editFilePath.$editFileSuffix") { - $inputFilePath = "$editFilePath.$editFileSuffix"; - } else { - $inputFilePath = $editFilePath; - } - - $inputFilePath = $editFilePath if defined($submit_button) and $submit_button eq 'Revert'; - - ##### handle button clicks ##### - - if (not defined $submit_button or $submit_button eq 'Revert' ) { - # this is a fresh editing job - # copy the pg file to a new file with the same name with .tmp added - # store this name in the $self->currentSourceFilePath for use in body - - # try to read file - if(-e $inputFilePath and not -d $inputFilePath) { - eval { $problemContents = WeBWorK::Utils::readFile($inputFilePath) }; - $problemContents = $@ if $@; - } else { # file not existing is not an error - $problemContents = ''; - } + + ########################################################################## + # For each of the actions define the following variables: + # + # path to permanent file -- $self->{problemPath} = $editFilePath; + # path to file being read (temporary or permanent) + # --- $self->{problemPath} = $problemPath; + # contents of the file being read --- $problemContents + # $self->{r_problemContents} = \$problemContents; + # + ################################# + # handle button clicks ##### + # Read contents of file + ################################# + ACTION_CASES: { + ($action eq 'fresh_edit') and do { + # this is a fresh editing job + # the original file will be read in the body + last ACTION_CASES; + }; + + ($action eq 'revert') and do { + # this is also fresh editing job + $outputFilePath = undef; + $self->addgoodmessage("Reverting to original file $editFilePath"); + $self->{problemPath} = $editFilePath; + last ACTION_CASES; + }; - $currentSourceFilePath = "$editFilePath.$editFileSuffix"; - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $editFilePath; - } elsif ($submit_button eq 'Refresh') { - # grab the problemContents from the form in order to save it to the tmp file - # store tmp file name in the $self->currentSourceFilePath for use in body - $problemContents = $r->param('problemContents'); - - $currentSourceFilePath = "$editFilePath.$editFileSuffix"; - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $editFilePath; - } elsif ($submit_button eq 'Save') { - # grab the problemContents from the form in order to save it to the permanent file - # later we will unlink (delete) the temporary file - # store permanent file name in the $self->currentSourceFilePath for use in body - $problemContents = $r->param('problemContents'); - - $currentSourceFilePath = "$editFilePath"; - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $editFilePath; - } elsif ($submit_button eq 'Save as') { - # grab the problemContents from the form in order to save it to a new permanent file - # later we will unlink (delete) the current temporary file - # store new permanent file name in the $self->currentSourceFilePath for use in body - $problemContents = $r->param('problemContents'); - # Save the user in case they forgot to end the file with .pg - if($self->{file_type} eq 'problem') { - my $save_to_new_file = $r->param('save_to_new_file'); - $save_to_new_file =~ s/\.pg$//; # remove it if it is there - $save_to_new_file .= '.pg'; # put it there - $r->param('save_to_new_file', $save_to_new_file); - } - $currentSourceFilePath = $ce->{courseDirs}->{templates} . '/' . $r->param('save_to_new_file'); - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $currentSourceFilePath; - } elsif ($submit_button eq 'Add this problem to: ') { - $problemContents = $r->param('problemContents'); - $currentSourceFilePath = "$editFilePath.$editFileSuffix"; - $self->{currentSourceFilePath} = $currentSourceFilePath; - $self->{problemPath} = $editFilePath; - } else { - die "Unrecognized submit command: $submit_button"; - } + ($action eq 'refresh') and do { + # grab the problemContents from the form in order to save it to the tmp file + # store tmp file name in the $self->problemPath for use in body + + $problemContents = $r->param('problemContents'); + $outputFilePath = "$editFilePath.$TEMPFILESUFFIX"; + $self->{problemPath} = $outputFilePath; + last ACTION_CASES; + }; - # Handle the problem of line endings. Make sure that all of the line endings. Convert \r\n to \n - $problemContents =~ s/\r\n/\n/g; - $problemContents =~ s/\r/\n/g; - - # FIXME convert all double returns to paragraphs for .txt files - # instead of doing this here, it should be done n the PLACE WHERE THE FILE IS DISPLAYED!!! - #if ($self->{file_type} eq 'course_info' ) { - # $problemContents =~ s/\n\n/\n<p>\n/g; - #} + ($action eq 'save') and do { + # grab the problemContents from the form in order to save it to the permanent file + # later we will unlink (delete) the temporary file + # store permanent file name in the $self->problemPath for use in body + $problemContents = $r->param('problemContents'); + $outputFilePath = "$editFilePath"; + $self->{problemPath} = $outputFilePath; + #$self->addgoodmessage("Saving to file $outputFilePath"); + last ACTION_CASES; + }; + + ($action eq 'save_as') and do { + my $new_file_name =$r->param('save_to_new_file') || ''; + ################################################# + #bail unless this new file name has been defined + ################################################# + if ( $new_file_name !~ /\S/) { # need a non-blank file name + # setting $self->{failure} stops saving and any redirects + $do_not_save = 1; + warn "new file name is $new_file_name"; + $self->addbadmessage(CGI::p("Please specify a file to save to.")); + last ACTION_CASES; #stop processing + } + ################################################# + # grab the problemContents from the form in order to save it to a new permanent file + # later we will unlink (delete) the current temporary file + # store new permanent file name in the $self->problemPath for use in body + ################################################# + $problemContents = $r->param('problemContents'); + + ################################################# + # Rescue the user in case they forgot to end the file name with .pg + ################################################# + if($self->{file_type} eq 'problem' + or $self->{file_type} eq 'blank_problem' + or $self->{file_type} eq 'set_header') { + $new_file_name =~ s/\.pg$//; # remove it if it is there + $new_file_name .= '.pg'; # put it there + + } + + ################################################# + # check to prevent overwrites: + ################################################# + $outputFilePath = $ce->{courseDirs}->{templates} . '/' . + $new_file_name; + + if (defined $outputFilePath and -e $outputFilePath) { + # setting $do_not_save stops saving and any redirects + $do_not_save = 1; + $self->addbadmessage(CGI::p("File $outputFilePath exists. File not saved.")); + } else { + #$self->addgoodmessage("Saving to file $outputFilePath."); + } + $self->{problemPath} = $outputFilePath; + last ACTION_CASES; + }; + ($action eq 'add_problem_to_set') and do { + my $sourceFile = $editFilePath; + my $targetSetName = $r->param('target_set'); + my $freeProblemID; + $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; + my $problemRecord = $self->addProblemToSet( + setName => $targetSetName, + sourceFile => $sourceFile, + problemID => $freeProblemID + ); + $self->assignProblemToAllSetUsers($problemRecord); + $self->addgoodmessage("Added $sourceFile to ". $targetSetName. " as problem $freeProblemID") ; + $outputFilePath = undef; # don't save any files + $self->{problemPath} = $editFilePath; + + }; + ($action eq 'add_set_header_to_set') and do { + my $sourceFile = $editFilePath; + my $targetSetName = $r->param('target_set'); + $sourceFile =~ s|^$ce->{courseDirs}->{templates}/||; + my $setRecord = $db->getGlobalSet($targetSetName); + $setRecord->set_header($sourceFile); + if( $db->putGlobalSet($setRecord) ) { + $self->addgoodmessage("Added $sourceFile to ". $targetSetName. " as new set header ") ; + } else { + $do_not_save = 1 ; + $self->addbadmessage("Unable to make $sourceFile the set header for $targetSetName"); + } + # change file type to set_header if it not already so + $self->{file_type} = 'set_header'; + $outputFilePath = undef; # don't save any files + $self->{problemPath} = $editFilePath; + + }; + last ACTION_CASES; + + die "Unrecognized action command: $action"; + }; # end ACTION_CASES + ############################################################################## @@ -564,36 +992,39 @@ # Make sure that the warning is being transmitted properly. ############################################################################## - # FIXME set a local state rather continue to call on the submit button. - if (defined $submit_button and $submit_button eq 'Save as' and $r->param('save_to_new_file') !~ /\w/) { - # setting $self->{failure} stops any future redirects - $self->{failure} = "Please specify a file to save to."; - $self->addbadmessage(CGI::p("Please specify a file to save to.")); - } elsif (defined $submit_button and $submit_button eq 'Save as' and defined $currentSourceFilePath and -e $currentSourceFilePath) { - # setting $self->{failure} stops any future redirects - $self->{failure} = "File $currentSourceFilePath exists. File not saved."; - $self->addbadmessage(CGI::p("File $currentSourceFilePath exists. File not saved.")); - } else { + my $writeFileErrors; + if ( defined($outputFilePath) and $outputFilePath =~/\S/ and ! $do_not_save ) { # save file + # Handle the problem of line endings. + # Make sure that all of the line endings are of unix type. + # Convert \r\n to \n + $problemContents =~ s/\r\n/\n/g; + $problemContents =~ s/\r/\n/g; + # make sure any missing directories are created - $currentSourceFilePath = WeBWorK::Utils::surePathToFile($ce->{courseDirs}->{templates},$currentSourceFilePath); + $outputFilePath = WeBWorK::Utils::surePathToFile($ce->{courseDirs}->{templates}, + $outputFilePath); + eval { local *OUTPUTFILE; - open OUTPUTFILE, ">", $currentSourceFilePath - or die "Failed to open $currentSourceFilePath"; + open OUTPUTFILE, ">", $outputFilePath + or die "Failed to open $outputFilePath"; print OUTPUTFILE $problemContents; close OUTPUTFILE; }; # any errors are caught in the next block - } + + $writeFileErrors = $@ if $@; + } ########################################################### # Catch errors in saving files, clean up temp files ########################################################### - - my $openTempFileErrors = $@ if $@; - - if ($openTempFileErrors) { - $currentSourceFilePath =~ m|^(/.*?/)[^/]+$|; + $self->{failure} = $do_not_save; # don't do redirects if the file was not saved. + # don't unlink files or send success messages + + if ($writeFileErrors) { + # get the current directory from the outputFilePath + $outputFilePath =~ m|^(/.*?/)[^/]+$|; my $currentDirectory = $1; my $errorMessage; @@ -602,28 +1033,37 @@ $errorMessage = "Write permissions have not been enabled in the templates directory. No changes can be made."; } elsif ( not -w $currentDirectory ) { $errorMessage = "Write permissions have not been enabled in $currentDirectory. Changes must be saved to a different directory for viewing."; - } elsif ( -e $currentSourceFilePath and not -w $currentSourceFilePath ) { - $errorMessage = "Write permissions have not been enabled for $currentSourceFilePath. Changes must be saved to another file for viewing."; + } elsif ( -e $outputFilePath and not -w $outputFilePath ) { + $errorMessage = "Write permissions have not been enabled for $outputFilePath. Changes must be saved to another file for viewing."; } else { - $errorMessage = "Unable to write to $currentSourceFilePath: $openTempFileErrors"; + $errorMessage = "Unable to write to $outputFilePath: $writeFileErrors"; } - $self->{failure} = $errorMessage; + $self->{failure} = 1; $self->addbadmessage(CGI::p($errorMessage)); - } else { - $self->{success} = "Problem saved to: $currentSourceFilePath"; + } + unless( $writeFileErrors or $do_not_save) { # everything worked! unlink and announce success! # unlink the temporary file if there are no errors and the save button has been pushed - unlink("$editFilePath.$editFileSuffix") - if defined $submit_button and ($submit_button eq 'Save' or $submit_button eq 'Save as'); + if ($action eq 'save' or $action eq 'save_as' or $action eq 'revert') { + unlink($self->{tempFilePath}) ; + } + if ( defined($outputFilePath) and ! $self->{failure} ) { + my $msg = "Saved to file: $outputFilePath"; + $self->addgoodmessage($msg); + } + } # return values for use in the body subroutine - $self->{inputFilePath} = $inputFilePath; - $self->{displayMode} = $displayMode; - $self->{problemSeed} = $problemSeed; + # The path to the current permanent file being edited: + # $self->{problemPath} = $editFilePath; + # The path to the current temporary file (if any). If no temporary file this the same + # as the permanent file path: + # $self->{outputFilePath} = $outputFilePath; + # + $self->{r_problemContents} = \$problemContents; - $self->{editFileSuffix} = $editFileSuffix; -} +} # end saveFileChanges 1; |
From: Mike G. v. a. <act...@de...> - 2004-12-17 17:18:22
|
Log Message: ----------- Added a link which allows one to add a blank problem to an existing set. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor: ProblemSetDetail.pm Revision Data ------------- Index: ProblemSetDetail.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm,v retrieving revision 1.13 retrieving revision 1.14 diff -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -Llib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm -u -r1.13 -r1.14 --- lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm +++ lib/WeBWorK/ContentGenerator/Instructor/ProblemSetDetail.pm @@ -1124,7 +1124,8 @@ my $editProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID => $problemID }); my $editProblemLink = $self->systemLink($editProblemPage, params => { make_local_copy => 0 }); - + + # FIXME: should we have an "act as" type link here when editing for multiple users? my $viewProblemPage = $urlpath->new(type => 'problem_detail', args => { courseID => $courseID, setID => $setID, problemID => $problemID }); my $viewProblemLink = $self->systemLink($viewProblemPage, params => { effectiveUser => ($forOneUser ? $editForUser[0] : $userID)}); @@ -1191,6 +1192,10 @@ ])); } + my $editNewProblemPage = $urlpath->new(type => 'instructor_problem_editor_withset_withproblem', args => { courseID => $courseID, setID => $setID, problemID =>'new_problem' }); + my $editNewProblemLink = $self->systemLink($editNewProblemPage, params => { make_local_copy => 1, file_type => 'blank_problem' }); + +# print final lines print CGI::end_table(); print CGI::checkbox({ label=> "Force problems to be numbered consecutively from one", @@ -1199,6 +1204,7 @@ CGI::br(); print CGI::input({type=>"submit", name=>"submit_changes", value=>"Save Changes"}); print CGI::input({type=>"submit", name=>"handle_numbers", value=>"Reorder problems only"}) . "(Any unsaved changes will be lost.)"; + print CGI::p( CGI::a({href=>$editNewProblemLink},'Create'). 'a new blank problem'); print CGI::p(<<HERE); Any time problem numbers are intentionally changed, the problems will always be renumbered consecutively, starting from one. When deleting |
From: Mike G. v. a. <act...@de...> - 2004-12-17 17:17:13
|
Log Message: ----------- Added code to display {status_message} when call is redirected from PGProblemEditor.pm If course_info.txt does not exist as a file create it (using `echo "">course_info.txt`) Hope that's not too dangerous, but I didn't feel like opening the file :-) This keeps unnecessary warnings from occuring later when a user would be warned that the course_info.txt file didn't exist. Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator: ProblemSets.pm Revision Data ------------- Index: ProblemSets.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/ProblemSets.pm,v retrieving revision 1.57 retrieving revision 1.58 diff -Llib/WeBWorK/ContentGenerator/ProblemSets.pm -Llib/WeBWorK/ContentGenerator/ProblemSets.pm -u -r1.57 -r1.58 --- lib/WeBWorK/ContentGenerator/ProblemSets.pm +++ lib/WeBWorK/ContentGenerator/ProblemSets.pm @@ -61,8 +61,12 @@ } else { print CGI::p(CGI::b("Course Info")); } - - if (-f $course_info_path) { + unless (-e $course_info_path) { # FIXME + `echo "" >$course_info_path`; # we seem to need to have this file + # around to prevent + # spurious errors when editing it. + } + if (-f $course_info_path) { #check that it's a plain file my $text = eval { readFile($course_info_path) }; if ($@) { print CGI::div({class=>"ResultsWithError"}, @@ -83,6 +87,26 @@ $name = lc('course home') unless defined($name); $name =~ s/\s/_/g; $self->helpMacro($name); +} +sub initialize { + + + +# get result and send to message + my ($self) = @_; + my $r = $self->r; + my $authz = $r->authz; + my $urlpath = $r->urlpath; + + my $user = $r->param("user"); + my $effectiveUser = $r->param("effectiveUser"); + if ($authz->hasPermissions($user, "access_instructor_tools")) { + # get result and send to message + my $status_message = $r->param("status_message"); + $self->addmessage(CGI::p("$status_message")) if $status_message; + + + } } sub body { my ($self) = @_; |
From: Mike G. v. a. <act...@de...> - 2004-12-17 17:14:24
|
Log Message: ----------- Move options subroutine up to ContentGenerator. Cleanup commented out code Add facility for reading {status_message} to display messages when a call is redirected from PGProblemEditor.pm Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator: Problem.pm Revision Data ------------- Index: Problem.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Problem.pm,v retrieving revision 1.168 retrieving revision 1.169 diff -Llib/WeBWorK/ContentGenerator/Problem.pm -Llib/WeBWorK/ContentGenerator/Problem.pm -u -r1.168 -r1.169 --- lib/WeBWorK/ContentGenerator/Problem.pm +++ lib/WeBWorK/ContentGenerator/Problem.pm @@ -34,6 +34,7 @@ use WeBWorK::Utils qw(writeLog writeCourseLog encodeAnswers decodeAnswers ref2string makeTempDirectory); use WeBWorK::DB::Utils qw(global2user user2global findDefaults); use WeBWorK::Timing; +use URI::Escape; use WeBWorK::Utils::Tasks qw(fake_set fake_problem); @@ -274,59 +275,6 @@ . ($showSummary ? CGI::p({class=>'emphasis'},$summary) : ""); } -# sub viewOptions { -# my ($self) = @_; -# my $ce = $self->r->ce; -# -# # don't show options if we don't have anything to show -# return if $self->{invalidSet} or $self->{invalidProblem}; -# return unless $self->{isOpen}; -# -# my $displayMode = $self->{displayMode}; -# my %must = %{ $self->{must} }; -# my %can = %{ $self->{can} }; -# my %will = %{ $self->{will} }; -# -# my $optionLine; -# $can{showOldAnswers} and $optionLine .= join "", -# "Show saved answers?".CGI::br(), -# CGI::radio_group( -# -name => "showOldAnswers", -# -values => [1,0], -# -default => $will{showOldAnswers}, -# -labels => { -# 0 => 'No', -# 1 => 'Yes', -# }, -# ), .CGI::br(); -# -# $optionLine and $optionLine .= join "", CGI::br(); -# -# my %display_modes = %{WeBWorK::PG::DISPLAY_MODES()}; -# my @active_modes = grep { exists $display_modes{$_} } -# @{$ce->{pg}->{displayModes}}; -# my $modeLine = (scalar(@active_modes) > 1) ? -# "View equations as: ".CGI::br(). -# CGI::radio_group( -# -name => "displayMode", -# -values => \@active_modes, -# -default => $displayMode, -# -linebreak=>'true', -# -labels => { -# plainText => "plain", -# formattedText => "formatted", -# images => "images", -# jsMath => "jsMath", -# asciimath => "asciimath", -# }, -# ). CGI::br().CGI::hr() : ''; -# -# return CGI::div({-style=>"border: thin groove; padding: 1ex; margin: 2ex align: left"}, -# $modeLine, -# $optionLine, -# CGI::submit(-name=>"redisplay", -label=>"Apply Options"), -# ); -# } sub previewAnswer { my ($self, $answerResult, $imgGen) = @_; @@ -524,10 +472,12 @@ $self->{formFields} = $formFields; # get result and send to message - my $success = $r->param("sucess"); - my $failure = $r->param("failure"); - $self->addbadmessage(CGI::p($failure)) if $failure; - $self->addgoodmessage(CGI::p($success)) if $success; +# my $success = $r->param("success"); +# my $failure = $r->param("failure"); + my $status_message = $r->param("status_message"); +# $self->addbadmessage(CGI::p($failure)) if $failure; +# $self->addgoodmessage(CGI::p($success)) if $success; + $self->addgoodmessage(CGI::p("$status_message")) if $status_message; # now that we've set all the necessary variables quit out if the set or problem is invalid return if $self->{invalidSet} || $self->{invalidProblem}; @@ -644,27 +594,27 @@ return $self->{pg}->{head_text} if $self->{pg}->{head_text}; } -sub options { - my ($self) = @_; - - return "" if $self->{invalidProblem}; - my $sourceFilePathfield = ''; - if($self->r->param("sourceFilePath")) { - $sourceFilePathfield = CGI::hidden(-name => "sourceFilePath", - -value => $self->r->param("sourceFilePath")); - } - - return join("", - CGI::start_form("POST", $self->{r}->uri), - $self->hidden_authen_fields, - $sourceFilePathfield, - CGI::hr(), - CGI::start_div({class=>"viewOptions"}), - $self->viewOptions(), - CGI::end_div(), - CGI::end_form() - ); -} +# sub options { +# my ($self) = @_; +# warn "doing options in Problem"; +# return "" if $self->{invalidProblem}; +# my $sourceFilePathfield = ''; +# if($self->r->param("sourceFilePath")) { +# $sourceFilePathfield = CGI::hidden(-name => "sourceFilePath", +# -value => $self->r->param("sourceFilePath")); +# } +# +# return join("", +# CGI::start_form("POST", $self->{r}->uri), +# $self->hidden_authen_fields, +# $sourceFilePathfield, +# CGI::hr(), +# CGI::start_div({class=>"viewOptions"}), +# $self->viewOptions(), +# CGI::end_div(), +# CGI::end_form() +# ); +# } sub siblings { my ($self) = @_; @@ -929,17 +879,17 @@ $WeBWorK::timer->continue("end answer processing") if defined($WeBWorK::timer); ##### output ##### - - print CGI::start_div({class=>"problemHeader"}); - # custom message for editor if ($authz->hasPermissions($user, "modify_problem_sets") and defined $editMode) { if ($editMode eq "temporaryFile") { - print CGI::p(CGI::i("Editing temporary file: ", $problem->source_file)); + print CGI::p(CGI::div({class=>'temporaryFile'}, "Viewing temporary file: ", $problem->source_file)); } elsif ($editMode eq "savedFile") { # taken care of in the initialization phase } } + print CGI::start_div({class=>"problemHeader"}); + + # attempt summary #FIXME -- the following is a kludge: if showPartialCorrectAnswers is negative don't show anything. |
From: Mike G. v. a. <act...@de...> - 2004-12-17 17:12:32
|
Log Message: ----------- Preserve display options when using the "up" arrow in navigation. Some code that helps pass messages through {status_message} when a call is redirected to ProblemSet (e.g. from PGproblemEditor.pm) Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator: ProblemSet.pm Revision Data ------------- Index: ProblemSet.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/ProblemSet.pm,v retrieving revision 1.59 retrieving revision 1.60 diff -Llib/WeBWorK/ContentGenerator/ProblemSet.pm -Llib/WeBWorK/ContentGenerator/ProblemSet.pm -u -r1.59 -r1.60 --- lib/WeBWorK/ContentGenerator/ProblemSet.pm +++ lib/WeBWorK/ContentGenerator/ProblemSet.pm @@ -29,6 +29,7 @@ use CGI qw(*ul *li); use WeBWorK::PG; use WeBWorK::Timing; +use URI::Escape; use WeBWorK::Utils qw(sortByName); sub initialize { @@ -50,7 +51,11 @@ die "effective user $effectiveUserName not found. One 'acts as' the effective user." unless $effectiveUser; # FIXME: some day it would be nice to take out this code and consolidate the two checks - + + # get result and send to message + my $status_message = $r->param("status_message"); + $self->addmessage(CGI::p("$status_message")) if $status_message; + # because of the database fix, we have to split our invalidSet check into two parts # First, if $set is undefined then $setName was never valid $self->{invalidSet} = not defined $set; @@ -94,9 +99,11 @@ my $problemSetsPage = $urlpath->parent; my @links = ("Problem Sets" , $r->location . $problemSetsPage->path, "navUp"); - return $self->navMacro($args, "", @links); + my $tail = "&displayMode=".$self->{displayMode}."&showOldAnswers=".$self->{will}->{showOldAnswers}; + return $self->navMacro($args, $tail, @links); } + sub siblings { my ($self) = @_; my $r = $self->r; @@ -209,11 +216,11 @@ }, ); - if (defined($set) and $set->set_header and $authz->hasPermissions($userID, "modify_problem_sets")) { + if (defined($set) and $authz->hasPermissions($userID, "modify_problem_sets")) { #FIXME ? can't edit the default set header this way my $editorPage = $urlpath->newFromModule("WeBWorK::ContentGenerator::Instructor::PGProblemEditor", courseID => $courseID, setID => $set->set_id, problemID => 0); - my $editorURL = $self->systemLink($editorPage); + my $editorURL = $self->systemLink($editorPage, params => { file_type => 'set_header'}); print CGI::p(CGI::b("Set Info"), " ", CGI::a({href=>$editorURL}, "[edit]")); |
From: Mike G. v. a. <act...@de...> - 2004-12-17 17:09:41
|
Log Message: ----------- No need to show options on the Home.pm page Modified Files: -------------- webwork-modperl/lib/WeBWorK/ContentGenerator: Home.pm Revision Data ------------- Index: Home.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator/Home.pm,v retrieving revision 1.6 retrieving revision 1.7 diff -Llib/WeBWorK/ContentGenerator/Home.pm -Llib/WeBWorK/ContentGenerator/Home.pm -u -r1.6 -r1.7 --- lib/WeBWorK/ContentGenerator/Home.pm +++ lib/WeBWorK/ContentGenerator/Home.pm @@ -31,6 +31,7 @@ sub loginstatus { "" } sub links { "" } +sub options { "" }; sub body { my ($self) = @_; |
From: Mike G. v. a. <act...@de...> - 2004-12-17 17:07:41
|
Log Message: ----------- Make sure that the instructor links preserve the displayOptions, namely the displayMode and the ability to view the old answers. Modified Files: -------------- webwork-modperl/lib/WeBWorK: ContentGenerator.pm Revision Data ------------- Index: ContentGenerator.pm =================================================================== RCS file: /webwork/cvs/system/webwork-modperl/lib/WeBWorK/ContentGenerator.pm,v retrieving revision 1.126 retrieving revision 1.127 diff -Llib/WeBWorK/ContentGenerator.pm -Llib/WeBWorK/ContentGenerator.pm -u -r1.126 -r1.127 --- lib/WeBWorK/ContentGenerator.pm +++ lib/WeBWorK/ContentGenerator.pm @@ -295,7 +295,7 @@ =cut -# FIXME: we should probably + sub addmessage { my ($self, $message) = @_; @@ -488,28 +488,26 @@ if ($authz->hasPermissions($user, "report_bugs")) { print CGI::p(CGI::a({style=>"font-size:larger", href=>$ce->{webworkURLs}{bugReporter}}, "Report bugs")),CGI::hr(); } - + my %displayOptions = (displayMode => $self->{displayMode}, + showOldAnswers => $self->{will}->{showOldAnswers} + ); print CGI::start_ul({class=>"LinksMenu"}); print CGI::li(CGI::span({style=>"font-size:larger"}, CGI::a({href=>$self->systemLink($sets, - params=>{ displayMode => $self->{displayMode}, - showOldAnswers => $self->{will}->{showOldAnswers} - } + params=>{ %displayOptions,} )}, sp2nbsp("Homework Sets"))) ); if ($authz->hasPermissions($user, "change_password") or $authz->hasPermissions($user, "change_email_address")) { print CGI::li(CGI::a({href=>$self->systemLink($options, - params=>{ displayMode => $self->{displayMode}, - showOldAnswers => $self->{will}->{showOldAnswers} - } - )}, sp2nbsp($options->name))); + params=>{ %displayOptions,} + ) + }, sp2nbsp($options->name)) + ); } print CGI::li(CGI::a({href=>$self->systemLink($grades, - params=>{ displayMode => $self->{displayMode}, - showOldAnswers => $self->{will}->{showOldAnswers} - } + params=>{ %displayOptions,} )}, sp2nbsp($grades->name))); print CGI::li(CGI::a({href=>$self->systemLink($logout)}, sp2nbsp($logout->name))); @@ -553,49 +551,48 @@ print CGI::hr(); print CGI::start_li(); print CGI::span({style=>"font-size:larger"}, - CGI::a({href=>$self->systemLink($instr)}, space2nbsp($instr->name)) + CGI::a({href=>$self->systemLink($instr,params=>{ %displayOptions,})}, space2nbsp($instr->name)) ); print CGI::start_ul(); - #print CGI::li(CGI::a({href=>$self->systemLink($addUsers)}, sp2nbsp($addUsers->name))) if $authz->hasPermissions($user, "modify_student_data"); - print CGI::li(CGI::a({href=>$self->systemLink($userList)}, sp2nbsp($userList->name))); + print CGI::li(CGI::a({href=>$self->systemLink($userList,params=>{ %displayOptions,})}, sp2nbsp($userList->name))); print CGI::start_li(); - print CGI::a({href=>$self->systemLink($setList)}, sp2nbsp($setList->name)); + print CGI::a({href=>$self->systemLink($setList,params=>{ %displayOptions,})}, sp2nbsp($setList->name)); if (defined $setID and $setID ne "") { print CGI::start_ul(); print CGI::start_li(); - print CGI::a({href=>$self->systemLink($setDetail)}, $setID); + print CGI::a({href=>$self->systemLink($setDetail,params=>{ %displayOptions,})}, $setID); if (defined $problemID and $problemID ne "") { print CGI::ul( - CGI::li(CGI::a({href=>$self->systemLink($problemEditor)}, $problemID)) + CGI::li(CGI::a({href=>$self->systemLink($problemEditor,params=>{ %displayOptions,})}, $problemID)) ); } print CGI::end_li(); print CGI::end_ul(); } print CGI::end_li(); - print CGI::li(CGI::a({href=>$self->systemLink($maker)}, sp2nbsp($maker->name))) if $authz->hasPermissions($user, "modify_problem_sets"); - print CGI::li(CGI::a({href=>$self->systemLink($assigner)}, sp2nbsp($assigner->name))) if $authz->hasPermissions($user, "assign_problem_sets"); + print CGI::li(CGI::a({href=>$self->systemLink($maker,params=>{ %displayOptions,})}, sp2nbsp($maker->name))) if $authz->hasPermissions($user, "modify_problem_sets"); + print CGI::li(CGI::a({href=>$self->systemLink($assigner,params=>{ %displayOptions,})}, sp2nbsp($assigner->name))) if $authz->hasPermissions($user, "assign_problem_sets"); - print CGI::li(CGI::a({href=>$self->systemLink($stats)}, sp2nbsp($stats->name))); + print CGI::li(CGI::a({href=>$self->systemLink($stats,params=>{ %displayOptions,})}, sp2nbsp($stats->name))); ## Added Link for Student Progress - print CGI::li(CGI::a({href=>$self->systemLink($progress)}, sp2nbsp($progress->name))); + print CGI::li(CGI::a({href=>$self->systemLink($progress,params=>{ %displayOptions,})}, sp2nbsp($progress->name))); print CGI::start_li(); if (defined $userID and $userID ne "") { print CGI::ul( - CGI::li(CGI::a({href=>$self->systemLink($userProgress)}, $userID)) + CGI::li(CGI::a({href=>$self->systemLink($userProgress,params=>{ %displayOptions,})}, $userID)) ); } if (defined $setID and $setID ne "") { print CGI::ul( - CGI::li(CGI::a({href=>$self->systemLink($setProgress)}, space2nbsp($setID))) + CGI::li(CGI::a({href=>$self->systemLink($setProgress,params=>{ %displayOptions,})}, space2nbsp($setID))) ); } print CGI::end_li(); - print CGI::li(CGI::a({href=>$self->systemLink($scoring)}, sp2nbsp($scoring->name))) if $authz->hasPermissions($user, "score_sets"); - print CGI::li(CGI::a({href=>$self->systemLink($mail)}, sp2nbsp($mail->name))) if $authz->hasPermissions($user, "send_mail"); - print CGI::li(CGI::a({href=>$self->systemLink($fileMgr)}, sp2nbsp($fileMgr->name))); + print CGI::li(CGI::a({href=>$self->systemLink($scoring,params=>{ %displayOptions,})}, sp2nbsp($scoring->name))) if $authz->hasPermissions($user, "score_sets"); + print CGI::li(CGI::a({href=>$self->systemLink($mail,params=>{ %displayOptions,})}, sp2nbsp($mail->name))) if $authz->hasPermissions($user, "send_mail"); + print CGI::li(CGI::a({href=>$self->systemLink($fileMgr,params=>{ %displayOptions,})}, sp2nbsp($fileMgr->name))); #print CGI::li(CGI::a({href=>$self->systemLink($fileXfer)}, sp2nbsp($fileXfer->name))); print CGI::li( $self->helpMacro('instructor_links')); print CGI::end_ul(); @@ -687,7 +684,7 @@ sub options { my ($self) = @_; - + return "" if $self->{invalidProblem}; my $sourceFilePathfield = ''; if($self->r->param("sourceFilePath")) { @@ -887,6 +884,7 @@ print "\n<!-- BEGIN " . __PACKAGE__ . "::message -->\n"; print $self->{status_message} if exists $self->{status_message}; + print "<!-- END " . __PACKAGE__ . "::message -->\n"; return ""; |
From: Sam H. v. a. <act...@de...> - 2004-12-16 02:45:16
|
Log Message: ----------- ordered has_many, creation date setting. * has_explicit_order() adds a wrapper for a has_many() accessor that sorts the returned records as per a comma-separated list field. * creation dates are now set properly using a before_create trigger. * also there is the beginnings of an assign_to_participants() routine. Modified Files: -------------- webwork2/lib/WeBWorK: DBv3.pm Revision Data ------------- Index: DBv3.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/DBv3.pm,v retrieving revision 1.7 retrieving revision 1.8 diff -Llib/WeBWorK/DBv3.pm -Llib/WeBWorK/DBv3.pm -u -r1.7 -r1.8 --- lib/WeBWorK/DBv3.pm +++ lib/WeBWorK/DBv3.pm @@ -69,8 +69,10 @@ use strict; use warnings; use vars qw/$dbh $dt_format/; +use Data::Dumper; use DateTime::Format::DBI; use WeBWorK::DBv3::Utils; +use WeBWorK::Utils qw/fisher_yates_shuffle/; =head1 INITIALIZATION @@ -124,7 +126,7 @@ WeBWorK::DBv3 extends Class::DBI to provide several features useful to users to the WWDBv3 system. -=head2 TABLE LOCKING +=head2 Table locking When using a table type that doesn't support transactions, we need to be able to do table-level locks. The currently implementations are from @@ -169,7 +171,7 @@ WeBWorK::DBv3 extends Class::DBI to provide several features useful in the definition of table classes. -=head2 DATETIME SUPPORT +=head2 DateTime support The method has_a_datetime() has been defined as a shortcut to specifying that a DATETIME column should be inflated to and deflated from a DateTime.pm object. @@ -201,7 +203,7 @@ ); } -=head2 PER-COLUMN NORMALIZATION SUPPORT +=head2 Per-column normalization support WeBWorK::DBv3 adds per-column normalization support to Class::DBI via WeBWorK::DBv3::NormalizerMixin. The interface and implementation are similar to @@ -241,7 +243,7 @@ ); } -=head3 PREDEFINED NORMALIZERS +=head3 Predefined normalizers Several normalizers are conveniently predefined using a syntax similar to that of has_a() relationship declarations. @@ -264,88 +266,104 @@ =back -=head2 MACROS FOR UNIQUENESS CONSTRAINTS +=head2 set_creation_date() -has_unique_columns() allows you do define uniqueness constraints by listing the -fields that must be unique: +The subroutine set_creation_date() is available for use in setting the +C<creation_date> in newly created records using a C<before_create> trigger. - __PACKAGE__->has_unique_columns($name => qw/field1 field2 field3/); + __PACKAGE__->add_trigger(before_create => \&WeBWorK::DBv3::set_creation_date); -$name gives a name to this constraint, which is included in the error message -given when the conditions of the constraint are not met. +set_creation_date() uses the DateTime->now() constructor. -=cut +set_creation_date() should probably not be used when defining table classes or +in client code. Instead, only refer to it when adding triggers. -# FIXME this is broken -- it doesn't allow multiple NULL values! I'd rather just -# catch the DBI uniqneness violation errors and munge them in some way to get a -# useful error message out. Is there some way to do that? Would it me MySQL -# specific? -# -# <NULL> and <NULL> - OK -# <NULL, foo> and <NULL, foo> - not OK +=cut -sub has_unique_columns { - my ($class, $name, @columns) = @_; - - $class->_invalid_object_method('has_unique_columns()') if ref $class; - $name or $class->_croak("has_unique_columns needs a name"); - - foreach my $column (@columns) { - # normalize columns, and croak on any invalid columns - my $normalized_column = $class->find_column($column) - or $class->_croak("has_unique_columns: '$column' is not a valid column"); - $column = $normalized_column; - } - - # closure over @columns, $name - my $unique_columns = sub { - my ($self) = @_; - my %search_spec = map { $_ => $self->$_ } @columns; - $search_spec{id} = { '!=', $self->id }; - unless ($self->count_search_where(%search_spec) == 0) { - my $columns = join(",", @columns); - my $values = join(",", map "'$search_spec{$_}'", @columns); - my $fail = @columns == 1 ? "fails" : "fail"; - return $self->_croak("$class ($columns) $fail uniqueness constraint '$name' with ($values)"); - } - }; - - $class->add_trigger(before_create => $unique_columns); - $class->add_trigger(before_update => $unique_columns); +sub set_creation_date { + $_[0]->creation_date(DateTime->now); } -=head2 COMMA-SEPARATED LIST HANDLING +#=head2 Macros for uniqueness constraints +# +#has_unique_columns() allows you do define uniqueness constraints by listing the +#fields that must be unique: +# +# __PACKAGE__->has_unique_columns($name => qw/field1 field2 field3/); +# +#$name gives a name to this constraint, which is included in the error message +#given when the conditions of the constraint are not met. +# +#=cut +# +## FIXME this is broken -- it doesn't allow multiple NULL values! I'd rather just +## catch the DBI uniqneness violation errors and munge them in some way to get a +## useful error message out. Is there some way to do that? Would it me MySQL +## specific? +## +## <NULL> and <NULL> - OK +## <NULL, foo> and <NULL, foo> - not OK +# +#sub has_unique_columns { +# my ($class, $name, @columns) = @_; +# +# $class->_invalid_object_method('has_unique_columns()') if ref $class; +# $name or $class->_croak("has_unique_columns needs a name"); +# +# foreach my $column (@columns) { +# # normalize columns, and croak on any invalid columns +# my $normalized_column = $class->find_column($column) +# or $class->_croak("has_unique_columns: '$column' is not a valid column"); +# $column = $normalized_column; +# } +# +# # closure over @columns, $name +# my $unique_columns = sub { +# my ($self) = @_; +# my %search_spec = map { $_ => $self->$_ } @columns; +# $search_spec{id} = { '!=', $self->id }; +# unless ($self->count_search_where(%search_spec) == 0) { +# my $columns = join(",", @columns); +# my $values = join(",", map "'$search_spec{$_}'", @columns); +# my $fail = @columns == 1 ? "fails" : "fail"; +# return $self->_croak("$class ($columns) $fail uniqueness constraint '$name' with ($values)"); +# } +# }; +# +# $class->add_trigger(before_create => $unique_columns); +# $class->add_trigger(before_update => $unique_columns); +#} + +=head2 Comma-separated list handling -has_cs_list() allows you to define a column as containing a comma-separated list +has_a_list() allows you to define a column as containing a comma-separated list of values. It will add an accessor/modifier to the invocant's class with the suffix C<_list>. Note that this handling is pretty dumb, and cannot deal with embedded commas. -This is typically OK, since cs_list fields are usually used to store list of -record IDs or strings that are valid identifiers. (If you really need to store -strings with embedded commas, you may URL-encode them or whatever you like. Just -make sure you decode them on the way out.) +This is typically OK, since list fields are usually used to store list of record +IDs or strings that are valid identifiers. (If you really need to store strings +with embedded commas, you may URL-encode them or whatever you like. Just make +sure you decode them on the way out!) - __PACKAGE__->has_cs_list("problem_order"); + __PACKAGE__->has_a_list("problem_order"); # results in this method being added to __PACKAGE__ sub problem_order_list { - my ($self, @list) = @_; - if (@list) { - return $self->problem_order(join(",", @list)); - } else { - return split(",", $self->problem_order); - } + my ($self, @list) = @_; + if (@list) { + return $self->problem_order(join(",", @list)); + } else { + return split(",", $self->problem_order); + } } =cut -sub has_cs_list { +sub has_a_list { my ($class, $field) = @_; return unless $field; - my $method_name = "${class}::${field}_list"; - # closure over $field my $cs_list = sub { my ($self, @list) = @_; @@ -356,10 +374,108 @@ } }; + my $method_name = "${class}::${field}_list"; + no strict 'refs'; *$method_name = $cs_list; } +=head2 Ordered has_many() relationships + +has_ordering() allows you to impose a constant ordering on records returned by a +has_many() accessor. The ordering can be specified by a cs_list field accessor +(i.e. C<problem_order_list>). This accessor must contain a list of IDs that can +be used to order the + + __PACKAGE__->has_many(abstract_problems => "WeBWorK::DBv3::AbstractProblem"); + __PACKAGE__->has_a_list("problem_order"); + __PACKAGE__->has_explicit_order(abstract_problems => "problem_order_list"); + +The above example will add an accessor ordered_abstract_problems() to the class +that uses the method problem_order_list() to order the records returned by the +abstract_problems() accessor. + +The records reference by the has_many relationship and the cs_list field must be +kept in sync, or a warning is emitted. + +=cut + +sub has_explicit_order { + my ($class, $relation, $cs_list) = @_; + return unless $relation && $cs_list; + + #warn "$class->has_explicit_order($relation => $cs_list)\n"; + + my $cs_list_method = "${cs_list}_list"; + $class->_croak("has_explicit_order() needs a cs_list field.") + unless $class->can($cs_list_method); + + # closure over: $class, $relation, $cs_list, $cs_list_method + my $ordered = sub { + my ($self, @args) = @_; + my @order = $self->$cs_list_method; + my %data_by_id; + + my $iter; + # set up %data_by_ids to map IDs to data + if (wantarray) { + # if we're dealing with real objects, the data is objects. + my @Objs = $self->$relation; + my @ids = map { $_->id } @Objs; + @data_by_id{@ids} = @Objs; + } else { + # if we're dealing with an iterator, the data is a field inside + # Class::DBI::Iterator. messing with this data structure is probably not + # a good idea. Instead, we should probably be creating our own + # relationship type, OrderedHasMany, which would use a specified field + # to order the related records. + $iter = $self->$relation; + my @data = @{$iter->{_data}}; + my @ids = map { $_->{id} } @data; + @data_by_id{@ids} = @data; + } + + # populate @sorted_data by taking the values of %data_by_id in order by @order + my @sorted_data; + foreach my $id (@order) { + if (exists $data_by_id{$id}) { + push @sorted_data, $data_by_id{$id}; + delete $data_by_id{$id}; + } else { + my $this_class = ref $class || $class; + my $f_class = "[foreign class]"; + $self->_carp("Warning: $f_class ID '$id' exists in $cs_list," + . " but it does not correspond to an $f_class of $this_class" + . " '" . $self->name . "'. Probably $cs_list was not updated" + . " when this problem was deleted."); + } + } + if (my @unlisted_ids = keys %data_by_id) { + my $this_class = ref $class || $class; + my $f_class = "[foreign class]"; + $self->_carp("Warning: $this_class '" . $self->name . "' is referenced by" + . " $f_class that do not appear in $cs_list. Probably" + . " $cs_list was not updated when the following $f_class" + . " were added: '@unlisted_ids'."); + } + + # now dispose of the data appropriately + if (wantarray) { + # @sorted_data contains real objects, so we just return it here + return @sorted_data; + } else { + # store new order for _data field into iterator, and return iterator + $iter->{_data} = \@sorted_data; + return $iter; + } + }; + + my $method_name = "${class}::ordered_$relation"; + + no strict 'refs'; + *$method_name = $ordered; +} + ################################################################################ # Table classes: each table in the database is a subclass of WeBWorK::DBv3. # (http://devel.webwork.rochester.edu/twiki/bin/view/Webwork/DatabaseSchemaV3) @@ -386,7 +502,7 @@ __PACKAGE__->has_a(problem_version => "WeBWorK::DBv3::ProblemVersion"); __PACKAGE__->has_a_datetime("creation_date"); -# FIXME need trigger to set creation_date +__PACKAGE__->add_trigger(before_create => \&WeBWorK::DBv3::set_creation_date); ################################################################################ @@ -403,7 +519,7 @@ __PACKAGE__->has_many(problem_attempts => "WeBWorK::DBv3::ProblemAttempt"); -# FIXME need trigger to set creation_date +__PACKAGE__->add_trigger(before_create => &WeBWorK::DBv3::set_creation_date); ################################################################################ @@ -415,21 +531,11 @@ __PACKAGE__->has_a(set_assignment => "WeBWorK::DBv3::SetAssignment"); __PACKAGE__->has_a_datetime("creation_date"); +__PACKAGE__->has_a_list("problem_order"); __PACKAGE__->has_many(problem_versions => "WeBWorK::DBv3::ProblemVersion"); -# FIXME need trigger to set creation_date - -#sub problem_order_list { -# my ($self, @problem_order) = @_; -# if (@problem_order) { -# return $self->problem_order(join(",", @problem_order)); -# } else { -# return split(",", $self->problem_order); -# } -#} - -__PACKAGE__->has_cs_list("problem_order"); +__PACKAGE__->add_trigger(before_create => &WeBWorK::DBv3::set_creation_date); ################################################################################ @@ -450,9 +556,6 @@ # FIXME need to make version_due_date_offset/version_answer_date_offset # DateTime::Offset objects -__PACKAGE__->has_unique_columns('override_scope_unique_for_abstract_problem' - => qw/section recitation participant abstract_problem/); - ################################################################################ package WeBWorK::DBv3::SetOverride; @@ -469,28 +572,14 @@ __PACKAGE__->has_a(section => "WeBWorK::DBv3::Section"); __PACKAGE__->has_a(recitation => "WeBWorK::DBv3::Recitation"); __PACKAGE__->has_a(participant => "WeBWorK::DBv3::Participant"); - __PACKAGE__->has_a_datetime("open_date"); __PACKAGE__->has_a_datetime("due_date"); __PACKAGE__->has_a_datetime("answer_date"); +__PACKAGE__->has_a_list("problem_order"); # FIXME need to make version_due_date_offset/version_answer_date_offset # DateTime::Offset objects -#sub problem_order_list { -# my ($self, @problem_order) = @_; -# if (@problem_order) { -# return $self->problem_order(join(",", @problem_order)); -# } else { -# return split(",", $self->problem_order); -# } -#} - -__PACKAGE__->has_cs_list("problem_order"); - -__PACKAGE__->has_unique_columns('override_scope_unique_for_abstract_set' - => qw/section recitation participant abstract_set/); - ################################################################################ package WeBWorK::DBv3::ProblemAssignment; @@ -505,9 +594,6 @@ __PACKAGE__->has_many(problem_overrides => "WeBWorK::DBv3::ProblemOverride"); __PACKAGE__->has_many(problem_versions => "WeBWorK::DBv3::ProblemVersion"); -__PACKAGE__->has_unique_columns('set_assignment_unique_for_abstract_problem' - => qw/set_assignment abstract_problem/); - ################################################################################ package WeBWorK::DBv3::SetAssignment; @@ -518,24 +604,13 @@ __PACKAGE__->has_a(abstract_set => "WeBWorK::DBv3::AbstractSet"); __PACKAGE__->has_a(participant => "WeBWorK::DBv3::Participant"); +__PACKAGE__->has_a_list("problem_order"); __PACKAGE__->has_many(problem_assignments => "WeBWorK::DBv3::ProblemAssignment"); __PACKAGE__->has_many(set_overrides => "WeBWorK::DBv3::SetOverride"); __PACKAGE__->has_many(set_versions => "WeBWorK::DBv3::SetVersion"); -#sub problem_order_list { -# my ($self, @problem_order) = @_; -# if (@problem_order) { -# return $self->problem_order(join(",", @problem_order)); -# } else { -# return split(",", $self->problem_order); -# } -#} - -__PACKAGE__->has_cs_list("problem_order"); - -__PACKAGE__->has_unique_columns('abstract_set_unique_for_participant' - => qw/abstract_set participant/); +__PACKAGE__->has_explicit_order(problem_assignments => "problem_order"); ################################################################################ @@ -552,9 +627,6 @@ __PACKAGE__->has_many(problem_assignments => "WeBWorK::DBv3::ProblemAssignment"); -__PACKAGE__->has_unique_columns('name_unique_for_abstract_set' - => qw/name abstract_set/); - # FIXME need to make version_due_date_offset/version_answer_date_offset # DateTime::Offset objects @@ -575,25 +647,57 @@ __PACKAGE__->has_a_datetime("due_date"); __PACKAGE__->has_a_datetime("answer_date"); __PACKAGE__->has_a_boolean("published"); +__PACKAGE__->has_a_list("problem_order"); __PACKAGE__->has_many(abstract_problems => "WeBWorK::DBv3::AbstractProblem"); __PACKAGE__->has_many(set_assignments => "WeBWorK::DBv3::SetAssignment"); +__PACKAGE__->has_explicit_order(abstract_problems => "problem_order"); + # FIXME need to make version_due_date_offset/version_answer_date_offset # DateTime::Offset objects -#sub problem_order_list { -# my ($self, @problem_order) = @_; -# if (@problem_order) { -# return $self->problem_order(join(",", @problem_order)); -# } else { -# return split(",", $self->problem_order); -# } -#} - -__PACKAGE__->has_cs_list("problem_order"); - -__PACKAGE__->has_unique_columns('name_unique_for_course' => qw/name course/); +sub assign_to_participant { + my ($self, $Participant) = @_; + + my $already_assigned = count_search_where WeBWorK::DBv3::SetAssignment( + abstract_set => $self, + participant => $Participant, + ); + + my $SetAssignment = eval { + create WeBWorK::DBv3::SetAssignment({ + abstract_set => $self, + participant => $Participant, + }) + }; + $@ =~ /Duplicate entry/ and die "Abstract set '", $self->name, + "' is already assigned to participant '", $Participant->user->name, "'.\n"; + $@ and die $@; + + my @problem_order = $self->problem_order_list; + + if ($self->reorder_time eq "assignment") { + my @new_problem_order = @problem_order; + + if ($self->reorder_type eq "none") { + # leave order alone + } elsif ($self->reorder_type eq "random") { + # randomize + fisher_yates_shuffle(\@new_problem_order); + } + + if (defined $self->reorder_subset_size) { + my $range = $self->reorder_subset_size; + if ($range < @new_problem_order) { + @new_problem_order = @new_problem_order[0 .. $range-1]; + } + } + + # set problem order for assignment + $SetAssignment->problem_order_list(@new_problem_order); + } +} ################################################################################ @@ -615,8 +719,6 @@ __PACKAGE__->has_many(set_overrides => "WeBWorK::DBv3::SetOverride"); __PACKAGE__->has_many(problem_overrides => "WeBWorK::DBv3::ProblemOverride"); -__PACKAGE__->has_unique_columns('user_unique_for_course' => qw/user course/); - ################################################################################ package WeBWorK::DBv3::Recitation; @@ -631,8 +733,6 @@ __PACKAGE__->has_many(set_overrides => "WeBWorK::DBv3::SetOverride"); __PACKAGE__->has_many(problem_overrides => "WeBWorK::DBv3::ProblemOverride"); -__PACKAGE__->has_unique_columns('name_unique_for_course' => qw/name course/); - ################################################################################ package WeBWorK::DBv3::Section; @@ -647,8 +747,6 @@ __PACKAGE__->has_many(set_overrides => "WeBWorK::DBv3::SetOverride"); __PACKAGE__->has_many(problem_overrides => "WeBWorK::DBv3::ProblemOverride"); -__PACKAGE__->has_unique_columns('name_unique_for_course' => qw/name course/); - ################################################################################ package WeBWorK::DBv3::Role; @@ -658,22 +756,10 @@ __PACKAGE__->columns(All => qw/id course name privs/); __PACKAGE__->has_a(course => "WeBWorK::DBv3::Course"); +__PACKAGE__->has_a_list("privs"); __PACKAGE__->has_many(participants => "WeBWorK::DBv3::Participant"); -#sub priv_list { -# my ($self, @privs) = @_; -# if (@privs) { -# return $self->privs(join(",", @privs)); -# } else { -# return split(",", $self->privs); -# } -#} - -__PACKAGE__->has_cs_list("privs"); - -__PACKAGE__->has_unique_columns('name_unique' => qw/name/); - ################################################################################ package WeBWorK::DBv3::Status; @@ -691,8 +777,6 @@ __PACKAGE__->has_many(participants => "WeBWorK::DBv3::Participant"); -__PACKAGE__->has_unique_columns('name_unique' => qw/name/); - ################################################################################ package WeBWorK::DBv3::User; @@ -706,8 +790,9 @@ __PACKAGE__->has_many(participants => "WeBWorK::DBv3::Participant"); -__PACKAGE__->has_unique_columns('student_id_unique' => qw/student_id/); -__PACKAGE__->has_unique_columns('login_id_unique' => qw/login_id/); +sub name { + return $_[0]->first_name . " " . $_[0]->last_name; +} ################################################################################ @@ -727,8 +812,6 @@ __PACKAGE__->has_many(recitations => "WeBWorK::DBv3::Recitation"); __PACKAGE__->has_many(participants => "WeBWorK::DBv3::Participant"); __PACKAGE__->has_many(abstract_sets => "WeBWorK::DBv3::AbstractSet"); - -__PACKAGE__->has_unique_columns('name_unique' => qw/name/); ################################################################################ |
From: Sam H. v. a. <act...@de...> - 2004-12-16 02:40:26
|
Log Message: ----------- added fisher_yates_shuffle() inplace-randomization routine. Modified Files: -------------- webwork2/lib/WeBWorK: Utils.pm Revision Data ------------- Index: Utils.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/Utils.pm,v retrieving revision 1.59 retrieving revision 1.60 diff -Llib/WeBWorK/Utils.pm -Llib/WeBWorK/Utils.pm -u -r1.59 -r1.60 --- lib/WeBWorK/Utils.pm +++ lib/WeBWorK/Utils.pm @@ -74,6 +74,7 @@ cryptPassword dequote undefstr + fisher_yates_shuffle sortByName ); @@ -721,6 +722,18 @@ sub undefstr($@) { map { defined $_ ? $_ : $_[0] } @_[1..$#_]; +} + +# shuffle an array in place +# Perl Cookbook, Recipe 4.17. Randomizing an Array +sub fisher_yates_shuffle { + my $array = shift; + my $i; + for ($i = @$array; --$i; ) { + my $j = int rand ($i+1); + next if $i == $j; + @$array[$i,$j] = @$array[$j,$i]; + } } ################################################################################ |
From: Sam H. v. a. <act...@de...> - 2004-12-10 16:53:20
|
Log Message: ----------- commit test Modified Files: -------------- webwork2/lib/WeBWorK: DBv3.pm Revision Data ------------- Index: DBv3.pm =================================================================== RCS file: /webwork/cvs/system/webwork2/lib/WeBWorK/DBv3.pm,v retrieving revision 1.6 retrieving revision 1.7 diff -Llib/WeBWorK/DBv3.pm -Llib/WeBWorK/DBv3.pm -u -r1.6 -r1.7 --- lib/WeBWorK/DBv3.pm +++ lib/WeBWorK/DBv3.pm @@ -280,6 +280,9 @@ # catch the DBI uniqneness violation errors and munge them in some way to get a # useful error message out. Is there some way to do that? Would it me MySQL # specific? +# +# <NULL> and <NULL> - OK +# <NULL, foo> and <NULL, foo> - not OK sub has_unique_columns { my ($class, $name, @columns) = @_; |