[system] / branches / gage_dev / webwork2 / lib / WeBWorK / ContentGenerator / ProblemSet.pm Repository:
ViewVC logotype

View of /branches/gage_dev/webwork2/lib/WeBWorK/ContentGenerator/ProblemSet.pm

Parent Directory Parent Directory | Revision Log Revision Log


Revision 6543 - (download) (as text) (annotate)
Sun Nov 21 00:18:35 2010 UTC (2 years, 6 months ago) by gage
File size: 16061 byte(s)
updates merged from Trunk


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

aubreyja at gmail dot com
ViewVC Help
Powered by ViewVC 1.0.9