--- loncom/interface/lontrackstudent.pm 2004/08/29 19:58:32 1.6 +++ loncom/interface/lontrackstudent.pm 2005/04/07 06:56:23 1.15 @@ -1,6 +1,6 @@ # The LearningOnline Network with CAPA # -# $Id: lontrackstudent.pm,v 1.6 2004/08/29 19:58:32 matthew Exp $ +# $Id: lontrackstudent.pm,v 1.15 2005/04/07 06:56:23 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -44,7 +44,8 @@ package Apache::lontrackstudent; use strict; use Apache::Constants qw(:common :http); -use Apache::lonnet(); +use Apache::lonmysql; +use Apache::lonnet; use Apache::lonlocal; use Time::HiRes; @@ -55,11 +56,23 @@ sub get_data { &Apache::lonhtmlcommon::Update_PrgWin ($r,$prog_state,&mt('Composing Query')); # + # Allow the other server to begin processing the data before we ask for it. + sleep(5); + # + my $max_time = &get_max_time_in_db($r,$prog_state); + if (defined($max_time)) { + $r->print('

'.&mt('Activity data goes to [_1]', + &Apache::lonlocal::locallocaltime($max_time)). + '

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

'.&mt('Unable to retrieve any data. Please reload this page and try again.').'

'); + return; + } my $query = &build_query($mode); - &Apache::lonnet::logthis('sending query '.$query); ## ## Send it along - my $home = $ENV{'course.'.$ENV{'request.course.id'}.'.home'}; + my $home = $env{'course.'.$env{'request.course.id'}.'.home'}; my $reply=&Apache::lonnet::metadata_query($query,undef,undef,[$home]); if (ref($reply) ne 'HASH') { $r->print('

'. @@ -88,42 +101,104 @@ sub get_data { $r->print(&mt('Please try again in a few minutes.')); return; } -# $r->print('

'.&mt('Elapsed Time = [_1] seconds', -# time-$starttime).'

'); $r->rflush(); + # &Apache::lonhtmlcommon::Update_PrgWin ($r,$prog_state,&mt('Parsing results')); -# $r->print('

'. -# &mt('Reloading this page may result in newer data'). -# '

'); + # &output_results($r,$results_file,$navmap,$mode); &Apache::lonhtmlcommon::Update_PrgWin($r,$prog_state,&mt('Finished!')); return; } +sub table_names { + my $cid = $env{'request.course.id'}; + my $domain = $env{'course.'.$cid.'.domain'}; + my $home = $env{'course.'.$cid.'.home'}; + my $course = $env{'course.'.$cid.'.num'}; + my $prefix = $course.'_'.$domain.'_'; + # + my %tables = + ( student =>&Apache::lonmysql::fix_table_name($prefix.'students'), + res =>&Apache::lonmysql::fix_table_name($prefix.'resource'), + machine =>&Apache::lonmysql::fix_table_name($prefix.'machine_table'), + activity=>&Apache::lonmysql::fix_table_name($prefix.'activity'), + ); + return %tables; +} + +sub get_max_time_in_db { + my ($r,$prog_state) = @_; + my %table = &table_names(); + my $query = qq{SELECT MAX(time) FROM $table{'activity'} }; + # + my $home = $env{'course.'.$env{'request.course.id'}.'.home'}; + my $reply=&Apache::lonnet::metadata_query($query,undef,undef,[$home]); + if (ref($reply) ne 'HASH') { + return undef; + } + my $results_file = $r->dir_config('lonDaemons').'/tmp/'.$reply->{$home}; + my $endfile = $results_file.'.end'; + ## + ## Check for the results + &Apache::lonhtmlcommon::Update_PrgWin + ($r,$prog_state,&mt('Waiting for results')); + my $maxtime = 500; + my $starttime = time; + while (! -e $endfile && (time-$starttime < $maxtime)) { + &Apache::lonhtmlcommon::Update_PrgWin + ($r,$prog_state,&mt('Waiting up to [_1] seconds for results', + $starttime+$maxtime-time)); + sleep(1); + } + if (! -e $endfile) { + $r->print('

'. + &mt('Unable to retrieve data.').'

'); + $r->print(&mt('Please try again in a few minutes.')); + return undef; + } + $r->rflush(); + # + &Apache::lonhtmlcommon::Update_PrgWin + ($r,$prog_state,&mt('Parsing results')); + # + if (! open(TIMEDATA,$results_file)) { + $r->print('

'.&mt('Unable to read results file.').'

'. + '

'. + &mt('This is a serious error and has been logged. '. + 'You should contact your system administrator '. + 'to resolve this issue.'). + '

