+
+
+
+
+
+
+
+
+ Threshold Name
+ Current value
+ Change?
+ ');
+ 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('
+
+ '.$threshold_titles{$type}.'
+ '.&Apache::lonhtmlcommon::textbox($parameter.'_value',
+ $threshold{$type},
+ 10,$onchange).'
+ '
+ .&Apache::lonhtmlcommon::checkbox($parameter.'_setparmval').
+ '
+ ');
+ $rowNum ++;
+ }
+ $r->print('
+
+
+ ');
+}
+
+sub display_launcher {
+ my ($r,$action,$checkallowed,$tabbg,$rowColor1,$rowColor2,$show,
+ $headings,$res_title,$tograde,$ungraded,$bombs,$bombed,$changed,
+ $warnings,$triggered,$newdiscussions,$unread,$msgcount,$newmsgs,
+ $critmsgcount,$critmsgs,$interval) = @_;
+
+ if ($$checkallowed{$action}) {
+ &start_box($r,$tabbg,$show,$headings,$action);
+ if ($$show{$action}) {
+ if ($action eq 'handgrading') { # UNGRADED ITEMS
+ &display_handgrade($r,$tograde,$rowColor1,$rowColor2,
+ $ungraded);
+ } elsif ($action eq 'haserrors') { # BOMBS
+ &display_haserrors($r,$bombs,$rowColor1,$rowColor2,$bombed,
+ $res_title);
+ } elsif ($action eq 'versionchanges') { # VERSION CHANGES
+ &display_versionchanges($r,$changed,$res_title,$rowColor1,
+ $rowColor2,$interval);
+
+ } elsif ($action eq 'abovethreshold') { # DEGDIFF/AV. TRIES TRIGGERS
+ &display_abovethreshold($r,$warnings,$triggered,$res_title,
+ $rowColor1,$rowColor2);
+ } elsif ($action eq 'coursediscussion') { # UNREAD COURSE DISCUSSION
+ &display_coursediscussion($r,$newdiscussions,$unread,
+ $res_title);
+ } elsif ($action eq 'coursenormalmail') { # NORMAL MESSAGES
+ &display_coursenormalmail($r,$msgcount,$newmsgs,$rowColor1,
+ $rowColor2);
+ } elsif ($action eq 'coursecritmail') { # CRITICAL MESSAGES
+ &display_coursecritmail($r,$critmsgcount,$critmsgs,$rowColor1,
+ $rowColor2);
+ }
+ }
+ &end_box($r);
+ }
+ return;
}
sub getitems {
- my ($unread,$ungraded,$bombed,$newdiscussions,$tograde,$bombs) = @_;
+ my ($unread,$ungraded,$bombed,$triggered,$changed,$newdiscussions,
+ $tograde,$bombs,$warnings,$rowColor1,$rowColor2,$threshold,$cdom,$crs,
+ $res_title,$show,$starttime) = @_;
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 %lastreadtime = ();
- my @discussions = ();
- my ($classlist,$keylist) = &Apache::loncoursedata::get_classlist();
-
- foreach my $key (keys(%lastread)) {
- my $newkey = $key;
- $newkey =~ s/_lastread$//;
- $lastreadtime{$newkey} = $lastread{$key};
+ # force retrieve Resource to seed the part id cache we'll need it later
+ my @allres=$navmap->retrieveResources(undef,sub {if ($_[0]->is_problem) { $_[0]->parts();} return 1;});
+ my %lastreadtime;
+ my %resourcetracker;
+
+# Resource version changes
+ if ($$show{'versionchanges'}) {
+ &checkversions($cdom,$crs,$navmap,$changed,$starttime);
+ }
+
+ if ($$show{'coursediscussions'}) {
+ my %lastread = &Apache::lonnet::dump('nohist_'.
+ $env{'request.course.id'}.'_discuss',
+ $env{'user.domain'},$env{'user.name'},'lastread');
+ foreach my $key (keys(%lastread)) {
+ my $newkey = $key;
+ $newkey =~ s/_lastread$//;
+ $lastreadtime{$newkey} = $lastread{$key};
+ }
+ }
+
+ if ($$show{'abovethreshold'}) {
+ %resourcetracker = &Apache::lonnet::dump('nohist_resourcetracker',
+ $cdom,$crs);
}
+
+ my $warningnum = 0;
foreach my $resource (@allres) {
my $result = '';
my $applies = 0;
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 = $resource->wrap_symb();
- }
+ $$res_title{$symb} = $title;
+ my $ressymb = $resource->wrap_symb();
+
# Check for unread discussion postings
- if (defined($discussiontime{$ressymb})) {
- push(@discussions,$ressymb);
- my $prevread = 0;
- my $unreadcount = 0;
- %{$$unread{$ressymb}} = ();
- $$unread{$ressymb}{'title'} = $title;
- $$unread{$ressymb}{'symb'} = $symb;
- 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'});
- 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 ($$show{'coursediscussion'}) {
+ &check_discussions($cdom,$crs,$resource,$symb,$ressymb,$title,
+ $newdiscussions,$unread,\&lastreadtime);
}
# Check for ungraded problems
if ($resource->is_problem()) {
- my $ctr = 0;
- 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 %status=&Apache::grades::student_gradeStatus($url,$symb,$udom,$uname,$partlist);
- my $submitted = 0;
- my $graded = 0;
- foreach (keys(%status)) {
- $submitted = 1 if ($status{$_} ne 'nothing');
- $graded = 1 if ($status{$_} !~ /^correct/);
- my ($foo,$partid,$foo1) = split(/\./,$_);
- if ($status{'resource.'.$partid.'.submitted_by'} ne '') {
- $submitted = 0;
+ if ($$show{'handgrading'}) {
+ &check_handgraded($resource,$symb,$title,$cdom,$crs,$ungraded,
+ $tograde);
+ }
+ }
+
+# Check for bombs
+ if ($$show{'haserrors'}) {
+ &check_bombed($resource,$symb,$title,$bombs,$bombed);
+ }
+
+# Maxtries and degree of difficulty for problem parts, unless handgradeable
+ if ($$show{'abovethreshold'}) {
+ &check_thresholds($resource,$symb,\%resourcetracker,$triggered,
+ $threshold,$warnings,$warningnum,$rowColor1,$rowColor2);
+ }
+
+ }
+}
+
+sub check_discussions {
+ my ($cdom,$crs,$resource,$symb,$ressymb,$title,$newdiscussions,$unread,
+ $lastreadtime) = @_;
+# Check for unread discussion postings
+ if ($resource->hasDiscussion()) {
+ my $prevread = 0;
+ my $unreadcount = 0;
+ %{$$unread{$ressymb}} = ();
+ $$unread{$ressymb}{'title'} = $title;
+ $$unread{$ressymb}{'symb'} = $symb;
+ if (defined($$lastreadtime{$ressymb})) {
+ $prevread = $$lastreadtime{$ressymb};
+ }
+ 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\./)) {
+ if ($prevread <$contrib{$id.':timestamp'}) {
+ $$unread{$ressymb}{$unreadcount} = $id.': '.$contrib{$id.':subject'};
+ $unreadcount ++;
}
}
- next if (!$submitted || !$graded);
- $ctr ++;
}
- if ($ctr) {
- $$ungraded{$symb}{count} = $ctr;
+ }
+ if ($unreadcount) { push(@{$newdiscussions}, $ressymb); }
+ }
+}
+
+sub check_handgraded {
+ my ($resource,$symb,$title,$cdom,$cnum,$ungraded,$tograde) = @_;
+ if ($resource->is_problem()) {
+ my ($map,$ind,$url)=&Apache::lonnet::decode_symb($symb);
+ my $partlist=$resource->parts();
+ my $handgradeable;
+ foreach my $part (@$partlist) {
+ if ($resource->handgrade($part) eq 'yes') {
+ $handgradeable=1; last;
+ }
+ }
+ if ($handgradeable) {
+ my @ungraded = &Apache::bridgetask::get_users_in_queue(
+ 'gradingqueue',$symb,$cdom,$cnum);
+ if (@ungraded > 0) {
+ $$ungraded{$symb}{count} = scalar(@ungraded);
$$ungraded{$symb}{title} = $title;
push(@{$tograde}, $symb);
}
}
+ }
+}
-# Check for bombs
- if ($resource->getErrors()) {
- my $errors = $resource->getErrors();
- my @bombs = split(/,/, $errors);
- my $errorcount = scalar(@bombs);
- my $errorlink = '';
- $$bombed{$symb}{errorcount} = $errorcount;
- $$bombed{$symb}{errorlink} = $errorlink;
- push(@{$bombs}, $symb);
+sub check_bombed {
+ my ($resource,$symb,$title,$bombs,$bombed) = @_;
+ if ($resource->getErrors()) {
+ my $errors = $resource->getErrors();
+ $errors =~ s/^,//;
+ my @bombs = split(/,/, $errors);
+ my $errorcount = scalar(@bombs);
+ my $errorlink = ' '.
+ $title.' ';
+ $$bombed{$symb}{errorcount} = $errorcount;
+ $$bombed{$symb}{errorlink} = $errorlink;
+ push(@{$bombs}, $symb);
+ }
+}
+
+sub check_thresholds {
+ my ($resource,$symb,$resourcetracker,$triggered,$threshold,$warnings,
+ $warningnum,$rowColor1,$rowColor2) = @_;
+# Compile maxtries and degree of difficulty for problem parts, unless handgradeable
+ my @parts = @{$resource->parts()};
+ my %stats;
+ my %lastreset = ();
+ my $warning = 0;
+ my $rowColor;
+ foreach my $part (@parts) {
+ if ($resource->handgrade($part) eq 'yes') {
+ next;
+ }
+ %{$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;
+ $av_attempts = sprintf("%.2f",$av_attempts);
+ }
+ 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;
+ $lastreset{$part} = $$resourcetracker{$symb."\0".$part."\0resettime"};
+ if ($lastreset{$part}) {
+ $lastreset{$part} = &Apache::lonnavmaps::timeToHumanString($lastreset{$part});
+ }
+ $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})) {
+ 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} .= '