--- loncom/interface/lonwhatsnew.pm 2005/04/07 03:58:02 1.2 +++ loncom/interface/lonwhatsnew.pm 2005/04/29 14:54:18 1.12 @@ -1,5 +1,5 @@ # -# $Id: lonwhatsnew.pm,v 1.2 2005/04/07 03:58:02 albertel Exp $ +# $Id: lonwhatsnew.pm,v 1.12 2005/04/29 14:54:18 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -30,11 +30,11 @@ package Apache::lonwhatsnew; use strict; use lib qw(/home/httpd/lib/perl); use Apache::lonnet; -use Apache::loncommon; -use Apache::lonhtmlcommon; +use Apache::loncommon(); +use Apache::lonhtmlcommon(); use Apache::lonlocal; -use Apache::loncoursedata; -use Apache::lonnavmaps; +use Apache::loncoursedata(); +use Apache::lonnavmaps(); use Apache::Constants qw(:common :http); use Time::Local; @@ -45,21 +45,42 @@ use Time::Local; sub handler { my $r = shift; + if ($r->header_only) { + &Apache::loncommon::content_type($r,'text/html'); + $r->send_http_header; + return OK; + } &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['command']); - my $command = $ENV{'form.command'}; + my $command = $env{'form.command'}; if ($command eq '') { $command = "info"; } + &Apache::loncommon::content_type($r,'text/html'); + $r->send_http_header; $r->print(&display_header()); - if (! (($ENV{'request.course.fn'}) && (&Apache::lonnet::allowed('vsa',$ENV{'request.course.id'})))) { + if (! (($env{'request.course.fn'}) && (&Apache::lonnet::allowed('vsa',$env{'request.course.id'})))) { # Not in a course, or not allowed to modify parms - $ENV{'user.error.msg'}="/adm/whatsnew:vsa:0:0:Cannot display student activity"; + $env{'user.error.msg'}="/adm/whatsnew:vsa:0:0:Cannot display student activity"; return HTTP_NOT_ACCEPTABLE; } + &Apache::lonhtmlcommon::clear_breadcrumbs(); + if ($command eq 'config') { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>'/adm/whatsnew?command=config', + text=>"Configure display"}); + $r->print(&Apache::lonhtmlcommon::breadcrumbs + (undef,'Course Action Items','Course_Action_Items_Config')); + } else { + &Apache::lonhtmlcommon::add_breadcrumb + ({href=>'/adm/whatsnew?command=info', + text=>"Display Action Items"}); + $r->print(&Apache::lonhtmlcommon::breadcrumbs + (undef,'Course Action Items','Course_Action_Items_Display')); + } &display_main_box($r,$command); } @@ -73,63 +94,32 @@ sub display_main_box { my ($r,$command) = @_; my $domain=&Apache::loncommon::determinedomain(); my $tabbg=&Apache::loncommon::designparm('coordinator.tabbg',$domain); - $r->print(< -
- - -
- - - - -
- - - - - - '); - $r->print(' - -
- - - - -
- - - - - - -
- Course Action Items  -
-
-
- - -
-END_OF_BLOCK - &display_nav_box($r,$command); - $r->print('
- -
'); - + my $selconfig; + my $selinfo; + if ($command eq 'config') { + $selinfo = 'selected="selected"'; + } else { + $selconfig = 'selected="selected"'; + } + my $picker = (' +
+ + + + +
'); + + $r->print(' -
'); + if ($command eq 'config') { - &display_config_box($r); + &display_config_box($r,$picker); } else { - &display_actions_box($r); + &display_actions_box($r,$picker); } $r->print(< -
-
-
-

@@ -138,37 +128,6 @@ END_OF_BLOCK END_OF_BLOCK } -#------------------------------ -# display_nav_box -# -# Display the navigation box -#------------------------------ - -sub display_nav_box { - my ($r,$command) = @_; - $r->print(''."\n"); - if ($command eq "info") { - $r->print(''); - } else { - $r->print(''); - } - $r->print(''); - if ($command eq "config") { - $r->print(''); - } else { - $r->print(''); - } - $r->print('
'); - $r->print('Action Items
'); - $r->print('
'); - $r->print('Current Action Items
'); - $r->print('
 