'); + return; + } + # + my $timestr = ''; + while (my $line = ) { + chomp($line); + $timestr = &Apache::lonnet::unescape($line); + } + close(TIMEDATA); + return &Apache::lonmysql::unsqltime($timestr); +} + sub build_query { my ($mode) = @_; - my $cid = $ENV{'request.course.id'}; - my $domain = $ENV{'course.'.$cid.'.domain'}; - my $home = $ENV{'course.'.$cid.'.home'}; - my $course = $ENV{'course.'.$cid.'.num'}; + my $cid = $env{'request.course.id'}; + my $domain = $env{'course.'.$cid.'.domain'}; + my $home = $env{'course.'.$cid.'.home'}; + my $course = $env{'course.'.$cid.'.num'}; my $prefix = $course.'_'.$domain.'_'; # - my $student_table = $prefix.'students'; - my $res_table = $prefix.'resource'; - my $action_table = $prefix.'actions'; - my $machine_table = $prefix.'machine_table'; - my $activity_table = $prefix.'activity'; + my %table = &table_names(); # my $query; if ($mode eq 'full_class') { $query = qq{ SELECT B.resource,A.time,C.student,A.action,E.machine,A.action_values - FROM $activity_table AS A - LEFT JOIN $res_table AS B ON B.res_id=A.res_id - LEFT JOIN $student_table AS C ON C.student_id=A.student_id - LEFT JOIN $machine_table AS E ON E.machine_id=A.machine_id - WHERE A.student_id>10 + FROM $table{'activity'} AS A + LEFT JOIN $table{'res'} AS B ON B.res_id=A.res_id + LEFT JOIN $table{'student'} AS C ON C.student_id=A.student_id + LEFT JOIN $table{'machine'} AS E ON E.machine_id=A.machine_id ORDER BY A.time DESC LIMIT 500 }; @@ -131,10 +206,10 @@ sub build_query { my $student = $1.':'.$2; $query = qq{ SELECT B.resource,A.time,A.action,E.machine,A.action_values - FROM $activity_table AS A - LEFT JOIN $res_table AS B ON B.res_id=A.res_id - LEFT JOIN $student_table AS C ON C.student_id=A.student_id - LEFT JOIN $machine_table AS E ON E.machine_id=A.machine_id + FROM $table{'activity'} AS A + LEFT JOIN $table{'res'} AS B ON B.res_id=A.res_id + LEFT JOIN $table{'student'} AS C ON C.student_id=A.student_id + LEFT JOIN $table{'machine'} AS E ON E.machine_id=A.machine_id WHERE C.student='$student' ORDER BY A.time DESC LIMIT 500 @@ -150,6 +225,11 @@ sub output_results { my ($r,$results_file,$navmap,$mode) = @_; ## ## + if (! -s $results_file) { + # results file is empty, just let them know there is no data + $r->print('

'.&mt('No data was returned for your request').'

'); + return; + } if (! open(ACTIVITYDATA,$results_file)) { $r->print('

'.&mt('Unable to read results file.').'

'. '

