File:  [LON-CAPA] / loncom / interface / lontrackstudent.pm
Revision 1.4: download - view: text, annotated - select for diffs
Mon Aug 23 15:03:15 2004 UTC (19 years, 9 months ago) by matthew
Branches: MAIN
CVS tags: HEAD
Color coding of output, modified query to not request ALL data.  Query and
table structure needs a good deal more work....

    1: # The LearningOnline Network with CAPA
    2: #
    3: # $Id: lontrackstudent.pm,v 1.4 2004/08/23 15:03:15 matthew Exp $
    4: #
    5: # Copyright Michigan State University Board of Trustees
    6: #
    7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
    8: #
    9: # LON-CAPA is free software; you can redistribute it and/or modify
   10: # it under the terms of the GNU General Public License as published by
   11: # the Free Software Foundation; either version 2 of the License, or
   12: # (at your option) any later version.
   13: #
   14: # LON-CAPA is distributed in the hope that it will be useful,
   15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
   16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   17: # GNU General Public License for more details.
   18: #
   19: # You should have received a copy of the GNU General Public License
   20: # along with LON-CAPA; if not, write to the Free Software
   21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   22: #
   23: # /home/httpd/html/adm/gpl.txt
   24: #
   25: # http://www.lon-capa.org/
   26: #
   27: ###
   28: 
   29: =pod
   30: 
   31: =head1 NAME
   32: 
   33: lontrackstudent
   34: 
   35: =head1 SYNOPSIS
   36: 
   37: Track student progress through course materials
   38: 
   39: =over 4
   40: 
   41: =cut
   42: 
   43: package Apache::lontrackstudent;
   44: 
   45: use strict;
   46: use Apache::Constants qw(:common :http);
   47: use Apache::lonnet();
   48: use Apache::lonlocal;
   49: use Time::HiRes;
   50: 
   51: ###################################################################
   52: ###################################################################
   53: sub get_all_data {
   54:     my ($r,$prog_state,$navmap) = @_;
   55:     ##
   56:     ## Compose the query
   57:     &Apache::lonhtmlcommon::Update_PrgWin
   58:         ($r,$prog_state,&mt('Composing Query'));
   59:     #
   60:     my $query;
   61:     my $cid = $ENV{'request.course.id'};
   62:     my $domain = $ENV{'course.'.$cid.'.domain'};
   63:     my $home = $ENV{'course.'.$cid.'.home'};
   64:     my $course = $ENV{'course.'.$cid.'.num'};
   65:     my $prefix = $course.'_'.$domain.'_';
   66:     #
   67:     my $student_table  = $prefix.'students';
   68:     my $res_table      = $prefix.'resource';
   69:     my $action_table   = $prefix.'actions';
   70:     my $machine_table  = $prefix.'machine_table';
   71:     my $activity_table = $prefix.'activity';
   72:     #
   73:     $query = qq{
   74:         SELECT B.resource,A.time,C.student,D.action,E.machine,A.action_values 
   75:             FROM $activity_table AS A
   76:             LEFT JOIN $res_table      AS B ON B.res_id=A.res_id 
   77:             LEFT JOIN $student_table  AS C ON C.student_id=A.student_id 
   78:             LEFT JOIN $action_table   AS D ON D.action_id=A.action_id 
   79:             LEFT JOIN $machine_table  AS E ON E.machine_id=A.machine_id
   80:             WHERE A.student_id>10
   81:             ORDER BY A.time ASC
   82:             LIMIT 5000
   83:     };
   84:     $query =~ s|$/||g;
   85:     &Apache::lonnet::logthis($query);
   86:     ##
   87:     ## Send it along
   88:     my $reply=&Apache::lonnet::metadata_query($query,undef,undef,[$home]);
   89:     if (ref($reply) ne 'HASH') {
   90:         $r->print('<h2>'.
   91:                   &mt('Error contacting home server for course: [_1]',
   92:                       $reply).
   93:                   '</h2>');
   94:         return;
   95:     }
   96:     my $results_file = $r->dir_config('lonDaemons').'/tmp/'.$reply->{$home};
   97:     my $endfile = $results_file.'.end';
   98:     ##
   99:     ## Check for the results
  100:     &Apache::lonhtmlcommon::Update_PrgWin
  101:         ($r,$prog_state,&mt('Waiting for results'));
  102:     my $maxtime = 500;
  103:     my $starttime = time;
  104:     while (! -e $endfile && (time-$starttime < $maxtime)) {
  105:         &Apache::lonhtmlcommon::Update_PrgWin
  106:             ($r,$prog_state,&mt('Waiting up to [_1] seconds for results',
  107:                                 $starttime+$maxtime-time));
  108:         sleep(1);
  109:     }
  110:     if (! -e $endfile) {
  111:         $r->print('<h2>'.
  112:                   &mt('Unable to retrieve data.').'</h2>');
  113:         $r->print(&mt('Please try again in a few minutes.'));
  114:         return;
  115:     }
  116:     &Apache::lonhtmlcommon::Update_PrgWin
  117:         ($r,$prog_state,&mt('Parsing results'));
  118:     &output_results($r,$results_file,$navmap);
  119:     &Apache::lonhtmlcommon::Update_PrgWin
  120:         ($r,$prog_state,&mt('Finished!'));
  121:     return;
  122: }
  123: 
  124: sub output_results {
  125:     my ($r,$results_file,$navmap) = @_;
  126:     if (! open(ACTIVITYDATA,$results_file)) {
  127:         $r->print('<h2>'.&mt('Unable to read results file.').'</h2>'.
  128:                   '<p>'.
  129:                   &mt('This is a serious error and has been logged.  '.
  130:                       'You should contact your system administrator '.
  131:                       'to resolve this issue.').
  132:                   '</p>');
  133:         return;
  134:     }
  135:     my $tableheader = 
  136:         '<table><tr>'.
  137:         '<th>'.&mt('Resource').'</th>'.
  138:         '<th>'.&mt('Time').'</th>'.
  139:         '<th>'.&mt('Student').'</th>'.
  140:         '<th>'.&mt('Action').'</th>'.
  141:         '<th>'.&mt('Originating Server').'</th>'.
  142:         '<th>'.&mt('Data').'</th>'.
  143:         '</tr>'.$/;
  144:     my $count = 0;
  145:     $r->print($tableheader);
  146:     $r->rflush();
  147:     while (my $line = <ACTIVITYDATA>) {
  148:         $line = &Apache::lonnet::unescape($line);
  149:         if (++$count % 50 == 0) {
  150:             $r->print('</table>'.$/);
  151:             $r->rflush();
  152:             $r->print($tableheader);
  153:         }
  154:         my ($symb,$timestamp,$student,$action,$machine,$values) =
  155:             map { &Apache::lonnet::unescape($_); } split(',',$line,6);
  156:         my ($title,$src);
  157:         if ($symb =~ m:^/adm/:) {
  158:             $title = $symb;
  159:             $src = $symb;
  160:         } elsif ($symb eq '/prtspool/') {
  161:             $title = "Printout";
  162:             $src = undef;
  163:         } else {
  164:             my $nav_res = $navmap->getBySymb($symb);
  165:             if (defined($nav_res)) {
  166:                 $title = $nav_res->title();
  167:                 $src   = $nav_res->src();
  168:             } else {
  169:                 $title = 'unable to retrieve title';
  170:                 $src   = '/dev/null';
  171:             }
  172:         }
  173:         my $class = '';
  174:         #
  175:         if ($symb eq '/printout/') {
  176:             $class = 'print';
  177:             $title = 'retrieve printout';
  178:         } elsif ($symb =~ m|^/adm/([^/]+)|) {
  179:             $class = $1;
  180:         } elsif ($symb =~ m|^/adm/|) {
  181:             $class = 'adm';
  182:         }
  183:         if ($title eq 'unable to retrieve title') {
  184:             $title =~ s/ /\&nbsp;/g;
  185:             $class = 'warning';
  186:         }
  187:         if (! defined($title) || $title eq '') {
  188:             $title = 'untitled';
  189:             $class = 'warning';
  190:         }
  191:         $r->print('<tr class="'.$class.'">'.
  192:                   '<td><a href="'.$src.'">'.$title.'</a>'.'</td>'.
  193:                   '<td><nobr>'.$timestamp.'</nobr></td>'.
  194:                   '<td>'.$student.'</td>'.
  195:                   '<td>'.$action.'</td>'.
  196:                   '<td>'.$machine.'</td>'.
  197:                   '<td>'.($class?$symb:'').'</td>'.'</tr>'.$/);
  198: #                  '<td>'.$symb.'</td>'.'</tr>'.$/);
  199:     }
  200:     $r->print('</table>'.$/);
  201:     close(ACTIVITYDATA);
  202:     return;
  203: }
  204: 
  205: sub get_student_data {}
  206: sub html_output_student_data {}
  207: sub html_output_class_data {}
  208: 
  209: sub request_data_update {
  210:     my $command = 'prepare activity log';
  211:     my $cid = $ENV{'request.course.id'};
  212:     my $domain = $ENV{'course.'.$cid.'.domain'};
  213:     my $home = $ENV{'course.'.$cid.'.home'};
  214:     my $course = $ENV{'course.'.$cid.'.num'};
  215:     &Apache::lonnet::logthis($command.' '.$course.' '.$domain.' '.$home);
  216:     my $result = &Apache::lonnet::metadata_query($command,$course,$domain,
  217:                                                  [$home]);
  218:     return $result;
  219: }
  220: 
  221: ###################################################################
  222: ###################################################################
  223: 
  224: sub styles {
  225:     return <<END;
  226: <STYLE TYPE="text/css">
  227:     tr.warning   { background-color: red; }
  228:     tr.chat      { background-color: yellow; }
  229:     tr.chatfetch { background-color: yellow; }
  230:     tr.navmaps   { background-color: \#777777; }
  231:     tr.roles     { background-color: \#999999; }
  232:     tr.flip      { background-color: \#BBBBBB; }
  233:     tr.adm       { background-color: green; }
  234:     tr.print     { background-color: blue; }
  235:     tr.printout  { background-color: blue; }
  236: </STYLE>
  237: END
  238: }
  239: 
  240: ###################################################################
  241: ###################################################################
  242: sub handler {
  243:     my $r=shift;
  244:     my $c = $r->connection();
  245:     #
  246:     # Check for overloading here and on the course home server
  247:     my $loaderror=&Apache::lonnet::overloaderror($r);
  248:     if ($loaderror) { return $loaderror; }
  249:     $loaderror=
  250:         &Apache::lonnet::overloaderror
  251:         ($r,
  252:          $ENV{'course.'.$ENV{'request.course.id'}.'.home'});
  253:     if ($loaderror) { return $loaderror; }
  254:     #
  255:     # Check for access
  256:     if (! &Apache::lonnet::allowed('vsa',$ENV{'request.course.id'})) {
  257:         $ENV{'user.error.msg'}=
  258:             $r->uri.":vsa:0:0:Cannot student activity for complete course";
  259:         if (! 
  260:             &Apache::lonnet::allowed('vsa',
  261:                                      $ENV{'request.course.id'}.'/'.
  262:                                      $ENV{'request.course.sec'})) {
  263:             $ENV{'user.error.msg'}=
  264:                 $r->uri.":vsa:0:0:Cannot view student activity with given role";
  265:             return HTTP_NOT_ACCEPTABLE;
  266:         }
  267:     }
  268:     #
  269:     # Send the header
  270:     &Apache::loncommon::no_cache($r);
  271:     &Apache::loncommon::content_type($r,'text/html');
  272:     $r->send_http_header;
  273:     if ($r->header_only) { return OK; }
  274:     #
  275:     # Extract form elements from query string
  276:     &Apache::loncommon::get_unprocessed_cgi($ENV{'QUERY_STRING'},
  277:                                             ['selected_student']);
  278:     #
  279:     # We will almost always need this...
  280:     my $navmap = Apache::lonnavmaps::navmap->new();
  281:     # 
  282:     &Apache::lonhtmlcommon::clear_breadcrumbs();
  283:     &Apache::lonhtmlcommon::add_breadcrumb({href=>'/adm/studentactivity',
  284:                                             title=>'Student Activity',
  285:                                             text =>'Student Activity',
  286:                                             faq=>139,
  287:                                             bug=>'instructor interface'});
  288:     #
  289:     # Give the LON-CAPA page header
  290:     $r->print('<html><head>'.&styles.'<title>'.
  291:               &mt('Student Activity').
  292:               "</title></head>\n".
  293:               &Apache::loncommon::bodytag('Student Activity').
  294:               &Apache::lonhtmlcommon::breadcrumbs(undef,'Student Activity'));
  295:     $r->rflush();
  296:     #
  297:     # Begin form output
  298:     $r->print('<form name="trackstudent" method="post" action="/adm/trackstudent">');
  299:     $r->print('<br />');
  300:     $r->print('<div name="statusline">'.
  301:               &mt('Status:[_1]',
  302:                   '<input type="text" name="status" size="60" value="" />').
  303:               '</div>');
  304:     $r->rflush();
  305:     my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
  306:         ($r,&mt('Student Activity Retrieval'),
  307:          &mt('Student Activity Retrieval'),undef,'inline',undef,
  308:          'trackstudent','status');
  309:     &Apache::lonhtmlcommon::Update_PrgWin
  310:         ($r,\%prog_state,&mt('Contacting course home server'));
  311:     #
  312:     my $result = &request_data_update();
  313:     if (ref($result) eq 'HASH') {
  314:         $result = join(' ',map { $_.'=>'.$result->{$_}; } keys(%$result));
  315:     }
  316:     &Apache::lonnet::logthis('result from request_data_update: '.$result);
  317:     #
  318:     if (! exists($ENV{'form.selected_student'})) {
  319:         # For now, just show all the data, in the future allow selection of
  320:         # a student
  321:         &get_all_data($r,\%prog_state,$navmap);
  322:     } else {
  323:         # For now, just show all the data instead of limiting it to one student
  324:         &get_all_data($r,\%prog_state,$navmap);
  325:     }
  326:     #
  327:     &Apache::lonhtmlcommon::Update_PrgWin($r,\%prog_state,&mt('Done'));
  328:     &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
  329:     #
  330:     $r->print("</form>\n");
  331:     $r->print("</body>\n</html>\n");
  332:     $r->rflush();
  333:     #
  334:     return OK;
  335: }
  336: 
  337: 1;
  338: 
  339: #######################################################
  340: #######################################################
  341: 
  342: =pod
  343: 
  344: =back
  345: 
  346: =cut
  347: 
  348: #######################################################
  349: #######################################################
  350: 
  351: __END__
  352: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>