--- loncom/interface/lonwhatsnew.pm 2005/04/29 14:54:18 1.12 +++ loncom/interface/lonwhatsnew.pm 2005/05/23 23:06:23 1.13 @@ -1,5 +1,5 @@ # -# $Id: lonwhatsnew.pm,v 1.12 2005/04/29 14:54:18 raeburn Exp $ +# $Id: lonwhatsnew.pm,v 1.13 2005/05/23 23:06:23 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -52,10 +52,13 @@ sub handler { } &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},['command']); - my $command = $env{'form.command'}; - - if ($command eq '') { - $command = "info"; + my $command; + if ($env{'form.action'} eq 'reset') { + $command = 'reset'; + } elsif ($env{'form.action'} eq 'update') { + $command = 'update'; + } else { + $command = $env{'form.command'}; } &Apache::loncommon::content_type($r,'text/html'); @@ -68,15 +71,15 @@ sub handler { } &Apache::lonhtmlcommon::clear_breadcrumbs(); - if ($command eq 'config') { + if ($command eq 'chgthreshold') { &Apache::lonhtmlcommon::add_breadcrumb - ({href=>'/adm/whatsnew?command=config', - text=>"Configure display"}); + ({href=>'/adm/whatsnew?command=threshold', + text=>"Change thresholds"}); $r->print(&Apache::lonhtmlcommon::breadcrumbs - (undef,'Course Action Items','Course_Action_Items_Config')); + (undef,'Course Action Items','Course_Action_Items_Thresholds')); } else { &Apache::lonhtmlcommon::add_breadcrumb - ({href=>'/adm/whatsnew?command=info', + ({href=>'/adm/whatsnew', text=>"Display Action Items"}); $r->print(&Apache::lonhtmlcommon::breadcrumbs (undef,'Course Action Items','Course_Action_Items_Display')); @@ -94,30 +97,17 @@ sub display_main_box { my ($r,$command) = @_; my $domain=&Apache::loncommon::determinedomain(); my $tabbg=&Apache::loncommon::designparm('coordinator.tabbg',$domain); - 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,$picker); + + my %threshold_titles = ( + av_attempts => 'Average number of attempts', + degdiff => 'Degree of difficulty', + numstudents => 'Total number of students with submissions', + ); + if ($command eq 'chgthreshold') { + &display_config_box($r,$command,$tabbg,\%threshold_titles); } else { - &display_actions_box($r,$picker); + &display_actions_box($r,$command,\%threshold_titles); } $r->print(< @@ -155,7 +145,7 @@ ENDHEAD #------------------------------- sub display_actions_box() { - my ($r,$picker) = @_; + my ($r,$command,$threshold_titles) = @_; my $rowColor1 = "#ffffff"; my $rowColor2 = "#eeeeee"; @@ -181,18 +171,38 @@ sub display_actions_box() { $function='admin'; } + my %threshold = ( + av_attempts => 0, + degdiff => 0.01, + numstudents => 0, + ); + my $pgbg=&Apache::loncommon::designparm($function.'.pgbg',$domain); my $tabbg=&Apache::loncommon::designparm($function.'.tabbg',$domain); - &getitems(\%unread,\%ungraded,\%bombed,\%triggered,\@newdiscussions,\@tograde,\@bombs,\@warnings,$rowColor1,$rowColor2); - my ($msgcount,$critmsgcount) = &getmail(\@newmsgs,\@critmsgs); - unless ($env{'request.course.id'}) { $r->print('
You are accessing an invalid course


'); return; } - $r->print(''.$picker.'