'); - $r->print('Display options
'); - $r->print('
'); - $r->print('Display options
'); - $r->print('
'); -} - #------------------------------- # display_header # @@ -177,9 +136,10 @@ sub display_nav_box { #------------------------------- sub display_header{ + my $html=&Apache::lonxml::xmlbegin(); my $bodytag=&Apache::loncommon::bodytag('Course Action Items'); return(< +$html Course Action Items @@ -195,7 +155,7 @@ ENDHEAD #------------------------------- sub display_actions_box() { - my $r = shift; + my ($r,$picker) = @_; my $rowColor1 = "#ffffff"; my $rowColor2 = "#eeeeee"; @@ -204,33 +164,138 @@ sub display_actions_box() { my %unread = (); my %ungraded = (); my %bombed = (); + my %triggered = (); my @newmsgs = (); my @critmsgs = (); my @newdiscussions = (); my @tograde = (); my @bombs = (); + my @warnings = (); my $domain=&Apache::loncommon::determinedomain(); my $function; - if ($ENV{'request.role'}=~/^(cc|in|ta|ep)/) { + if ($env{'request.role'}=~/^(cc|in|ta|ep)/) { $function='coordinator'; } - if ($ENV{'request.role'}=~/^(su|dc|ad|li)/) { + if ($env{'request.role'}=~/^(su|dc|ad|li)/) { $function='admin'; } my $pgbg=&Apache::loncommon::designparm($function.'.pgbg',$domain); my $tabbg=&Apache::loncommon::designparm($function.'.tabbg',$domain); - &getitems(\%unread,\%ungraded,\%bombed,\@newdiscussions,\@tograde,\@bombs); + &getitems(\%unread,\%ungraded,\%bombed,\%triggered,\@newdiscussions,\@tograde,\@bombs,\@warnings,$rowColor1,$rowColor2); my ($msgcount,$critmsgcount) = &getmail(\@newmsgs,\@critmsgs); - unless ($ENV{'request.course.id'}) { + unless ($env{'request.course.id'}) { $r->print('
You are accessing an invalid course


'); return; } - $r->print('Course Action Items

'); + $r->print(''.$picker.'

