Diff for /loncom/interface/lonnavmaps.pm between versions 1.223 and 1.235

version 1.223, 2003/09/03 20:27:08 version 1.235, 2003/09/29 21:07:10
Line 46  use strict; Line 46  use strict;
 use Apache::Constants qw(:common :http);  use Apache::Constants qw(:common :http);
 use Apache::loncommon();  use Apache::loncommon();
 use Apache::lonmenu();  use Apache::lonmenu();
   use Apache::lonlocal;
 use POSIX qw (floor strftime);  use POSIX qw (floor strftime);
 use Data::Dumper; # for debugging, not always used  use Data::Dumper; # for debugging, not always used
   
Line 61  my $resObj = "Apache::lonnavmaps::resour Line 62  my $resObj = "Apache::lonnavmaps::resour
 # Keep these mappings in sync with lonquickgrades, which uses the colors  # Keep these mappings in sync with lonquickgrades, which uses the colors
 # instead of the icons.  # instead of the icons.
 my %statusIconMap =   my %statusIconMap = 
     ( $resObj->NETWORK_FAILURE    => '',      (
       $resObj->NOTHING_SET        => '',       $resObj->CLOSED       => '',
       $resObj->CORRECT            => 'navmap.correct.gif',       $resObj->OPEN         => 'navmap.open.gif',
       $resObj->EXCUSED            => 'navmap.correct.gif',       $resObj->CORRECT      => 'navmap.correct.gif',
       $resObj->PAST_DUE_NO_ANSWER => 'navmap.wrong.gif',       $resObj->INCORRECT    => 'navmap.wrong.gif',
       $resObj->PAST_DUE_ANSWER_LATER => 'navmap.wrong.gif',       $resObj->ATTEMPTED    => 'navmap.ellipsis.gif',
       $resObj->ANSWER_OPEN        => 'navmap.wrong.gif',       $resObj->ERROR        => ''
       $resObj->OPEN_LATER         => '',       );
       $resObj->TRIES_LEFT         => 'navmap.open.gif',  
       $resObj->INCORRECT          => 'navmap.wrong.gif',  
       $resObj->OPEN               => 'navmap.open.gif',  
       $resObj->ATTEMPTED          => 'navmap.ellipsis.gif',  
       $resObj->ANSWER_SUBMITTED   => 'navmap.ellipsis.gif' );  
   
 my %iconAltTags =   my %iconAltTags = 
     ( 'navmap.correct.gif' => 'Correct',      ( 'navmap.correct.gif' => 'Correct',
Line 111  sub real_handler { Line 107  sub real_handler {
     # Handle header-only request      # Handle header-only request
     if ($r->header_only) {      if ($r->header_only) {
         if ($ENV{'browser.mathml'}) {          if ($ENV{'browser.mathml'}) {
             $r->content_type('text/xml');              &Apache::loncommon::content_type($r,'text/xml');
         } else {          } else {
             $r->content_type('text/html');              &Apache::loncommon::content_type($r,'text/html');
         }          }
         $r->send_http_header;          $r->send_http_header;
         return OK;          return OK;
Line 121  sub real_handler { Line 117  sub real_handler {
   
     # Send header, don't cache this page      # Send header, don't cache this page
     if ($ENV{'browser.mathml'}) {      if ($ENV{'browser.mathml'}) {
         $r->content_type('text/xml');          &Apache::loncommon::content_type($r,'text/xml');
     } else {      } else {
         $r->content_type('text/html');          &Apache::loncommon::content_type($r,'text/html');
     }      }
     &Apache::loncommon::no_cache($r);      &Apache::loncommon::no_cache($r);
     $r->send_http_header;      $r->send_http_header;
Line 139  sub real_handler { Line 135  sub real_handler {
     }      }
   
     $r->print("<html><head>\n");      $r->print("<html><head>\n");
     $r->print("<title>Navigate Course Contents</title>");      $r->print("<title>".&mt('Navigate Course Contents')."</title>");
 # ------------------------------------------------------------ Get query string  # ------------------------------------------------------------ Get query string
     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register']);      &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['register']);
           
Line 226  sub real_handler { Line 222  sub real_handler {
         }          }
     } else {      } else {
         $r->print("<a href='navmaps?jumpToFirstHomework'>" .          $r->print("<a href='navmaps?jumpToFirstHomework'>" .
                   "Go To My First Homework Problem</a>&nbsp;&nbsp;&nbsp;&nbsp;");         &mt("Go To My First Homework Problem")."</a>&nbsp;&nbsp;&nbsp;&nbsp;");
     }      }
   
     my $suppressEmptySequences = 0;      my $suppressEmptySequences = 0;
Line 241  sub real_handler { Line 237  sub real_handler {
         $filterFunc = sub { my $res = shift;           $filterFunc = sub { my $res = shift; 
                             return $res->completable() || $res->is_map();                              return $res->completable() || $res->is_map();
                         };                          };
         $r->print("<p><font size='+2'>Uncompleted Homework</font></p>");          $r->print("<p><font size='+2'>".&mt("Uncompleted Homework")."</font></p>");
         $ENV{'form.filter'} = '';          $ENV{'form.filter'} = '';
         $ENV{'form.condition'} = 1;          $ENV{'form.condition'} = 1;
  $resource_no_folder_link = 1;   $resource_no_folder_link = 1;
     } else {      } else {
         $r->print("<a href='navmaps?showOnlyHomework'>" .          $r->print("<a href='navmaps?showOnlyHomework'>" .
                   "Show Only Uncompleted Homework</a>&nbsp;&nbsp;&nbsp;&nbsp;");         &mt("Show Only Uncompleted Homework")."</a>&nbsp;&nbsp;&nbsp;&nbsp;");
     }      }
   
     # renderer call      # renderer call
Line 266  sub real_handler { Line 262  sub real_handler {
     # user knows there was no error.      # user knows there was no error.
     if ($renderArgs->{'counter'} == 0) {      if ($renderArgs->{'counter'} == 0) {
         if ($showOnlyHomework) {          if ($showOnlyHomework) {
             $r->print("<p><font size='+1'>All homework is currently completed.</font></p>");              $r->print("<p><font size='+1'>".&mt("All homework is currently completed").".</font></p>");
         } else { # both jumpToFirstHomework and normal use the same: course must be empty          } else { # both jumpToFirstHomework and normal use the same: course must be empty
             $r->print("<p><font size='+1'>This course is empty.</font></p>");              $r->print("<p><font size='+1'>This course is empty.</font></p>");
         }          }
Line 336  sub getDescription { Line 332  sub getDescription {
     my $status = $res->status($part);      my $status = $res->status($part);
   
     if ($status == $res->NETWORK_FAILURE) {       if ($status == $res->NETWORK_FAILURE) { 
         return "Having technical difficulties; please check status later";           return &mt("Having technical difficulties; please check status later"); 
     }      }
     if ($status == $res->NOTHING_SET) {      if ($status == $res->NOTHING_SET) {
         return "Not currently assigned.";          return &mt("Not currently assigned.");
     }      }
     if ($status == $res->OPEN_LATER) {      if ($status == $res->OPEN_LATER) {
         return "Open " . timeToHumanString($res->opendate($part));          return "Open " . timeToHumanString($res->opendate($part));
     }      }
     if ($status == $res->OPEN) {      if ($status == $res->OPEN) {
         if ($res->duedate($part)) {          if ($res->duedate($part)) {
             return "Due " . timeToHumanString($res->duedate($part));              return &mt("Due")."  " .timeToHumanString($res->duedate($part));
         } else {          } else {
             return "Open, no due date";              return &mt("Open, no due date");
         }          }
     }      }
     if ($status == $res->PAST_DUE_ANSWER_LATER) {      if ($status == $res->PAST_DUE_ANSWER_LATER) {
         return "Answer open " . timeToHumanString($res->answerdate($part));          return &mt("Answer open")." " . timeToHumanString($res->answerdate($part));
     }      }
     if ($status == $res->PAST_DUE_NO_ANSWER) {      if ($status == $res->PAST_DUE_NO_ANSWER) {
         return "Was due " . timeToHumanString($res->duedate($part));          return &mt("Was due")." " . timeToHumanString($res->duedate($part));
     }      }
     if ($status == $res->ANSWER_OPEN) {      if ($status == $res->ANSWER_OPEN) {
         return "Answer available";          return &mt("Answer available");
     }      }
     if ($status == $res->EXCUSED) {      if ($status == $res->EXCUSED) {
         return "Excused by instructor";          return &mt("Excused by instructor");
     }      }
     if ($status == $res->ATTEMPTED) {      if ($status == $res->ATTEMPTED) {
         return "Answer submitted, not yet graded.";          return &mt("Answer submitted, not yet graded");
     }      }
     if ($status == $res->TRIES_LEFT) {      if ($status == $res->TRIES_LEFT) {
         my $tries = $res->tries($part);          my $tries = $res->tries($part);
Line 377  sub getDescription { Line 373  sub getDescription {
             }              }
         }          }
         if ($res->duedate()) {          if ($res->duedate()) {
             return "Due " . timeToHumanString($res->duedate($part)) .              return &mt("Due")." " . timeToHumanString($res->duedate($part)) .
                 " $triesString";                  " $triesString";
         } else {          } else {
             return "No due date $triesString";              return &mt("No due date")." $triesString";
         }          }
     }      }
     if ($status == $res->ANSWER_SUBMITTED) {      if ($status == $res->ANSWER_SUBMITTED) {
         return 'Answer submitted';          return &mt('Answer submitted');
     }      }
 }  }
   
Line 434  sub timeToHumanString { Line 430  sub timeToHumanString {
     my ($time) = @_;      my ($time) = @_;
     # zero, '0' and blank are bad times      # zero, '0' and blank are bad times
     if (!$time) {      if (!$time) {
         return 'never';          return &mt('never');
     }      }
       unless (&Apache::lonlocal::current_language()=~/^en/) {
    return localtime($time);
       } 
     my $now = time();      my $now = time();
   
     my @time = localtime($time);      my @time = localtime($time);
Line 660  can't close or open folders when this is Line 658  can't close or open folders when this is
   
 =back  =back
   
 =item B<Apache::lonnavmaps::communication_status>:  =item * B<Apache::lonnavmaps::communication_status>:
   
 Whether there is discussion on the resource, email for the user, or  Whether there is discussion on the resource, email for the user, or
 (lumped in here) perl errors in the execution of the problem. This is  (lumped in here) perl errors in the execution of the problem. This is
 the second column in the main nav map.  the second column in the main nav map.
   
 =item B<Apache::lonnavmaps::quick_status>:  =item * B<Apache::lonnavmaps::quick_status>:
   
 An icon for the status of a problem, with five possible states:  An icon for the status of a problem, with five possible states:
 Correct, incorrect, open, awaiting grading (for a problem where the  Correct, incorrect, open, awaiting grading (for a problem where the
Line 674  computer's grade is suppressed, or the c Line 672  computer's grade is suppressed, or the c
 essay problem), or none (not open yet, not a problem). The  essay problem), or none (not open yet, not a problem). The
 third column of the standard navmap.  third column of the standard navmap.
   
 =item B<Apache::lonnavmaps::long_status>:  =item * B<Apache::lonnavmaps::long_status>:
   
 A text readout of the details of the current status of the problem,  A text readout of the details of the current status of the problem,
 such as "Due in 22 hours". The fourth column of the standard navmap.  such as "Due in 22 hours". The fourth column of the standard navmap.
   
   =item * B<Apache::lonnavmaps::part_status_summary>:
   
   A text readout summarizing the status of the problem. If it is a
   single part problem, will display "Correct", "Incorrect", 
   "Not yet open", "Open", "Attempted", or "Error". If there are
   multiple parts, this will output a string that in HTML will show a
   status of how many parts are in each status, in color coding, trying
   to match the colors of the icons within reason.
   
   Note this only makes sense if you are I<not> showing parts. If 
   C<showParts> is true (see below), this column will not output
   anything. 
   
 =back  =back
   
 If you add any others please be sure to document them here.  If you add any others please be sure to document them here.
Line 846  sub resource { return 0; } Line 857  sub resource { return 0; }
 sub communication_status { return 1; }  sub communication_status { return 1; }
 sub quick_status { return 2; }  sub quick_status { return 2; }
 sub long_status { return 3; }  sub long_status { return 3; }
   sub part_status_summary { return 4; }
 # Data for render_resource  
   
 sub render_resource {  sub render_resource {
     my ($resource, $part, $params) = @_;      my ($resource, $part, $params) = @_;
Line 1036  sub render_quick_status { Line 1046  sub render_quick_status {
   
     if ($resource->is_problem() &&      if ($resource->is_problem() &&
         !$firstDisplayed) {          !$firstDisplayed) {
         my $icon = $statusIconMap{$resource->status($part)};  
           my $icon = $statusIconMap{$resource->simpleStatus($part)};
         my $alt = $iconAltTags{$icon};          my $alt = $iconAltTags{$icon};
         if ($icon) {          if ($icon) {
             $result .= "<td width='30' valign='center' width='50' align='right'>$linkopen<img width='25' height='25' src='/adm/lonIcons/$icon' border='0' alt='$alt' />$linkclose</td>\n";              $result .= "<td width='30' valign='center' width='50' align='right'>$linkopen<img width='25' height='25' src='/adm/lonIcons/$icon' border='0' alt='$alt' />$linkclose</td>\n";
Line 1085  sub render_long_status { Line 1096  sub render_long_status {
     return $result;      return $result;
 }  }
   
   # Colors obtained by taking the icons, matching the colors, and
   # possibly reducing the Value (HSV) of the color, if it's too bright
   # for text, generally by one third or so.
   my %statusColors = 
       (
        $resObj->CLOSED => '#000000',
        $resObj->OPEN   => '#998b13',
        $resObj->CORRECT => '#26933f',
        $resObj->INCORRECT => '#c48207',
        $resObj->ATTEMPTED => '#a87510',
        $resObj->ERROR => '#000000'
        );
   my %statusStrings = 
       (
        $resObj->CLOSED => 'Not yet open',
        $resObj->OPEN   => 'Open',
        $resObj->CORRECT => 'Correct',
        $resObj->INCORRECT => 'Incorrect',
        $resObj->ATTEMPTED => 'Attempted',
        $resObj->ERROR => 'Network Error'
        );
   my @statuses = ($resObj->CORRECT, $resObj->ATTEMPTED, $resObj->INCORRECT, $resObj->OPEN, $resObj->CLOSED, $resObj->ERROR);
   
   use Data::Dumper;
   sub render_parts_summary_status {
       my ($resource, $part, $params) = @_;
       if (!$resource->is_problem()) { return '<td></td>'; }
       if ($params->{showParts}) { 
    return '<td></td>';
       }
   
       my $td = "<td align='right'>\n";
       my $endtd = "</td>\n";
   
       # If there is a single part, just show the simple status
       if ($resource->singlepart()) {
    my $status = $resource->simpleStatus('0');
    return $td . "<font color='" . $statusColors{$status} . "'>"
       . $statusStrings{$status} . "</font>" . $endtd;
       }
   
       # Now we can be sure the $part doesn't really matter.
       my $statusCount = $resource->simpleStatusCount();
       my @counts;
       foreach my $status(@statuses) {
    # decouple display order from the simpleStatusCount order
    my $slot = Apache::lonnavmaps::resource::statusToSlot($status);
    if ($statusCount->[$slot]) {
       push @counts, "<font color='" . $statusColors{$status} .
    "'>" . $statusCount->[$slot] . ' '
    . $statusStrings{$status} . "</font>";
    }
       }
   
       return $td . $resource->countParts() . ' parts: ' . join (', ', @counts) . $endtd;
   }
   
 my @preparedColumns = (\&render_resource, \&render_communication_status,  my @preparedColumns = (\&render_resource, \&render_communication_status,
                        \&render_quick_status, \&render_long_status);                         \&render_quick_status, \&render_long_status,
          \&render_parts_summary_status);
   
 sub setDefault {  sub setDefault {
     my ($val, $default) = @_;      my ($val, $default) = @_;
Line 1262  sub render { Line 1331  sub render {
         $result.='<tr><td align="right" valign="bottom">Key:&nbsp;&nbsp;</td>';          $result.='<tr><td align="right" valign="bottom">Key:&nbsp;&nbsp;</td>';
         if ($navmap->{LAST_CHECK}) {          if ($navmap->{LAST_CHECK}) {
             $result .=               $result .= 
                 '<img src="/adm/lonMisc/chat.gif"> New discussion since '.                  '<img src="/adm/lonMisc/chat.gif"> '.&mt('New discussion since').' '.
                 strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})).                  strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})).
                 '</td><td align="center" valign="bottom">&nbsp;&nbsp;'.                  '</td><td align="center" valign="bottom">&nbsp;&nbsp;'.
                 '<img src="/adm/lonMisc/feedback.gif"> New message (click to open)<p>'.                  '<img src="/adm/lonMisc/feedback.gif"> '.&mt('New message (click to open)').'<p>'.
                 '</td>';                   '</td>'; 
         } else {          } else {
             $result .= '<td align="center" valign="bottom">&nbsp;&nbsp;'.              $result .= '<td align="center" valign="bottom">&nbsp;&nbsp;'.
                 '<img src="/adm/lonMisc/chat.gif"> Discussions</td><td align="center" valign="bottom">'.                  '<img src="/adm/lonMisc/chat.gif"> '.&mt('Discussions').'</td><td align="center" valign="bottom">'.
                 '&nbsp;&nbsp;<img src="/adm/lonMisc/feedback.gif"> New message (click to open)'.                  '&nbsp;&nbsp;<img src="/adm/lonMisc/feedback.gif"> '.&mt('New message (click to open)').
                 '</td>';                   '</td>'; 
         }          }
   
Line 1281  sub render { Line 1350  sub render {
         if ($condition) {          if ($condition) {
             $result.="<a href=\"navmaps?condition=0&filter=&$queryString" .              $result.="<a href=\"navmaps?condition=0&filter=&$queryString" .
                 "&here=" . Apache::lonnet::escape($here) .                  "&here=" . Apache::lonnet::escape($here) .
                 "\">Close All Folders</a>";                  "\">".&mt('Close All Folders')."</a>";
         } else {          } else {
             $result.="<a href=\"navmaps?condition=1&filter=&$queryString" .              $result.="<a href=\"navmaps?condition=1&filter=&$queryString" .
                 "&here=" . Apache::lonnet::escape($here) .                   "&here=" . Apache::lonnet::escape($here) . 
                 "\">Open All Folders</a>";                  "\">".&mt('Open All Folders')."</a>";
         }          }
         $result .= "<br /><br />\n";          $result .= "<br /><br />\n";
     }          }    
Line 1909  sub getById { Line 1978  sub getById {
 sub getBySymb {  sub getBySymb {
     my $self = shift;      my $self = shift;
     my $symb = shift;      my $symb = shift;
     my ($mapUrl, $id, $filename) = split (/___/, $symb);      my ($mapUrl, $id, $filename) = &Apache::lonnet::decode_symb($symb);
     my $map = $self->getResourceByUrl($mapUrl);      my $map = $self->getResourceByUrl($mapUrl);
     return $self->getById($map->map_pc() . '.' . $id);      return $self->getById($map->map_pc() . '.' . $id);
 }  }
Line 1985  sub parmval_real { Line 2054  sub parmval_real {
     unless ($symb) { return ''; }      unless ($symb) { return ''; }
     my $result='';      my $result='';
   
     my ($mapname,$id,$fn)=split(/\_\_\_/,$symb);      my ($mapname,$id,$fn)=&Apache::lonnet::decode_symb($symb);
   
 # ----------------------------------------------------- Cascading lookup scheme  # ----------------------------------------------------- Cascading lookup scheme
     my $rwhat=$what;      my $rwhat=$what;
Line 3468  sub multipart { Line 3537  sub multipart {
     return $self->countParts() > 1;      return $self->countParts() > 1;
 }  }
   
   sub singlepart {
       my $self = shift;
       return $self->countParts() == 1;
   }
   
 sub responseType {  sub responseType {
     my $self = shift;      my $self = shift;
     my $part = shift;      my $part = shift;
   
     $self->extractParts();      $self->extractParts();
     return $self->{RESPONSE_TYPES}->{$part};      if (defined($self->{RESPONSE_TYPES}->{$part})) {
    return @{$self->{RESPONSE_TYPES}->{$part}};
       } else {
    return undef;
       }
 }  }
   
 sub responseIds {  sub responseIds {
Line 3481  sub responseIds { Line 3559  sub responseIds {
     my $part = shift;      my $part = shift;
   
     $self->extractParts();      $self->extractParts();
     return $self->{RESPONSE_IDS}->{$part};      if (defined($self->{RESPONSE_IDS}->{$part})) {
    return @{$self->{RESPONSE_IDS}->{$part}};
       } else {
    return undef;
       }
 }  }
   
 # Private function: Extracts the parts information, both part names and  # Private function: Extracts the parts information, both part names and
Line 3555  sub extractParts { Line 3637  sub extractParts {
                         my @otherChunks = @partChunks[$i+1..$#partChunks];                          my @otherChunks = @partChunks[$i+1..$#partChunks];
                         my $responseId = join('_', @otherChunks);                          my $responseId = join('_', @otherChunks);
                         push @{$responseIdHash{$partIdSoFar}}, $responseId;                          push @{$responseIdHash{$partIdSoFar}}, $responseId;
                         $responseTypeHash{$partIdSoFar} = $responseType;                          push @{$responseTypeHash{$partIdSoFar}}, $responseType;
                         last;  
                     }                      }
                 }                  }
             }              }
Line 3927  sub status { Line 4008  sub status {
     return OPEN;       return OPEN; 
 }  }
   
   sub CLOSED { return 23; }
   sub ERROR { return 24; }
   
   =pod
   
   B<Simple Status>
   
   Convenience method B<simpleStatus> provides a "simple status" for the resource.
   "Simple status" corresponds to "which icon is shown on the
   Navmaps". There are six "simple" statuses:
   
   =over 4
   
   =item * B<CLOSED>: The problem is currently closed. (No icon shown.)
   
   =item * B<OPEN>: The problem is open and unattempted.
   
   =item * B<CORRECT>: The problem is correct for any reason.
   
   =item * B<INCORRECT>: The problem is incorrect and can still be
   completed successfully.
   
   =item * B<ATTEMPTED>: The problem has been attempted, but the student
   does not know if they are correct. (The ellipsis icon.)
   
   =item * B<ERROR>: There is an error retrieving information about this
   problem.
   
   =back
   
   =cut
   
   # This hash maps the composite status to this simple status, and
   # can be used directly, if you like
   my %compositeToSimple = 
       (
         NETWORK_FAILURE()       => ERROR,
         NOTHING_SET()           => CLOSED,
         CORRECT()               => CORRECT,
         EXCUSED()               => CORRECT,
         PAST_DUE_NO_ANSWER()    => INCORRECT,
         PAST_DUE_ANSWER_LATER() => INCORRECT,
         ANSWER_OPEN()           => INCORRECT,
         OPEN_LATER()            => CLOSED,
         TRIES_LEFT()            => OPEN,
         INCORRECT()             => INCORRECT,
         OPEN()                  => OPEN,
         ATTEMPTED()             => ATTEMPTED,
         ANSWER_SUBMITTED()      => ATTEMPTED
        );
   
   sub simpleStatus {
       my $self = shift;
       my $part = shift;
       my $status = $self->status($part);
       return $compositeToSimple{$status};
   }
   
   =pod
   
   B<simpleStatusCount> will return an array reference containing, in
   this order, the number of OPEN, CLOSED, CORRECT, INCORRECT, ATTEMPTED,
   and ERROR parts the given problem has.
   
   =cut
       
   # This maps the status to the slot we want to increment
   my %statusToSlotMap = 
       (
        OPEN()      => 0,
        CLOSED()    => 1,
        CORRECT()   => 2,
        INCORRECT() => 3,
        ATTEMPTED() => 4,
        ERROR()     => 5
        );
   
   sub statusToSlot { return $statusToSlotMap{shift()}; }
   
   sub simpleStatusCount {
       my $self = shift;
   
       my @counts = (0, 0, 0, 0, 0, 0, 0);
       foreach my $part (@{$self->parts()}) {
    $counts[$statusToSlotMap{$self->simpleStatus($part)}]++;
       }
   
       return \@counts;
   }
   
 =pod  =pod
   
 B<Completable>  B<Completable>

Removed from v.1.223  
changed lines
  Added in v.1.235


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>