'); + my $result; + my $cdom = $env{'course.'.$env{'request.course.id'}.'.domain'}; + my $crs = $env{'course.'.$env{'request.course.id'}.'.num'}; + + if ($command eq 'reset') { + $result = &process_reset($cdom,$crs); + } elsif ($command eq 'update') { + $result = &process_update($cdom,$crs,$threshold_titles); + } + if ($result) { + $r->print($result.'
'); + } + + &get_curr_thresholds(\%threshold,$cdom,$crs); + &getitems(\%unread,\%ungraded,\%bombed,\%triggered,\@newdiscussions,\@tograde,\@bombs,\@warnings,$rowColor1,$rowColor2,\%threshold,$cdom,$crs); + my ($msgcount,$critmsgcount) = &getmail(\@newmsgs,\@critmsgs); + + $r->print('
'); # @bombs = sort { &cmp_title($a,$b) } @bombs; foreach my $bomb (@bombs) { if ($bombnum %2 == 1) { @@ -249,7 +260,7 @@ END } else { $rowColor = $rowColor2; } - $r->print(''); + $r->print(''); $bombnum ++; } } else { @@ -258,25 +269,30 @@ END $r->print('
'); ## UNGRADED ITEMS ## $r->print(< 0) { + $r->print('
ResourceNumber of errors
'.$bombed{$bomb}{errorlink}.'
'.$bombed{$bomb}{errorlink}.''.$bombed{$bomb}{errorcount}.'