'. @@ -187,6 +267,7 @@ sub output_results { ## ## while (my $line = ) { + # FIXME: does not pass symbs along :( chomp($line); $line = &Apache::lonnet::unescape($line); if (++$count % 50 == 0) { @@ -198,12 +279,13 @@ sub output_results { } my ($symb,$timestamp,$student,$action,$machine,$values); if ($mode eq 'full_class') { - ($symb,$timestamp,$student,$action,$machine,$values) = - map { &Apache::lonnet::unescape($_); } split(',',$line,6); + ($symb,$timestamp,$student,$action,$machine,$values) = split(',',$line,6); } else { - ($symb,$timestamp,$action,$machine,$values) = - map { &Apache::lonnet::unescape($_); } split(',',$line,5); + ($symb,$timestamp,$action,$machine,$values) = split(',',$line,5); } + foreach ($symb,$timestamp,$student,$action,$machine) { + $_=&Apache::lonnet::unescape($_); + } my ($title,$src); if ($symb =~ m:^/adm/:) { $title = $symb; @@ -211,7 +293,7 @@ sub output_results { } else { my $nav_res = $navmap->getBySymb($symb); if (defined($nav_res)) { - $title = $nav_res->title(); + $title = $nav_res->compTitle(); $src = $nav_res->src(); } else { if ($src =~ m|^/res|) { @@ -251,27 +333,27 @@ sub output_results { $class = 'warning'; } # Clean up the values - $values =~ s/counter=\d+$//; + $values = &display_values($action,$values); # # Build the row for output my $tablerow = qq{}; if ($src =~ m|^/adm/|) { $tablerow .= - ''.$title.''; + ''.$title.''; } else { $tablerow .= - ''. + ''. ''.$title.''. ''; } - $tablerow .= ''.$timestamp.''; + $tablerow .= ''.$timestamp.''; if ($mode eq 'full_class') { - $tablerow.=''.$student.''; + $tablerow.=''.$student.''; } $tablerow .= - ''.$action.''. + ''.$action.''. # ''.$machine.''. - ''.$values.''. + ''.$values.''. ''; $r->print($tablerow.$/); } @@ -282,12 +364,40 @@ sub output_results { ################################################################### ################################################################### +sub display_values { + my ($action,$values)=@_; + my $result=''; + if ($action eq 'CSTORE') { + my %values=map {split('=',$_,-1)} split(/\&/,$values); + foreach my $key (sort(keys(%values))) { + $result.=''; + } + $result.='
'. + &Apache::lonnet::unescape($key). + '='. + &Apache::lonnet::unescape($values{$key}).'
'; + } elsif ($action eq 'POST') { + my %values= + map {split('=',&Apache::lonnet::unescape($_),-1)} split(/\&/,$values); + foreach my $key (sort(keys(%values))) { + if ($key eq 'counter') { next; } + $result.=''.$key.''. + '='.$values{$key}.''; + } + $result.=''; + } else { + $result=&Apache::lonnet::unescape($values) + } + return $result; +} +################################################################### +################################################################### sub request_data_update { my $command = 'prepare activity log'; - my $cid = $ENV{'request.course.id'}; - my $domain = $ENV{'course.'.$cid.'.domain'}; - my $home = $ENV{'course.'.$cid.'.home'}; - my $course = $ENV{'course.'.$cid.'.num'}; + my $cid = $env{'request.course.id'}; + my $domain = $env{'course.'.$cid.'.domain'}; + my $home = $env{'course.'.$cid.'.home'}; + my $course = $env{'course.'.$cid.'.num'}; # &Apache::lonnet::logthis($command.' '.$course.' '.$domain.' '.$home); my $result = &Apache::lonnet::metadata_query($command,$course,$domain, [$home]); @@ -328,6 +438,7 @@ sub developer_centric_styles { tr.warning { background-color: red; } tr.chat { background-color: yellow; } tr.chatfetch { background-color: yellow; } + tr.evaluate { background-color: red; } tr.navmaps { background-color: \#777777; } tr.roles { background-color: \#999999; } tr.flip { background-color: \#BBBBBB; } @@ -352,18 +463,18 @@ sub handler { $loaderror= &Apache::lonnet::overloaderror ($r, - $ENV{'course.'.$ENV{'request.course.id'}.'.home'}); + $env{'course.'.$env{'request.course.id'}.'.home'}); if ($loaderror) { return $loaderror; } # # Check for access - if (! &Apache::lonnet::allowed('vsa',$ENV{'request.course.id'})) { - $ENV{'user.error.msg'}= + if (! &Apache::lonnet::allowed('vsa',$env{'request.course.id'})) { + $env{'user.error.msg'}= $r->uri.":vsa:0:0:Cannot student activity for complete course"; if (! &Apache::lonnet::allowed('vsa', - $ENV{'request.course.id'}.'/'. - $ENV{'request.course.sec'})) { - $ENV{'user.error.msg'}= + $env{'request.course.id'}.'/'. + $env{'request.course.sec'})) { + $env{'user.error.msg'}= $r->uri.":vsa:0:0:Cannot view student activity with given role"; return HTTP_NOT_ACCEPTABLE; } @@ -390,7 +501,8 @@ sub handler { bug=>'instructor interface'}); # # Give the LON-CAPA page header - $r->print(''.&styles.''. + my $html=&Apache::lonxml::xmlbegin(); + $r->print($html.'<head>'.&styles().'<title>'. &mt('Student Activity'). "\n". &Apache::loncommon::bodytag('Student Activity'). @@ -413,20 +525,21 @@ sub handler { ($r,\%prog_state,&mt('Contacting course home server')); # my $result = &request_data_update(); - if (ref($result) eq 'HASH') { - $result = join(' ',map { $_.'=>'.$result->{$_}; } keys(%$result)); - } # - if (exists($ENV{'form.selected_student'})) { + if (exists($env{'form.selected_student'})) { # For now, just show all the data, in the future allow selection of # a student - my ($sname,$sdom) = split(':',$ENV{'form.selected_student'}); + my ($sname,$sdom) = split(':',$env{'form.selected_student'}); if ($sname =~ /^\w*$/ && $sdom =~ /^\w*$/) { $r->print('

'. &mt('Recent activity of [_1]@[_2]',$sname,$sdom). '

'); + $r->print('

'.&mt(<'); +Compiling student activity data can take a long time. +It may be necessary to reload this page to get the most current information. +END &get_data($r,\%prog_state,$navmap, - 'student:'.$ENV{'form.selected_student'}); + 'student:'.$env{'form.selected_student'}); } else { $r->print('

'.&mt('Unable to process for [_1]@[_2]', $sname,$sdom).'

');