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/ /\ /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>