'); # DEGDIFF AND AV. TRIES TRIGGERS - $r->print(<<"END"); + $r->print(<<"END");
- + + + +
Problems with average attempts > 0 or degree of difficulty > 0Problems with av. attempts ≥ $threshold{'av_attempts'} or deg. difficulty ≥ $threshold{'degdiff'}
and total number of students with submissions ≥ $threshold{'numstudents'}
Change thresholds?
END - my $warningnum = 0; - if (@warnings > 0) { + my $warningnum = 0; + if (@warnings > 0) { # @warnings = sort { &cmp_title($a,$b) } @warnings; - $r->print(''); + $r->print(''. + ' print(''); foreach my $res (@warnings) { if ($warningnum %2 == 1) { - $rowColor = $rowColor1; + $rowColor = $rowColor1; } else { $rowColor = $rowColor2; } @@ -290,8 +306,9 @@ END $r->print(''.$triggered{$res}{text}); $warningnum ++; } + $r->print(''); + $r->print(''); } $r->print('
ResourcePartNum. studentsAv. AttemptsDeg. Diff
ResourcePartNum. studentsAv. AttemptsDeg. DiffLast ResetReset Count?
'.$triggered{$res}{title}.'

'); } else { - $r->print('

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


No problems satisfy threshold criteria.


'); @@ -414,33 +431,82 @@ END #------------------------------- # display_config_box # -# Display the action items +# Display the threshold setting screen # #------------------------------- sub display_config_box() { - my ($r,$picker) = @_; - $r->print(''.$picker.'

'); + my ($r,$command,$tabbg,$threshold_titles) = @_; + my %threshold = (); + my $rowColor1 = "#ffffff"; + my $rowColor2 = "#eeeeee"; + my $rowColor; + + my @thresholditems = ("av_attempts","degdiff","numstudents"); + my %threshold_titles = ( + av_attempts => 'Average number of attempts', + degdiff => 'Degree of difficulty', + numstudents => 'Total number of students with submissions', + ); + &get_curr_thresholds(\%threshold); + + $r->print('
'."\n"; } @@ -577,6 +650,8 @@ sub getitems { + + '; $$triggered{$symb}{numparts} ++; } @@ -587,6 +662,101 @@ sub getitems { } } +sub get_counter_resets { + my ($resethash,$part) = @_; + my $lastreset = 'None'; + if ($$resethash{'version'}) { + for (my $version=1;$version<=$$resethash{'version'};$version++) { + if (exists($$resethash{$version.':'.$part.'prev_attempts'})) { + $lastreset = $$resethash{$version.':timestamp'}; + } + } + } + unless ($lastreset eq 'None') { + $lastreset = localtime($lastreset); + } + return $lastreset; +} + +sub get_curr_thresholds { + my ($threshold,$cdom,$crs) = @_; + my %coursesettings = &Apache::lonnet::dump('environment', + $cdom,$crs,'internal.threshold'); + if (exists($coursesettings{'internal.threshold_av_attempts'})) { + $$threshold{'av_attempts'} = $coursesettings{'internal.threshold_av_attempts'}; + } + if (exists($coursesettings{'internal.threshold_degdiff'})) { + $$threshold{'degdiff'} = $coursesettings{'internal.threshold_degdiff'}; + } + if (exists($coursesettings{'internal.threshold_numstudents'})) { + $$threshold{'numstudents'} = $coursesettings{'internal.threshold_numstudents'}; + } +} + +sub process_reset { + my ($dom,$crs) = @_; + my $result = 'Counters reset for following problems (and parts):
'; + my @agg_types = ('attempts','users','correct'); + my %agg_titles = ( + attempts => 'Number of submissions', + users => 'Students with submissions', + correct => 'Number of correct submissions', + ); + my @resets = (); + my %titles = (); + foreach my $key (keys %env) { + next if ($key !~ /^form\.reset_(.+)$/); + my $title = &Apache::lonnet::unescape($env{'form.title_'.$1}); + my $reset_item = &Apache::lonnet::unescape($1); + my %curr_aggregates = &Apache::lonnet::dump('nohist_resourcetracker',$dom,$crs,$reset_item); + my %resethash = (); + my %aggregates = (); + my ($symb,$part) = split/\0/,$reset_item; + foreach my $type (@agg_types) { + $aggregates{$reset_item."\0".$type} = 0; + $resethash{$part."\0".'prev_'.$type} = $curr_aggregates{$reset_item."\0".$type}; + } + my $putresult = &Apache::lonnet::put('nohist_resourcetracker',\%aggregates, + $dom,$crs); + if ($putresult eq 'ok') { + my $storeresult = &Apache::lonnet::cstore(\%resethash,$symb,'nohist_resourcetracker',$dom,$crs); + $result .= $title.' -part '.$part.': '; + my %new_aggregates = &Apache::lonnet::dump('nohist_resourcetracker',$dom,$crs,$reset_item); + foreach my $type (@agg_types) { + $result .= $agg_titles{$type}.' = '.$new_aggregates{$reset_item."\0".$type}.'; '; + } + $result =~ s/; $//; + $result .= '
'; + } else { + $result = $title.' -part '.$part.': '.&mt('Unable to reset counters to zero due to ').$putresult.'.
'."\n"; + } + } + return $result; +} + +sub process_update { + my ($dom,$crs,$threshold_titles) = @_; + my $setoutput = 'Changes to threshold(s):
'; + foreach (keys %env) { + next if ($_!~/^form\.(.+)\_setparmval$/); + my $name = $1; + my $value = $env{'form.'.$name.'_value'}; + if ($name && defined($value)) { + my $put_result = &Apache::lonnet::put('environment', + {$name=>$value},$dom,$crs); + + my ($shortname) = ($name =~ /^internal\.threshold_(.+)$/); + if ($put_result eq 'ok') { + $setoutput.=&mt('Set threshold for').' '.$$threshold_titles{$shortname}.' '.&mt('to').' '.$value.'.
'; + } else { + $setoutput.=&mt('Unable to set threshold for').' '.$name.' '.&mt('to'). + ' '.$value.' '.&mt('due to').' '.$put_result.'.
'; + } + } + } + return $setoutput; +} + sub getmail { my ($newmsgs,$critmsgs) = @_; # Check for unread mail in course
+ + +
+ + +
+ + + + + + '); + my $rowNum =0; + foreach my $type (@thresholditems) { + my $parameter = 'internal.threshold_'.$type; +# onchange is javascript to automatically check the 'Set' button. + my $onchange = 'onFocus="javascript:window.document.forms'. + "['thresholdform'].elements['".$parameter."_setparmval']". + '.checked=true;"'; + if ($rowNum %2 == 1) { + $rowColor = $rowColor1; + } else { + $rowColor = $rowColor2; + } + $r->print(' + + + + + '); + $rowNum ++; + } + $r->print('
Threshold NameCurrent valueChange?
'.$threshold_titles{$type}.''.&Apache::lonhtmlcommon::textbox($parameter.'_value', + $threshold{$type}, + 10,$onchange).'' + .&Apache::lonhtmlcommon::checkbox($parameter.'_setparmval'). + '
+
+ + '); } sub getitems { - my ($unread,$ungraded,$bombed,$triggered,$newdiscussions,$tograde,$bombs,$warnings,$rowColor1,$rowColor2) = @_; + my ($unread,$ungraded,$bombed,$triggered,$newdiscussions,$tograde,$bombs,$warnings,$rowColor1,$rowColor2,$threshold,$cdom,$crs) = @_; 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'); + my %discussiontime = &Apache::lonnet::dump('discussiontimes',$cdom,$crs); + 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(); 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; + $cdom,$crs); + my %res_title; my $warningnum = 0; foreach my $key (keys(%lastread)) { my $newkey = $key; @@ -451,11 +517,12 @@ sub getitems { my $result = ''; my $applies = 0; my $symb = $resource->symb(); - %{$$bombed{$symb}} = (); +# %{$$bombed{$symb}} = (); %{$$ungraded{$symb}} = (); %{$$triggered{$symb}} = (); $$triggered{$symb}{numparts} = 0; my $title = $resource->compTitle(); + $res_title{$symb} = $title; my $ressymb = $resource->wrap_symb(); # Check for unread discussion postings if (defined($discussiontime{$ressymb})) { @@ -468,9 +535,8 @@ 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'},$cdom,$crs); if ($contrib{'version'}) { for (my $id=1;$id<=$contrib{'version'};$id++) { unless (($contrib{'hidden'}=~/\.$id\./) || ($contrib{'deleted'}=~/\.$id\./)) { @@ -515,10 +581,12 @@ sub getitems { # Check for bombs if ($resource->getErrors()) { my $errors = $resource->getErrors(); + $errors =~ s/^,//; my @bombs = split(/,/, $errors); my $errorcount = scalar(@bombs); my $errorlink = ''; + &Apache::lonnet::escape($bombs[0]).'">'. + $title.''; $$bombed{$symb}{errorcount} = $errorcount; $$bombed{$symb}{errorlink} = $errorlink; push(@{$bombs}, $symb); @@ -526,6 +594,7 @@ sub getitems { # Compile maxtries and degree of difficulty for problem parts my @parts = @{$resource->parts()}; my %stats; + my %lastreset = (); my $warning = 0; my $rowColor; foreach my $part (@parts) { @@ -547,10 +616,12 @@ sub getitems { if ($users > 0) { $av_attempts = $attempts/$users; } - if (($degdiff ne '' && $degdiff >= $diffcheck) || ($av_attempts ne '' && $av_attempts >= $triescheck)) { + if ((($degdiff ne '' && $degdiff >= $$threshold{'degdiff'}) || ($av_attempts ne '' && $av_attempts >= $$threshold{'av_attempts'})) && ($users >= $$threshold{'numstudents'})) { $stats{$part}{degdiff} = $degdiff; $stats{$part}{attempts} = $av_attempts; $stats{$part}{users} = $users; + my %resethash = &Apache::lonnet::restore($symb,'nohist_resourcetracker',$cdom,$crs); + $lastreset{$part} = &get_counter_resets(\%resethash,$part); $warning = 1; } } @@ -563,6 +634,8 @@ sub getitems { $$triggered{$symb}{title} = $resource->title; foreach my $part (@parts) { if (exists($stats{$part}{users})) { + my $resetname = 'reset_'.&Apache::lonnet::escape($symb."\0".$part); + my $resettitle = 'title_'.&Apache::lonnet::escape($symb."\0".$part); if ($$triggered{$symb}{numparts}) { $$triggered{$symb}{text} .= '
'.$stats{$part}{users}.' '.$stats{$part}{attempts}.' '.$stats{$part}{degdiff}.''.$lastreset{$part}.'