--- loncom/interface/lonnavmaps.pm 2004/08/10 20:52:03 1.274
+++ loncom/interface/lonnavmaps.pm 2004/11/02 21:02:01 1.302
@@ -1,7 +1,7 @@
# The LearningOnline Network with CAPA
# Navigate Maps Handler
#
-# $Id: lonnavmaps.pm,v 1.274 2004/08/10 20:52:03 matthew Exp $
+# $Id: lonnavmaps.pm,v 1.302 2004/11/02 21:02:01 albertel Exp $
#
# Copyright Michigan State University Board of Trustees
#
@@ -35,7 +35,7 @@ use Apache::loncommon();
use Apache::lonmenu();
use Apache::lonlocal;
use POSIX qw (floor strftime);
-use Data::Dumper; # for debugging, not always used
+use Data::Dumper; # for debugging, not always
# symbolic constants
sub SYMB { return 1; }
@@ -84,7 +84,7 @@ my %colormap =
my $hurryUpColor = "#FF0000";
sub launch_win {
- my ($mode,$script)=@_;
+ my ($mode,$script,$toplinkitems)=@_;
my $result;
if ($script ne 'no') {
$result.='';
}
if ($mode eq 'link') {
- $result.=''
- .&mt("Launch navigation in separate window")." ";
+ &add_linkitem($toplinkitems,'launchnav','launch_navmapwin()',
+ "Launch navigation window");
}
return $result;
}
@@ -161,16 +161,36 @@ sub real_handler {
&Apache::loncommon::no_cache($r);
$r->send_http_header;
+ my %toplinkitems=();
+
if ($ENV{QUERY_STRING} eq 'collapseExternal') {
&Apache::lonnet::put('environment',{'remotenavmap' => 'off'});
&Apache::lonnet::appenv('environment.remotenavmap' => 'off');
+ my $menu=&Apache::lonmenu::reopenmenu();
+ my $navstatus=&Apache::lonmenu::get_nav_status();
+ if ($menu) {
+ $menu=(<
");
# renderer call
- my $renderArgs = { 'cols' => [0,2,3],
+ my $renderArgs = { 'cols' => [0,1,2,3],
'sort' => $ENV{'form.sort'},
'url' => '/adm/navmaps',
'navmap' => $navmap,
@@ -359,9 +388,11 @@ ENDSUBM
'suppressEmptySequences' => $suppressEmptySequences,
'filterFunc' => $filterFunc,
'resource_no_folder_link' => $resource_no_folder_link,
- 'r' => $r};
+ 'sort_html'=> $sort_html,
+ 'r' => $r,
+ 'caller' => 'navmapsdisplay',
+ 'linkitems' => \%toplinkitems};
my $render = render($renderArgs);
- $navmap->untieHashes();
# If no resources were printed, print a reassuring message so the
# user knows there was no error.
@@ -631,7 +662,7 @@ sub timeToHumanString {
}
# Not this year, so show the year
- my $timeStr = strftime("on %A, %b %e %G at %I:%M %P", localtime($time));
+ my $timeStr = strftime("on %A, %b %e %Y at %I:%M %P", localtime($time));
$timeStr =~ s/12:00 am/00:00/;
$timeStr =~ s/12:00 pm/noon/;
return $timeStr;
@@ -986,10 +1017,10 @@ sub render_resource {
my $partLabel = "";
my $newBranchText = "";
-
+ my $location=&Apache::loncommon::lonhttpdurl("/adm/lonIcons");
# If this is a new branch, label it so
if ($params->{'isNewBranch'}) {
- $newBranchText = "";
+ $newBranchText = "";
}
# links to open and close the folder
@@ -997,17 +1028,16 @@ sub render_resource {
my $linkclose = "";
# Default icon: unknown page
- my $icon = "";
+ my $icon = "";
if ($resource->is_problem()) {
if ($part eq '0' || $params->{'condensed'}) {
- $icon = '';
+ $icon ='';
} else {
$icon = $params->{'indentString'};
}
} else {
- $icon = "";
+ $icon = "";
}
# Display the correct map icon to open or shut map
@@ -1022,7 +1052,7 @@ sub render_resource {
if (!$params->{'resource_no_folder_link'}) {
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') . '.gif';
- $icon = "";
+ $icon = "";
$linkopen = "{'queryString'} . '&filter=';
@@ -1039,7 +1069,7 @@ sub render_resource {
# Don't allow users to manipulate folder
$icon = "navmap.$folderType." . ($nowOpen ? 'closed' : 'open') .
'.nomanip.gif';
- $icon = "";
+ $icon = "";
$linkopen = "";
$linkclose = "";
@@ -1107,10 +1137,10 @@ sub render_communication_status {
my $link = $params->{"resourceLink"};
my $linkopen = "";
my $linkclose = "";
-
+ my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc");
if ($resource->hasDiscussion()) {
$discussionHTML = $linkopen .
- '' .
+ '' .
$linkclose;
}
@@ -1120,7 +1150,7 @@ sub render_communication_status {
if ($_) {
$feedbackHTML .= ' '
- . '';
}
}
@@ -1135,7 +1165,7 @@ sub render_communication_status {
$errorcount++;
$errorHTML .= ' '
- . '';
}
}
@@ -1164,7 +1194,9 @@ sub render_quick_status {
my $icon = $statusIconMap{$resource->simpleStatus($part)};
my $alt = $iconAltTags{$icon};
if ($icon) {
- $result .= "
$linkopen$linkclose
\n";
+ my $location=
+ &Apache::loncommon::lonhttpdurl("/adm/lonIcons/$icon");
+ $result .= "
$linkopen$linkclose
\n";
} else {
$result .= "
\n";
}
@@ -1295,11 +1327,17 @@ sub setDefault {
return $val;
}
+sub cmp_title {
+ my ($atitle,$btitle) = (lc($_[0]->compTitle),lc($_[1]->compTitle));
+ $atitle=~s/^\s*//;
+ $btitle=~s/^\s*//;
+ return $atitle cmp $btitle;
+}
+
sub render {
my $args = shift;
&Apache::loncommon::get_unprocessed_cgi($ENV{QUERY_STRING});
my $result = '';
-
# Configure the renderer.
my $cols = $args->{'cols'};
if (!defined($cols)) {
@@ -1370,10 +1408,7 @@ sub render {
#$currenturl=~s/^[^\/]+//;
$here = $jump = &Apache::lonnet::symbread($currenturl);
- } else {
- &Apache::lonnet::logthis("Hrrm,");
-
- }
+ }
# Step three: Ensure the folders are open
my $mapIterator = $navmap->getIterator(undef, undef, undef, 1);
@@ -1383,7 +1418,7 @@ sub render {
# We only need to do this if we need to open the maps to show the
# current position. This will change the counter so we can't count
# for the jump marker with this loop.
- while (($curRes = $mapIterator->next()) && !$found) {
+ while ($here && ($curRes = $mapIterator->next()) && !$found) {
if (ref($curRes) && $curRes->symb() eq $here) {
my $mapStack = $mapIterator->getStack();
@@ -1459,23 +1494,24 @@ sub render {
my $printKey = $args->{'printKey'};
my $printCloseAll = $args->{'printCloseAll'};
if (!defined($printCloseAll)) { $printCloseAll = 1; }
-
+
# Print key?
if ($printKey) {
$result .= '
';
my $date=localtime;
$result.='
Key:
';
+ my $location=&Apache::loncommon::lonhttpdurl("/adm/lonMisc");
if ($navmap->{LAST_CHECK}) {
$result .=
- ' '.&mt('New discussion since').' '.
+ ' '.&mt('New discussion since').' '.
strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})).
'
';
+ if ($ENV{'environment.remotenavmap'} ne 'on') {
+ $result .= '
';
} else {
- $result .= ' ';
+ $result .= '
';
+ }
+ $result.=&show_linkitems($args->{'linkitems'});
+ if ($args->{'sort_html'}) {
+ if ($ENV{'environment.remotenavmap'} ne 'on') {
+ $result.='
'.
+ '
'.$args->{'sort_html'}.'
';
+ } else {
+ $result.='
'.
+ $args->{'sort_html'}.'
';
+ }
}
+ $result .= '
';
+ } elsif ($args->{'sort_html'}) {
+ $result.=$args->{'sort_html'};
}
+
$result .= " \n";
if ($r) {
$r->print($result);
@@ -1550,7 +1621,9 @@ END
$args->{'indentLevel'} = 0;
$args->{'isNewBranch'} = 0;
$args->{'condensed'} = 0;
- $args->{'indentString'} = setDefault($args->{'indentString'}, "");
+ my $location=
+ &Apache::loncommon::lonhttpdurl("/adm/lonIcons/whitespace1.gif");
+ $args->{'indentString'} = setDefault($args->{'indentString'}, "");
$args->{'displayedHereMarker'} = 0;
# If we're suppressing empty sequences, look for them here. Use DFS for speed,
@@ -1603,22 +1676,49 @@ END
my @resources;
my $code='';# sub { !(shift->is_map();) };
if ($args->{'sort'} eq 'title') {
- @resources=$navmap->retrieveResources(undef,
- sub { !shift->is_map(); });
- @resources= sort {lc($a->compTitle) cmp lc($b->compTitle)} @resources;
+ my $oldFilterFunc = $filterFunc;
+ my $filterFunc=
+ sub {
+ my ($res)=@_;
+ if ($res->is_map()) { return 0;}
+ return &$oldFilterFunc($res);
+ };
+ @resources=$navmap->retrieveResources(undef,$filterFunc);
+ @resources= sort { &cmp_title($a,$b) } @resources;
} elsif ($args->{'sort'} eq 'duedate') {
- @resources=$navmap->retrieveResources(undef,
- sub { shift->is_problem(); });
- @resources= sort
- {
+ my $oldFilterFunc = $filterFunc;
+ my $filterFunc=
+ sub {
+ my ($res)=@_;
+ if (!$res->is_problem()) { return 0;}
+ return &$oldFilterFunc($res);
+ };
+ @resources=$navmap->retrieveResources(undef,$filterFunc);
+ @resources= sort {
if ($a->duedate ne $b->duedate) {
return $a->duedate cmp $b->duedate;
- } else {
- lc($a->compTitle) cmp lc($b->compTitle)
}
+ my $value=&cmp_title($a,$b);
+ return $value;
} @resources;
+ } elsif ($args->{'sort'} eq 'discussion') {
+ my $oldFilterFunc = $filterFunc;
+ my $filterFunc=
+ sub {
+ my ($res)=@_;
+ if (!$res->hasDiscussion() &&
+ !$res->getFeedback() &&
+ !$res->getErrors()) { return 0;}
+ return &$oldFilterFunc($res);
+ };
+ @resources=$navmap->retrieveResources(undef,$filterFunc);
+ @resources= sort { &cmp_title($a,$b) } @resources;
+ } else {
+ #unknow sort mechanism or default
+ undef($args->{'sort'});
}
+
while (1) {
if ($args->{'sort'}) {
$curRes = shift(@resources);
@@ -1822,8 +1922,46 @@ if (location.href.indexOf('#curloc')==-1
$r->rflush();
}
- if ($mustCloseNavMap) { $navmap->untieHashes(); }
+ return $result;
+}
+
+sub add_linkitem {
+ my ($linkitems,$name,$cmd,$text)=@_;
+ $$linkitems{$name}{'cmd'}=$cmd;
+ $$linkitems{$name}{'text'}=&mt($text);
+}
+sub show_linkitems {
+ my ($linkitems)=@_;
+ my @linkorder = ("launchnav","closenav","firsthomework","everything",
+ "uncompleted","changefolder","clearbubbles");
+
+ my $result .= (<
+
+ '."\n";
+
return $result;
}
@@ -1884,10 +2022,6 @@ successful, or B if not.
=back
-When you are done with the $navmap object, you I call
-$navmap->untieHashes(), or you'll prevent the current user from using that
-course until the web server is restarted. (!)
-
=head2 Methods
=over 4
@@ -2033,17 +2167,11 @@ sub generate_email_discuss_status {
foreach my $msgid (split(/\&/, $keys)) {
$msgid=&Apache::lonnet::unescape($msgid);
- my $plain=&Apache::lonnet::unescape(&Apache::lonnet::unescape($msgid));
- if ($plain=~/(Error|Feedback) \[([^\]]+)\]/) {
- my ($what,$url)=($1,$2);
- my %status=
- &Apache::lonnet::get('email_status',[$msgid]);
- if ($status{$msgid}=~/^error\:/) {
- $status{$msgid}='';
- }
-
- if (($status{$msgid} eq 'new') ||
- (!$status{$msgid})) {
+ if ((!$emailstatus{$msgid}) || ($emailstatus{$msgid} eq 'new')) {
+ my $plain=
+ &Apache::lonnet::unescape(&Apache::lonnet::unescape($msgid));
+ if ($plain=~/(Error|Feedback) \[([^\]]+)\]/) {
+ my ($what,$url)=($1,$2);
if ($what eq 'Error') {
$error{$url}.=','.$msgid;
} else {
@@ -2053,8 +2181,10 @@ sub generate_email_discuss_status {
}
}
+ #url's of resources that have feedbacks
$self->{FEEDBACK} = \%feedback;
- $self->{ERROR_MSG} = \%error; # what is this? JB
+ #or errors
+ $self->{ERROR_MSG} = \%error;
$self->{DISCUSSION_TIME} = \%discussiontime;
$self->{EMAIL_STATUS} = \%emailstatus;
$self->{LAST_READ} = \%lastreadtime;
@@ -2108,13 +2238,6 @@ sub getIterator {
return $iterator;
}
-# unties the hash when done
-sub untieHashes {
- my $self = shift;
- untie %{$self->{NAV_HASH}};
- untie %{$self->{PARM_HASH}};
-}
-
# Private method: Does the given resource (as a symb string) have
# current discussion? Returns 0 if chat/mail data not extracted.
sub hasDiscussion {
@@ -2211,9 +2334,14 @@ sub getById {
sub getBySymb {
my $self = shift;
my $symb = shift;
+
my ($mapUrl, $id, $filename) = &Apache::lonnet::decode_symb($symb);
my $map = $self->getResourceByUrl($mapUrl);
- return $self->getById($map->map_pc() . '.' . $id);
+ my $returnvalue = undef;
+ if (ref($map)) {
+ $returnvalue = $self->getById($map->map_pc() .'.'.$id);
+ }
+ return $returnvalue;
}
sub getByMapPc {
@@ -2407,6 +2535,7 @@ in the filter function.
=cut
+
sub getResourceByUrl {
my $self = shift;
my $resUrl = shift;
@@ -2489,7 +2618,7 @@ sub hasResource {
1;
package Apache::lonnavmaps::iterator;
-
+use WeakRef;
=pod
=back
@@ -2629,7 +2758,7 @@ sub new {
my $class = ref($proto) || $proto;
my $self = {};
- $self->{NAV_MAP} = shift;
+ weaken($self->{NAV_MAP} = shift);
return undef unless ($self->{NAV_MAP});
# Handle the parameters
@@ -2965,7 +3094,7 @@ sub populateStack {
1;
package Apache::lonnavmaps::DFSiterator;
-
+use WeakRef;
# Not documented in the perldoc: This is a simple iterator that just walks
# through the nav map and presents the resources in a depth-first search
# fashion, ignorant of conditionals, randomized resources, etc. It presents
@@ -2993,7 +3122,7 @@ sub new {
my $class = ref($proto) || $proto;
my $self = {};
- $self->{NAV_MAP} = shift;
+ weaken($self->{NAV_MAP} = shift);
return undef unless ($self->{NAV_MAP});
$self->{FIRST_RESOURCE} = shift || $self->{NAV_MAP}->firstResource();
@@ -3147,7 +3276,7 @@ sub populateStack {
1;
package Apache::lonnavmaps::resource;
-
+use WeakRef;
use Apache::lonnet;
=pod
@@ -3229,7 +3358,7 @@ sub new {
my $class = ref($proto) || $proto;
my $self = {};
- $self->{NAV_MAP} = shift;
+ weaken($self->{NAV_MAP} = shift);
$self->{ID} = shift;
# Store this new resource in the parent nav map's cache.
@@ -3350,6 +3479,16 @@ sub title {
return $self->navHash("title_", 1); }
# considered private and undocumented
sub to { my $self=shift; return $self->navHash("to_", 1); }
+sub condition {
+ my $self=shift;
+ my $undercond=$self->navHash("undercond_", 1);
+ if (!defined($undercond)) { return 1; };
+ my $condid=$self->navHash("condid_$undercond");
+ if (!defined($condid)) { return 1; };
+ my $condition=&Apache::lonnet::directcondval($condid);
+ return $condition;
+}
+
sub compTitle {
my $self = shift;
my $title = $self->title();
@@ -3771,6 +3910,16 @@ Returns the number of parts of the probl
for single part problems, returns 1. For multipart, it returns the
number of parts in the problem, not including psuedo-part 0.
+=item * B():
+
+Returns the total number of responses in the problem a student can answer.
+
+=item * B():
+
+Returns a hash whose keys are the response types. The values are the number
+of times each response type is used. This is for the I problem, not
+just a single part.
+
=item * B():
Returns true if the problem is multipart, false otherwise. Use this instead
@@ -3817,6 +3966,26 @@ sub countParts {
return scalar(@{$parts}); # + $delta;
}
+sub countResponses {
+ my $self = shift;
+ my $count;
+ foreach my $part (@{$self->parts()}) {
+ $count+= scalar($self->responseIds($part));
+ }
+ return $count;
+}
+
+sub responseTypes {
+ my $self = shift;
+ my %responses;
+ foreach my $part ($self->parts()) {
+ foreach my $responsetype ($self->responseType($part)) {
+ $responses{$responsetype}++ if (defined($responsetype));
+ }
+ }
+ return %responses;
+}
+
sub multipart {
my $self = shift;
return $self->countParts() > 1;
@@ -3904,6 +4073,7 @@ sub extractParts {
}
+ # These hashes probably do not need names that end with "Hash"....
my %responseIdHash;
my %responseTypeHash;
@@ -3939,17 +4109,27 @@ sub extractParts {
}
}
my $resorder = &Apache::lonnet::metadata($self->src(),'responseorder');
+ #
+ # Reorder the arrays in the %responseIdHash and %responseTypeHash
if ($resorder) {
my @resorder=split(/,/,$resorder);
foreach my $part (keys(%responseIdHash)) {
- my %resids = map { ($_,1) } @{ $responseIdHash{$part} };
+ my $i=0;
+ my %resids = map { ($_,$i++) } @{ $responseIdHash{$part} };
my @neworder;
foreach my $possibleid (@resorder) {
if (exists($resids{$possibleid})) {
- push(@neworder,$possibleid);
+ push(@neworder,$resids{$possibleid});
}
}
- $responseIdHash{$part}=\@neworder;
+ my @ids;
+ my @type;
+ foreach my $element (@neworder) {
+ push (@ids,$responseIdHash{$part}->[$element]);
+ push (@type,$responseTypeHash{$part}->[$element]);
+ }
+ $responseIdHash{$part}=\@ids;
+ $responseTypeHash{$part}=\@type;
}
}
$self->{RESPONSE_IDS} = \%responseIdHash;
@@ -4145,6 +4325,7 @@ sub getCompletionStatus {
# Left as separate if statements in case we ever do more with this
if ($status eq 'correct_by_student') {return $self->CORRECT;}
+ if ($status eq 'correct_by_scantron') {return $self->CORRECT;}
if ($status eq 'correct_by_override') {return $self->CORRECT_BY_OVERRIDE; }
if ($status eq 'incorrect_attempted') {return $self->INCORRECT; }
if ($status eq 'incorrect_by_override') {return $self->INCORRECT_BY_OVERRIDE; }
@@ -4481,6 +4662,7 @@ sub getNext {
my $to = $self->to();
foreach my $branch ( split(/,/, $to) ) {
my $choice = $self->{NAV_MAP}->getById($branch);
+ if (!$choice->condition()) { next; }
my $next = $choice->goesto();
$next = $self->{NAV_MAP}->getById($next);