'); + +## UNGRADED ITEMS ## + $r->print(< +
+ + + + +
Problems requiring handgrading
+ +END + + if (@tograde > 0) { + $r->print(''); + my $rowNum = 0; + foreach my $res (@tograde) { + if ($rowNum %2 == 1) { + $rowColor = $rowColor1; + } else { + $rowColor = $rowColor2; + } + my ($map,$id,$url)=&Apache::lonnet::decode_symb($res); + my $linkurl=&Apache::lonnet::clutter($url); + $linkurl .= '?symb='.&Apache::lonnet::escape($res); + + $r->print(''); + $rowNum ++; + } + } else { + $r->print(''); + } + $r->print('
Problem NameNumber ungraded
'.$ungraded{$res}{title}.''.$ungraded{$res}{count}.'

  No problems require handgrading  


'); + +## BOMBS ## + $r->print(<<"END"); + + +
+ + + + + +
Problems with errors
+ +END + my $bombnum = 0; + if (@bombs > 0) { +# @bombs = sort { &cmp_title($a,$b) } @bombs; + foreach my $bomb (@bombs) { + if ($bombnum %2 == 1) { + $rowColor = $rowColor1; + } else { + $rowColor = $rowColor2; + } + $r->print(''); + $bombnum ++; + } + } else { + $r->print(''); + } + $r->print('
'.$bombed{$bomb}{errorlink}.'

No problems with errors


'); + +# DEGDIFF AND AV. TRIES TRIGGERS + $r->print(<<"END"); + + +
+ + + + + +
Problems with average attempts > 0 or degree of difficulty > 0
+ +END + my $warningnum = 0; + if (@warnings > 0) { +# @warnings = sort { &cmp_title($a,$b) } @warnings; + $r->print(''); + foreach my $res (@warnings) { + if ($warningnum %2 == 1) { + $rowColor = $rowColor1; + } else { + $rowColor = $rowColor2; + } + my ($map,$id,$url)=&Apache::lonnet::decode_symb($res); + my $linkurl=&Apache::lonnet::clutter($url); + my $rowspan; + if ($triggered{$res}{numparts} > 1) { + $rowspan = 'rowspan="'.$triggered{$res}{numparts}.'"'; + } + $linkurl .= '?symb='.&Apache::lonnet::escape($res); + $r->print(''.$triggered{$res}{text}); + $warningnum ++; + } + } else { + $r->print(''); + } + $r->print('
ResourcePartNum. studentsAv. AttemptsDeg. Diff
'.$triggered{$res}{title}.'

No problems with av. attempts or degree of difficulty above thresholds


'); + + $r->print('
 '); ## UNREAD COURSE DISCUSSION POSTS ## $r->print(<<"END"); @@ -238,25 +303,25 @@ sub display_actions_box() {
- +
Unread course discussion posts:Unread course discussion posts
END - + if (@newdiscussions > 0) { + $r->print(''); # @newdiscussions = sort { &cmp_title($a,$b) } @newdiscussions; my $rowNum = 0; foreach my $ressymb (@newdiscussions) { my $forum_title = $unread{$ressymb}{'title'}; - my ($map,$ind,$url)=&Apache::lonnet::decode_symb($ressymb); - my $feedurl = &Apache::lonnet::clutter($url); -# backward compatibility (bulletin boards used to be 'wrapped') - if ($feedurl=~m|^/adm/wrapper/adm/.*/bulletinboard$|) { - $feedurl=~s|^/adm/wrapper||; + my $type = 'Resource'; + my $feedurl=&Apache::lonfeedback::get_feedurl($ressymb); + if ($feedurl =~ /bulletinboard/) { + $type = 'Bulletin Board'; } - my $unreadnum = keys %{$unread{$ressymb}}; + my $unreadnum = keys(%{$unread{$ressymb}}); $unreadnum = $unreadnum - 2; if ($unreadnum > 0) { if ($rowNum %2 == 1) { @@ -264,7 +329,7 @@ END } else { $rowColor = $rowColor2; } - $r->print(''); + $r->print(''); $rowNum ++; } } @@ -273,37 +338,6 @@ END } $r->print('
LocationTypeNumber of new posts
'.$forum_title.': '.$unreadnum.' 
'.$forum_title.' '.$type.''.$unreadnum.' 

'); -## UNGRADED ITEMS ## - $r->print(< - - - - - -
Problems requiring handgrading:
- -END - - if (@tograde > 0) { - $r->print(''); - my $rowNum = 0; - foreach my $res (@tograde) { - if ($rowNum %2 == 1) { - $rowColor = $rowColor1; - } else { - $rowColor = $rowColor2; - } - - $r->print(''); - $rowNum ++; - } - } else { - $r->print(''); - } - $r->print('
Problem NameNumber ungraded
'.$ungraded{$res}{title}.''.$ungraded{$res}{count}.'

  No problems require handgrading  


'); - $r->print(' '); - ## MESSAGES ## $r->print(< @@ -318,6 +352,7 @@ END END if ($msgcount > 0) { + $r->print(''); my $rowNum = 0; my $mailcount = 1; foreach my $msg (@newmsgs) { @@ -326,7 +361,7 @@ END } else { $rowColor = $rowColor2; } - $r->print(''); + $r->print(''); $rowNum ++; $mailcount ++; } @@ -349,6 +384,7 @@ END END if ($critmsgcount > 0) { + $r->print(''); my $rowNum = 0; my $mailcount = 1; foreach my $msg (@critmsgs) { @@ -357,7 +393,7 @@ END } else { $rowColor = $rowColor2; } - $r->print(''); + $r->print(''); $rowNum ++; $mailcount ++; } @@ -367,35 +403,6 @@ END $r->print('
'.&mt('Number').''.&mt('Subject').''.&mt('Sender').''.&mt('Date/Time').'
'.$mailcount.'.  '.$msg->{'shortsub'}.'    '.$msg->{'from'}.'@'.$msg->{'fromdom'}.' '.$msg->{'sendtime'}.'
'.$mailcount.'.  '.$msg->{'shortsub'}.'    '.$msg->{'from'}.'@'.$msg->{'fromdom'}.' '.$msg->{'sendtime'}.'
NumberSubjectSenderDate/Time
'.$mailcount.'.  '.$msg->{'shortsub'}.'    '.$msg->{'from'}.'@'.$msg->{'fromdom'}.' '.$msg->{'sendtime'}.'
'.$mailcount.'.  '.$msg->{'shortsub'}.'    '.$msg->{'from'}.'@'.$msg->{'fromdom'}.' '.$msg->{'sendtime'}.'

'); -## BOMBS ## - $r->print(< - - - - - - - -
Problems with errors
- -END - my $bombnum = 0; - if (@bombs > 0) { -# @bombs = sort { &cmp_title($a,$b) } @bombs; - foreach my $bomb (@bombs) { - if ($bombnum %2 == 1) { - $rowColor = $rowColor1; - } else { - $rowColor = $rowColor2; - } - $r->print(''); - $bombnum ++; - } - } else { - $r->print(''); - } - $r->print('
'.$bombed{$bomb}{errorlink}.'

No problems with errors

'); $r->print(' @@ -404,22 +411,41 @@ END $r->print(''); } +#------------------------------- +# display_config_box +# +# Display the action items +# +#------------------------------- + +sub display_config_box() { + my ($r,$picker) = @_; + $r->print(''.$picker.'

'."\n"; + } + if (@parts > 1) { + $$triggered{$symb}{text} .= ' + '; + } else { + $$triggered{$symb}{text} .= ' + '; + } + $$triggered{$symb}{text} .= ' + + + + '; + $$triggered{$symb}{numparts} ++; + } + } + push(@{$warnings},$symb); + $warningnum ++; + } } -# Compile maxtries and degree of difficulty. } sub getmail { my ($newmsgs,$critmsgs) = @_; # Check for unread mail in course my $msgcount = 0; - my @msgids = sort split(/\&/,&Apache::lonnet::reply - ('keys:'.$ENV{'user.domain'}.':'. - $ENV{'user.name'}.':nohist_email', - $ENV{'user.home'})); - foreach my $msgid (@msgids) { - my ($sendtime,$shortsubj,$fromname,$fromdom,$fromcid,$status)= + + my @messages = sort(&Apache::lonnet::getkeys('nohist_email')); + foreach my $message (@messages) { + my $msgid=&Apache::lonnet::escape($message); + my ($sendtime,$shortsubj,$fromname,$fromdom,$status,$fromcid)= &Apache::lonmsg::unpackmsgid($msgid); - if ($fromcid eq $ENV{'request.course.id'}) { + if (($fromcid) && ($fromcid eq $env{'request.course.id'})) { if (defined($sendtime) && $sendtime!~/error/) { my $numsendtime = $sendtime; $sendtime = &Apache::lonlocal::locallocaltime($sendtime); if ($status eq 'new') { - $$msgcount ++; + $msgcount ++; + if ($shortsubj eq '') { + $shortsubj = &mt('No subject'); + } + $shortsubj = &Apache::lonnet::unescape($shortsubj); push(@{$newmsgs}, { msgid => $msgid, sendtime => $sendtime, - shortsub => &Apache::lonnet::unescape($shortsubj), + shortsub => $shortsubj, from => $fromname, fromdom => $fromdom }); @@ -539,18 +623,22 @@ sub getmail { my %what=&Apache::lonnet::dump('critical'); my $result = ''; my $critmsgcount = 0; - foreach my $msgid (sort keys %what) { - my ($sendtime,$shortsubj,$fromname,$fromdom,$fromcid,$status)= - &Apache::lonmsg::unpackmsgid($_); - if ($fromcid eq $ENV{'request.course.id'}) { + foreach my $msgid (sort(keys(%what))) { + my ($sendtime,$shortsubj,$fromname,$fromdom,$status,$fromcid)= + &Apache::lonmsg::unpackmsgid($msgid); + if (($fromcid) && ($fromcid eq $env{'request.course.id'})) { if (defined($sendtime) && $sendtime!~/error/) { my $numsendtime = $sendtime; $sendtime = &Apache::lonlocal::locallocaltime($sendtime); $critmsgcount ++; + if ($shortsubj eq '') { + $shortsubj = &mt('No subject'); + } + $shortsubj = &Apache::lonnet::unescape($shortsubj); push(@{$critmsgs}, { msgid => $msgid, sendtime => $sendtime, - shortsub => &Apache::lonnet::unescape($shortsubj), + shortsub => $shortsubj, from => $fromname, fromdom => $fromdom }); @@ -567,3 +655,4 @@ sub cmp_title { return $atitle cmp $btitle; } +1;
'); +} + sub getitems { - my ($unread,$ungraded,$bombed,$newdiscussions,$tograde,$bombs) = @_; + my ($unread,$ungraded,$bombed,$triggered,$newdiscussions,$tograde,$bombs,$warnings,$rowColor1,$rowColor2) = @_; my $navmap = Apache::lonnavmaps::navmap->new(); my @allres=$navmap->retrieveResources(); my %discussiontime = &Apache::lonnet::dump('discussiontimes', - $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, - $ENV{'course.'.$ENV{'request.course.id'}.'.num'}); - my %lastread = &Apache::lonnet::dump('nohist_'.$ENV{'request.course.id'}.'_discuss',$ENV{'user.domain'},$ENV{'user.name'},'lastread'); + $env{'course.'.$env{'request.course.id'}.'.domain'}, + $env{'course.'.$env{'request.course.id'}.'.num'}); + my %lastread = &Apache::lonnet::dump('nohist_'.$env{'request.course.id'}.'_discuss',$env{'user.domain'},$env{'user.name'},'lastread'); my %lastreadtime = (); my @discussions = (); my ($classlist,$keylist) = &Apache::loncoursedata::get_classlist(); - foreach (keys %lastread) { - my $key = $_; - $key =~ s/_lastread$//; - $lastreadtime{$key} = $lastread{$_}; + my %resourcetracker = &Apache::lonnet::dump('nohist_resourcetracker', + $env{'course.'.$env{'request.course.id'}.'.domain'}, + $env{'course.'.$env{'request.course.id'}.'.num'}); + + my $diffcheck = 0; + my $triescheck = 0; + my $warningnum = 0; + foreach my $key (keys(%lastread)) { + my $newkey = $key; + $newkey =~ s/_lastread$//; + $lastreadtime{$newkey} = $lastread{$key}; } foreach my $resource (@allres) { my $result = ''; @@ -427,15 +453,10 @@ sub getitems { my $symb = $resource->symb(); %{$$bombed{$symb}} = (); %{$$ungraded{$symb}} = (); + %{$$triggered{$symb}} = (); + $$triggered{$symb}{numparts} = 0; my $title = $resource->compTitle(); - my $ressymb = $symb; - if ($ressymb =~ m-(___adm/\w+/\w+)/(\d+)/bulletinboard$-) { - $ressymb = 'bulletin___'.$2.$1.'/'.$2.'/bulletinboard'; - unless ($ressymb =~ m|bulletin___\d+___adm/wrapper|) { - $ressymb=~s|(bulletin___\d+___)|$1adm/wrapper/|; - } - } - + my $ressymb = $resource->wrap_symb(); # Check for unread discussion postings if (defined($discussiontime{$ressymb})) { push(@discussions,$ressymb); @@ -447,21 +468,21 @@ sub getitems { if (defined($lastreadtime{$ressymb})) { $prevread = $lastreadtime{$ressymb}; } - my %contrib = &Apache::lonnet::restore($ressymb,$ENV{'request.course.id'}, - $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, - $ENV{'course.'.$ENV{'request.course.id'}.'.num'}); + my %contrib = &Apache::lonnet::restore($ressymb,$env{'request.course.id'}, + $env{'course.'.$env{'request.course.id'}.'.domain'}, + $env{'course.'.$env{'request.course.id'}.'.num'}); if ($contrib{'version'}) { for (my $id=1;$id<=$contrib{'version'};$id++) { unless (($contrib{'hidden'}=~/\.$id\./) || ($contrib{'deleted'}=~/\.$id\./)) { if ($prevread <$contrib{$id.':timestamp'}) { $$unread{$ressymb}{$unreadcount} = $id.': '.$contrib{$id.':subject'}; $unreadcount ++; - push(@{$newdiscussions}, $ressymb); } } } } - } + if ($unreadcount) { push(@{$newdiscussions}, $ressymb); } + } # Check for ungraded problems if ($resource->is_problem()) { @@ -469,19 +490,19 @@ sub getitems { my ($map,$ind,$url)=&Apache::lonnet::decode_symb($symb); my ($partlist,$handgrade,$responseType) = &Apache::grades::response_type($url,$symb); foreach my $student (keys(%$classlist)) { - my ($uname,$udom) = split/:/,$student; + my ($uname,$udom) = split(/:/,$student); my %status=&Apache::grades::student_gradeStatus($url,$symb,$udom,$uname,$partlist); my $submitted = 0; - my $graded = 0; + my $ungraded = 0; foreach (keys(%status)) { $submitted = 1 if ($status{$_} ne 'nothing'); - $graded = 1 if ($status{$_} !~ /^correct/); + $ungraded = 1 if ($status{$_} =~ /^ungraded/); my ($foo,$partid,$foo1) = split(/\./,$_); if ($status{'resource.'.$partid.'.submitted_by'} ne '') { $submitted = 0; } } - next if (!$submitted || !$graded); + next if (!$submitted || !$ungraded); $ctr ++; } if ($ctr) { @@ -502,31 +523,94 @@ sub getitems { $$bombed{$symb}{errorlink} = $errorlink; push(@{$bombs}, $symb); } +# Compile maxtries and degree of difficulty for problem parts + my @parts = @{$resource->parts()}; + my %stats; + my $warning = 0; + my $rowColor; + foreach my $part (@parts) { + %{$stats{$part}} = (); + my ($attempts,$users,$corrects,$degdiff,$av_attempts); + if (exists($resourcetracker{$symb."\0".$part."\0attempts"})) { + $attempts = $resourcetracker{$symb."\0".$part."\0attempts"}; + } + if (exists($resourcetracker{$symb."\0".$part."\0users"})) { + $users = $resourcetracker{$symb."\0".$part."\0users"}; + } + if (exists($resourcetracker{$symb."\0".$part."\0correct"})) { + $corrects = $resourcetracker{$symb."\0".$part."\0correct"}; + } + if ($attempts > 0) { + $degdiff = 1 - ($corrects/$attempts); + $degdiff = sprintf("%.2f",$degdiff); + } + if ($users > 0) { + $av_attempts = $attempts/$users; + } + if (($degdiff ne '' && $degdiff >= $diffcheck) || ($av_attempts ne '' && $av_attempts >= $triescheck)) { + $stats{$part}{degdiff} = $degdiff; + $stats{$part}{attempts} = $av_attempts; + $stats{$part}{users} = $users; + $warning = 1; + } + } + if ($warning) { + if ($warningnum %2 == 1) { + $rowColor = $rowColor1; + } else { + $rowColor = $rowColor2; + } + $$triggered{$symb}{title} = $resource->title; + foreach my $part (@parts) { + if (exists($stats{$part}{users})) { + if ($$triggered{$symb}{numparts}) { + $$triggered{$symb}{text} .= '
part - '.$part.'single part'.$stats{$part}{users}.''.$stats{$part}{attempts}.''.$stats{$part}{degdiff}.'