Annotation of loncom/interface/lonchart.pm, revision 1.45
1.1 www 1: # The LearningOnline Network with CAPA
1.25 minaeibi 2: # (Publication Handler
3: #
1.45 ! stredwic 4: # $Id: lonchart.pm,v 1.44 2002/06/28 18:06:14 stredwic Exp $
1.25 minaeibi 5: #
6: # Copyright Michigan State University Board of Trustees
7: #
8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
9: #
10: # LON-CAPA is free software; you can redistribute it and/or modify
11: # it under the terms of the GNU General Public License as published by
12: # the Free Software Foundation; either version 2 of the License, or
13: # (at your option) any later version.
14: #
15: # LON-CAPA is distributed in the hope that it will be useful,
16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18: # GNU General Public License for more details.
19: #
20: # You should have received a copy of the GNU General Public License
21: # along with LON-CAPA; if not, write to the Free Software
22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23: #
24: # /home/httpd/html/adm/gpl.txt
25: #
26: # http://www.lon-capa.org/
27: #
1.1 www 28: # Homework Performance Chart
29: #
30: # (Navigate Maps Handler
31: #
32: # (Page Handler
33: #
34: # (TeX Content Handler
1.27 minaeibi 35: # YEAR=2000
1.1 www 36: # 05/29/00,05/30 Gerd Kortemeyer)
37: # 08/30,08/31,09/06,09/14,09/15,09/16,09/19,09/20,09/21,09/23,
38: # 10/02,10/10,10/14,10/16,10/18,10/19,10/31,11/6,11/14,11/16 Gerd Kortemeyer)
1.27 minaeibi 39: # YEAR=2001
1.14 minaeibi 40: # 3/1/1,6/1,17/1,29/1,30/1,31/1 Gerd Kortemeyer)
1.5 minaeibi 41: # 7/10/01 Behrouz Minaei
1.6 www 42: # 9/8 Gerd Kortemeyer
1.27 minaeibi 43: # 10/1, 10/19, 11/17, 11/22, 11/24, 11/28 12/18 Behrouz Minaei
44: # YEAR=2002
1.33 minaeibi 45: # 2/1, 2/6, 2/19, 2/28 Behrouz Minaei
1.26 minaeibi 46: #
47: ###
1.1 www 48:
49: package Apache::lonchart;
50:
51: use strict;
52: use Apache::Constants qw(:common :http);
53: use Apache::lonnet();
1.28 albertel 54: use Apache::loncommon();
1.1 www 55: use HTML::TokeParser;
56: use GDBM_File;
57:
1.44 stredwic 58: #my $jr;
59: # ----- FORMAT PRINT DATA ----------------------------------------------
1.1 www 60:
1.44 stredwic 61: sub FormatStudentInformation {
62: my ($cache,$name,$studentInformation,$spacePadding)=@_;
63: my $Str='<pre>';
64:
65: foreach (@$studentInformation) {
66: my $data=$cache->{$name.':'.$_};
67: $Str .= $data;
68:
69: my @dataLength=split(//,$data);
70: my $length=scalar @dataLength;
71: $Str .= (' 'x($cache->{$_.'Length'}-$length));
72: $Str .= $spacePadding;
73: }
74:
75: return $Str;
76: }
77:
78: sub FormatStudentData {
79: my ($name,$coid,$studentInformation,$spacePadding,$ChartDB)=@_;
1.43 stredwic 80: my ($sname,$sdom) = split(/\:/,$name);
81: my $Str;
1.44 stredwic 82: my %CacheData;
1.43 stredwic 83:
1.44 stredwic 84: unless(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_READER,0640)) {
85: return '';
86: }
1.43 stredwic 87: # Handle Student information ------------------------------------------
1.44 stredwic 88: # Handle user data
89: $Str=&FormatStudentInformation(\%CacheData, $name, $studentInformation,
90: $spacePadding);
91:
1.43 stredwic 92: # Handle errors
1.44 stredwic 93: if($CacheData{$name.':error'} =~ /environment/) {
94: untie(%CacheData);
95: $Str .= '</pre>';
96: return $Str;
97: # my $errorMessage = $CacheData{$name.':error'};
1.43 stredwic 98: # return '<td>'.$sname.'</td><td>'.$sdom.
99: # '</td><td><font color="#000088">'.$errorMessage.'</font></td>';
1.44 stredwic 100: }
1.43 stredwic 101:
1.44 stredwic 102: if($CacheData{$name.':error'} =~ /course/) {
103: untie(%CacheData);
104: $Str .= '</pre>';
1.40 stredwic 105: return $Str;
1.43 stredwic 106: # my $errorMessage = 'May have no course data or '.
1.44 stredwic 107: # $CacheData{$name.':error'};
1.43 stredwic 108: # return '<td>'.$sname.'</td><td>'.$sdom.
109: # '</td><td><font color="#000088">'.$errorMessage.'</font></td>';
1.40 stredwic 110: }
111:
1.43 stredwic 112: # Handle problem data ------------------------------------------------
1.44 stredwic 113: my $Version;
114: my $problemsCorrect = 0;
115: my $totalProblems = 0;
116: my $problemsSolved = 0;
117: my $numberOfParts = 0;
118: foreach my $sequence (split(/\:/,$CacheData{'orderedSequences'})) {
119: my $characterCount=0;
120: foreach my $problemID (split(/\:/,$CacheData{$sequence.':problems'})) {
121: my $problem = $CacheData{$problemID.':problem'};
122: my $LatestVersion = $CacheData{$name.":version:$problem"};
123:
124: if(!$LatestVersion) {
125: foreach my $part (split(/\:/,$CacheData{$sequence.':'.
126: $problemID.
127: ':parts'})) {
128: $Str .= ' ';
129: $totalProblems++;
130: $characterCount++;
131: }
132: next;
133: }
134:
135: my %partData=undef;
136: #initialize data, displays skips correctly
137: foreach my $part (split(/\:/,$CacheData{$sequence.':'.
138: $problemID.
139: ':parts'})) {
140: $partData{$part.':tries'}=0;
141: $partData{$part.':code'}=' ';
142: }
143: for(my $Version=1; $Version<=$LatestVersion; $Version++) {
144: foreach my $part (split(/\:/,$CacheData{$sequence.':'.
145: $problemID.
146: ':parts'})) {
147:
148: if(!defined($CacheData{$name.":$Version:$problem".
149: ":resource.$part.solved"})) {
150: next;
151: }
152:
153: my $tries=0;
154: my $code=' ';
155:
156: $tries = $CacheData{$name.":$Version:$problem".
157: ":resource.$part.tries"};
158: $partData{$part.':tries'}=($tries) ? $tries : 0;
159:
160: my $val = $CacheData{$name.":$Version:$problem".
161: ":resource.$part.solved"};
162: if ($val eq 'correct_by_student') {$code = '*';}
163: elsif ($val eq 'correct_by_override') {$code = '+';}
164: elsif ($val eq 'incorrect_attempted') {$code = '.';}
165: elsif ($val eq 'incorrect_by_override'){$code = '-';}
166: elsif ($val eq 'excused') {$code = 'x';}
167: elsif ($val eq 'ungraded_attempted') {$code = '#';}
168: else {$code = ' ';}
169: $partData{$part.':code'}=$code;
170: }
171: }
172:
173: $Str.='<a href="/adm/grades?symb='.
174: &Apache::lonnet::escape($problem).
175: '&student='.$sname.'&domain='.$sdom.'&command=submission">';
176: foreach(split(/\:/,$CacheData{$sequence.':'.$problemID.
177: ':parts'})) {
178: if($partData{$_.':code'} eq '*') {
179: $problemsCorrect++;
180: if (($partData{$_.':tries'}<10) &&
181: ($partData{$_.':tries'} ne '')) {
182: $partData{$_.':code'}=$partData{$_.':tries'};
183: }
184: } elsif($partData{$_.':code'} eq '+') {
185: $problemsCorrect++;
186: }
187:
188: $Str .= $partData{$_.':code'};
189: $characterCount++;
190:
191: if($partData{$_.':code'} ne 'x') {
192: $totalProblems++;
193: }
194: }
195: $Str.='</a>';
196: }
197:
198: my $spacesNeeded=$CacheData{$sequence.':columnWidth'}-$characterCount;
199: $spacesNeeded -= 3;
200: $Str .= (' 'x$spacesNeeded);
201:
202: my $outputProblemsCorrect = sprintf( "%3d", $problemsCorrect );
203: $Str .= '<font color="#007700">'.$outputProblemsCorrect.'</font>';
204: $problemsSolved += $problemsCorrect;
205: $problemsCorrect=0;
206:
207: $Str .= $spacePadding;
208: }
1.11 minaeibi 209:
1.44 stredwic 210: $Str .= '<font color="#000088">'.$problemsSolved.
211: ' / '.$totalProblems.'</font></pre>';
1.39 stredwic 212:
1.44 stredwic 213: untie(%CacheData);
214: return $Str;
215: }
1.43 stredwic 216:
1.44 stredwic 217: sub CreateTableHeadings {
218: my ($CacheData,$studentInformation,$headings,$spacePadding)=@_;
219: my $Str='<pre>';
1.43 stredwic 220:
1.44 stredwic 221: for(my $index=0; $index<(scalar @$headings); $index++) {
222: my $data=$$headings[$index];
223: $Str .= $data;
224:
225: my @dataLength=split(//,$data);
226: my $length=scalar @dataLength;
227: $Str .= (' 'x($CacheData->{$$studentInformation[$index].'Length'}-
228: $length));
229: $Str .= $spacePadding;
230: }
231:
232: foreach my $sequence (split(/\:/,$CacheData->{'orderedSequences'})) {
233: $Str .= $CacheData->{$sequence.':title'};
234: my @titleLength=split(//,$CacheData->{$sequence.':title'});
235: my $leftover=$CacheData->{$sequence.':columnWidth'}-
236: (scalar @titleLength);
237: $Str .= (' 'x$leftover);
238: $Str .= $spacePadding;
1.1 www 239: }
1.39 stredwic 240:
1.44 stredwic 241: $Str .= 'Total Solved/Total Problems';
242: $Str .= '</pre>';
1.11 minaeibi 243:
1.43 stredwic 244: return $Str;
245: }
246:
247: sub CreateForm {
248: my $OpSel1='';
249: my $OpSel2='';
250: my $OpSel3='';
251: my $Status = $ENV{'form.status'};
252: if ( $Status eq 'Any' ) { $OpSel3='selected'; }
253: elsif ($Status eq 'Expired' ) { $OpSel2 = 'selected'; }
254: else { $OpSel1 = 'selected'; }
255:
1.44 stredwic 256: my $Ptr = '<form name="stat" method="post" action="/adm/chart" >'."\n";
1.43 stredwic 257: $Ptr .= '<b> Sort by: </b>'."\n";
258: $Ptr .= ' ';
1.44 stredwic 259: $Ptr .= '<input type="submit" name="sort" value="User Name" />'."\n";
1.43 stredwic 260: $Ptr .= ' ';
1.44 stredwic 261: $Ptr .= '<input type="submit" name="sort" value="Last Name" />'."\n";
1.43 stredwic 262: $Ptr .= ' ';
1.44 stredwic 263: $Ptr .= '<input type="submit" name="sort" value="Section"/>'."\n";
1.43 stredwic 264: $Ptr .= '<br><br>';
265: $Ptr .= '<b> Student Status: </b>'."\n".
266: '<select name="status">'.
267: '<option '.$OpSel1.' >Active</option>'."\n".
268: '<option '.$OpSel2.' >Expired</option>'."\n".
269: '<option '.$OpSel3.' >Any</option> </select> '."\n";
1.45 ! stredwic 270: $Ptr .= '<br><br>';
! 271: $Ptr .= '<input type="submit" name="sort" value="Recalculate Chart"/>';
! 272: $Ptr .= "\n";
1.43 stredwic 273: $Ptr .= ' ';
1.45 ! stredwic 274: $Ptr .= '<input type="submit" name="sort" value="Refresh Chart"/>';
1.44 stredwic 275: $Ptr .= "\n";
1.43 stredwic 276: $Ptr .= '</form>'."\n";
1.44 stredwic 277:
278: return $Ptr;
279: }
280:
281: sub CreateLegend {
282: my $Str = '<h1>'.$ENV{'course.'.$ENV{'request.course.id'}.'.description'}.
283: '</h1><h3>'.localtime().
284: "</h3><p><pre>1..9: correct by student in 1..9 tries\n".
285: " *: correct by student in more than 9 tries\n".
286: " +: correct by override\n".
287: " -: incorrect by override\n".
288: " .: incorrect attempted\n".
289: " #: ungraded attempted\n".
290: " : not attempted\n".
291: " x: excused</pre><p>";
292: return $Str;
293: }
294:
295: sub StartDocument {
296: my $Str = '';
297: $Str .= '<html>';
298: $Str .= '<head><title>';
299: $Str .= 'LON-CAPA Assessment Chart</title></head>';
300: $Str .= '<body bgcolor="#FFFFFF">';
301: $Str .= '<script>window.focus();</script>';
302: $Str .= '<img align=right src=/adm/lonIcons/lonlogos.gif>';
303: $Str .= '<h1>Assessment Chart</h1>';
304:
305: return $Str;
306: }
307:
308: # ----- END FORMAT PRINT DATA ------------------------------------------
309:
310: # ----- DOWNLOAD INFORMATION -------------------------------------------
311:
312: sub DownloadPrerequisiteData {
313: my ($courseID, $c)=@_;
314: my ($courseDomain,$courseNumber)=split(/\_/,$courseID);
315:
316: my %classlist=&Apache::lonnet::dump('classlist',$courseDomain,
317: $courseNumber);
318: my ($checkForError)=keys (%classlist);
319: if($checkForError =~ /^(con_lost|error|no_such_host)/i) {
320: return \%classlist;
321: }
322:
323: foreach my $name (keys(%classlist)) {
324: if($c->aborted()) {
325: $classlist{'error'}='aborted';
326: return \%classlist;
327: }
328:
329: my ($studentName,$studentDomain) = split(/\:/,$name);
330: # Download student environment data, specifically the full name and id.
331: my %studentInformation=&Apache::lonnet::get('environment',
332: ['lastname','generation',
333: 'firstname','middlename',
334: 'id'],
335: $studentDomain,
336: $studentName);
337: $classlist{$name.':studentInformation'}=\%studentInformation;
338:
339: if($c->aborted()) {
340: $classlist{'error'}='aborted';
341: return \%classlist;
342: }
343:
344: #Section
345: my %section=&Apache::lonnet::dump('roles',$studentDomain,$studentName);
346: $classlist{$name.':section'}=\%section;
347: }
348:
349: return \%classlist;
1.1 www 350: }
351:
1.44 stredwic 352: sub DownloadStudentCourseInformation {
353: my ($name,$courseID)=@_;
354: my ($studentName,$studentDomain) = split(/\:/,$name);
355:
356: # Download student course data
357: my %courseData=&Apache::lonnet::dump($courseID,$studentDomain,
358: $studentName);
359: return \%courseData;
360: }
361:
362: # ----- END DOWNLOAD INFORMATION ---------------------------------------
363:
364: # ----- END PROCESSING FUNCTIONS ---------------------------------------
365:
366: sub ProcessTopResourceMap {
367: my ($ChartDB,$c)=@_;
368: my %hash;
369: my $fn=$ENV{'request.course.fn'};
370: if(-e "$fn.db") {
371: my $tieTries=0;
372: while($tieTries < 3) {
373: if(tie(%hash,'GDBM_File',"$fn.db",&GDBM_READER,0640)) {
374: last;
375: }
376: $tieTries++;
377: sleep 1;
1.43 stredwic 378: }
1.44 stredwic 379: if($tieTries >= 3) {
380: return 'Coursemap undefined.';
381: }
382: } else {
383: return 'Can not open Coursemap.';
1.43 stredwic 384: }
385:
1.44 stredwic 386: my %CacheData;
387: unless(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
388: untie(%hash);
389: return 'Could not tie cache hash.';
390: }
391:
392: my (@sequences, @currentResource, @finishResource);
393: my ($currentSequence, $currentResourceID, $lastResourceID);
394:
395: $currentResourceID=$hash{'ids_/res/'.$ENV{'request.course.uri'}};
396: $lastResourceID=-1;
397: $currentSequence=-1;
398: my $topLevelSequenceNumber = $currentSequence;
399:
400: while(1) {
401: if($c->aborted()) {
402: last;
403: }
404: # HANDLE NEW SEQUENCE!
405: #if page || sequence
406: if(defined($hash{'map_pc_'.$hash{'src_'.$currentResourceID}})) {
407: push(@sequences, $currentSequence);
408: push(@currentResource, $currentResourceID);
409: push(@finishResource, $lastResourceID);
410:
411: $currentSequence=$hash{'map_pc_'.$hash{'src_'.$currentResourceID}};
412: $lastResourceID=$hash{'map_finish_'.
413: $hash{'src_'.$currentResourceID}};
414: $currentResourceID=$hash{'map_start_'.
415: $hash{'src_'.$currentResourceID}};
416:
417: if(!($currentResourceID) || !($lastResourceID)) {
418: $currentSequence=pop(@sequences);
419: $currentResourceID=pop(@currentResource);
420: $lastResourceID=pop(@finishResource);
421: if($currentSequence eq $topLevelSequenceNumber) {
422: last;
423: }
424: }
425: }
426:
427: # Handle gradable resources: exams, problems, etc
428: $currentResourceID=~/(\d+)\.(\d+)/;
429: my $partA=$1;
430: my $partB=$2;
431: if($hash{'src_'.$currentResourceID}=~
432: /\.(problem|exam|quiz|assess|survey|form)$/ &&
433: $partA eq $currentSequence) {
434: my $Problem = &Apache::lonnet::symbclean(
435: &Apache::lonnet::declutter($hash{'map_id_'.$partA}).
436: '___'.$partB.'___'.
437: &Apache::lonnet::declutter($hash{'src_'.
438: $currentResourceID}));
439:
440: $CacheData{$currentResourceID.':problem'}=$Problem;
441: if(!defined($CacheData{$currentSequence.':problems'})) {
442: $CacheData{$currentSequence.':problems'}=$currentResourceID;
443: } else {
444: $CacheData{$currentSequence.':problems'}.=
445: ':'.$currentResourceID;
446: }
447:
448: #Get Parts for problem
449: my $meta=$hash{'src_'.$currentResourceID};
450: foreach (split(/\,/,&Apache::lonnet::metadata($meta,'keys'))) {
451: if($_=~/^stores\_(\d+)\_tries$/) {
452: my $Part=&Apache::lonnet::metadata($meta,$_.'.part');
453: if(!defined($CacheData{$currentSequence.':'.
454: $currentResourceID.':parts'})) {
455: $CacheData{$currentSequence.':'.$currentResourceID.
456: ':parts'}=$Part;
457: } else {
458: $CacheData{$currentSequence.':'.$currentResourceID.
459: ':parts'}.=':'.$Part;
460: }
461: }
462: }
463: }
464:
465: #if resource == finish resource
466: if($currentResourceID eq $lastResourceID) {
467: #pop off last resource of sequence
468: $currentResourceID=pop(@currentResource);
469: #pop to get last resource in previous sequence
470: $currentResourceID=pop(@currentResource);
471: $lastResourceID=pop(@finishResource);
472:
473: if(defined($CacheData{$currentSequence.':problems'})) {
474: # Capture sequence information here
475: if(!defined($CacheData{'orderedSequences'})) {
476: $CacheData{'orderedSequences'}=$currentSequence;
477: } else {
478: $CacheData{'orderedSequences'}.=':'.$currentSequence;
479: }
480:
481: $CacheData{$currentSequence.':title'}=
482: $hash{'title_'.$currentResourceID};
483:
484: my $totalProblems=0;
485: foreach (split(/\:/,$CacheData{$currentSequence.
486: ':problems'})) {
487: foreach ($CacheData{$currentSequence.':'.$_.':parts'}) {
488: $totalProblems++;
489: }
490: }
491: my @titleLength=split(//,$CacheData{$currentSequence.
492: ':title'});
493: # $extra is 3 for problems correct and 3 for space
494: # between problems correct and problem output
495: my $extra = 6;
496: if(($totalProblems + $extra) > (scalar @titleLength)) {
497: $CacheData{$currentSequence.':columnWidth'}=
498: $totalProblems + $extra;
499: } else {
500: $CacheData{$currentSequence.':columnWidth'}=
501: (scalar @titleLength);
502: }
503: }
504:
505: $currentSequence=pop(@sequences);
506: if($currentSequence eq $topLevelSequenceNumber) {
507: last;
508: }
509: #else
510: }
1.43 stredwic 511:
1.44 stredwic 512: # MOVE!!!
513: #move to next resource
514: unless(defined($hash{'to_'.$currentResourceID})) {
515: # big problem, need to handle. Next is probably wrong
516: last;
517: }
518: my @nextResources=();
519: foreach (split(/\,/,$hash{'to_'.$currentResourceID})) {
520: push(@nextResources, $hash{'goesto_'.$_});
521: }
522: pop(@currentResource);
523: push(@currentResource, @nextResources);
524: # Set the next resource to be popped(processed)
525: $currentResourceID=$currentResource[-1];
526: }
1.5 minaeibi 527:
1.44 stredwic 528: unless (untie(%hash)) {
529: &Apache::lonnet::logthis("<font color=blue>WARNING: ".
530: "Could not untie coursemap $fn (browse)".
531: ".</font>");
532: }
1.1 www 533:
1.44 stredwic 534: unless (untie(%CacheData)) {
535: &Apache::lonnet::logthis("<font color=blue>WARNING: ".
536: "Could not untie Cache Hash (browse)".
537: ".</font>");
1.1 www 538: }
1.44 stredwic 539:
540: return 'OK';
1.1 www 541: }
1.33 minaeibi 542:
1.44 stredwic 543: sub ProcessSection {
544: my ($sectionData, $courseid,$ActiveFlag)=@_;
1.33 minaeibi 545: $courseid=~s/\_/\//g;
546: $courseid=~s/^(\w)/\/$1/;
1.39 stredwic 547:
1.41 albertel 548: my $cursection='-1';
549: my $oldsection='-1';
550: my $status='Expired';
1.44 stredwic 551: my $section='';
552: foreach my $key (keys (%$sectionData)) {
553: my $value = $sectionData->{$key};
1.33 minaeibi 554: if ($key=~/^$courseid(?:\/)*(\w+)*\_st$/) {
1.44 stredwic 555: $section=$1;
556: if($key eq $courseid.'_st') {
557: $section='';
558: }
1.39 stredwic 559: my ($dummy,$end,$start)=split(/\_/,$value);
1.41 albertel 560: my $now=time;
561: my $notactive=0;
1.43 stredwic 562: if ($start) {
563: if($now<$start) {
564: $notactive=1;
565: }
566: }
567: if($end) {
568: if ($now>$end) {
569: $notactive=1;
570: }
571: }
572: if($notactive == 0) {
573: $status='Active';
574: $cursection=$section;
1.44 stredwic 575: last;
1.43 stredwic 576: }
577: if($notactive == 1) {
578: $oldsection=$section;
579: }
580: }
581: }
582: if($status eq $ActiveFlag) {
583: if($cursection eq '-1') {
584: return $oldsection;
585: }
586: return $cursection;
587: }
588: if($ActiveFlag eq 'Any') {
589: if($cursection eq '-1') {
590: return $oldsection;
591: }
592: return $cursection;
1.41 albertel 593: }
1.36 minaeibi 594: return '-1';
1.33 minaeibi 595: }
596:
1.44 stredwic 597: sub ProcessStudentInformation {
598: my ($CacheData,$studentInformation,$section,$date,$name,$courseID,$c)=@_;
599: my ($studentName,$studentDomain) = split(/\:/,$name);
600:
601: $CacheData->{$name.':username'}=$studentName;
602: $CacheData->{$name.':domain'}=$studentDomain;
603: $CacheData->{$name.':date'}=$date;
604:
605: my ($checkForError)=keys(%$studentInformation);
606: if($checkForError =~ /^(con_lost|error|no_such_host)/i) {
607: $CacheData->{$name.':error'}=
608: 'Could not download student environment data.';
609: $CacheData->{$name.':fullname'}='';
610: $CacheData->{$name.':id'}='';
611: } else {
612: $CacheData->{$name.':fullname'}=&ProcessFullName(
613: $studentInformation->{'lastname'},
614: $studentInformation->{'generation'},
615: $studentInformation->{'firstname'},
616: $studentInformation->{'middlename'});
617: $CacheData->{$name.':id'}=$studentInformation->{'id'};
618: }
619:
620: # Get student's section number
621: my $sec=&ProcessSection($section, $courseID, $ENV{'form.status'});
622: if($sec != -1) {
623: $CacheData->{$name.':section'}=$sec;
624: } else {
625: $CacheData->{$name.':section'}='';
626: }
627:
628: return 0;
629: }
630:
631: sub ProcessClassList {
632: my ($classlist,$courseID,$ChartDB,$c)=@_;
633: my @names=();
634:
635: my %CacheData;
636: if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
637: foreach my $name (keys(%$classlist)) {
638: if($name =~ /\:section/ || $name =~ /\:studentInformation/) {
639: next;
640: }
641: if($c->aborted()) {
642: last;
643: }
644: push(@names,$name);
645: &ProcessStudentInformation(
646: \%CacheData,
647: $classlist->{$name.':studentInformation'},
648: $classlist->{$name.':section'},
649: $classlist->{$name},
650: $name,$courseID,$c);
651: }
652:
653: $CacheData{'NamesOfStudents'}=join(":::",@names);
654: # $CacheData{'NamesOfStudents'}=&Apache::lonnet::arrayref2str(\@names);
655: untie(%CacheData);
656: }
657:
658: return @names;
659: }
660:
661: # ----- END PROCESSING FUNCTIONS ---------------------------------------
662:
663: # ----- HELPER FUNCTIONS -----------------------------------------------
664:
665: sub SpaceColumns {
666: my ($students,$studentInformation,$headings,$ChartDB)=@_;
667:
668: my %CacheData;
669: if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
670: # Initialize Lengths
671: for(my $index=0; $index<(scalar @$headings); $index++) {
672: my @titleLength=split(//,$$headings[$index]);
673: $CacheData{$$studentInformation[$index].'Length'}=
674: scalar @titleLength;
675: }
676:
677: foreach my $name (@$students) {
678: foreach (@$studentInformation) {
679: my @dataLength=split(//,$CacheData{$name.':'.$_});
680: my $length=scalar @dataLength;
681: if($length > $CacheData{$_.'Length'}) {
682: $CacheData{$_.'Length'}=$length;
683: }
684: }
685: }
686: untie(%CacheData);
687: }
688:
689: return;
690: }
691:
1.43 stredwic 692: sub ProcessFullName {
1.44 stredwic 693: my ($lastname, $generation, $firstname, $middlename)=@_;
1.43 stredwic 694: my $Str = '';
695:
1.44 stredwic 696: if($lastname ne '') {
697: $Str .= $lastname.' ';
698: if($generation ne '') {
699: $Str .= $generation;
1.43 stredwic 700: } else {
701: chop($Str);
702: }
703: $Str .= ', ';
1.44 stredwic 704: if($firstname ne '') {
705: $Str .= $firstname.' ';
1.43 stredwic 706: }
1.44 stredwic 707: if($middlename ne '') {
708: $Str .= $middlename;
1.40 stredwic 709: } else {
1.43 stredwic 710: chop($Str);
1.44 stredwic 711: if($firstname eq '') {
1.43 stredwic 712: chop($Str);
1.31 minaeibi 713: }
1.30 minaeibi 714: }
1.43 stredwic 715: } else {
1.44 stredwic 716: if($firstname ne '') {
717: $Str .= $firstname.' ';
1.43 stredwic 718: }
1.44 stredwic 719: if($middlename ne '') {
720: $Str .= $middlename.' ';
1.43 stredwic 721: }
1.44 stredwic 722: if($generation ne '') {
723: $Str .= $generation;
1.43 stredwic 724: } else {
725: chop($Str);
726: }
727: }
728:
729: return $Str;
730: }
1.30 minaeibi 731:
1.44 stredwic 732: sub SortStudents {
733: my ($CacheData)=@_;
734: my @students = split(/:::/,$CacheData->{'NamesOfStudents'});
735: # my @students=&Apache::lonnet::str2array($CacheData->{'NamesOfStudents'});
736:
737: my @sorted1Students=();
738: foreach (@students) {
739: my ($end,$start)=split(/\:/,$CacheData->{$_.':date'});
740: my $active=1;
741: my $now=time;
742: my $Status=$ENV{'form.status'};
743: $Status = ($Status) ? $Status : 'Active';
744: if((($end) && $now > $end) && (($Status eq 'Active'))) {
745: $active=0;
746: }
747: if(($Status eq 'Expired') && ($end == 0 || $now < $end)) {
748: $active=0;
749: }
750: if($active) {
751: push(@sorted1Students, $_);
752: }
1.43 stredwic 753: }
1.1 www 754:
1.43 stredwic 755: my $Pos = $ENV{'form.sort'};
756: my %sortData;
757: if($Pos eq 'Last Name') {
1.44 stredwic 758: for(my $index=0; $index<scalar @sorted1Students; $index++) {
759: $sortData{$CacheData->{$sorted1Students[$index].':fullname'}}=
760: $sorted1Students[$index];
1.43 stredwic 761: }
762: } elsif($Pos eq 'Section') {
1.44 stredwic 763: for(my $index=0; $index<scalar @sorted1Students; $index++) {
764: $sortData{$CacheData->{$sorted1Students[$index].':section'}.
765: $sorted1Students[$index]}=$sorted1Students[$index];
1.43 stredwic 766: }
767: } else {
768: # Sort by user name
1.44 stredwic 769: for(my $index=0; $index<scalar @sorted1Students; $index++) {
770: $sortData{$sorted1Students[$index]}=$sorted1Students[$index];
1.43 stredwic 771: }
772: }
773:
774: my @order = ();
775: foreach my $key (sort keys(%sortData)) {
776: push (@order,$sortData{$key});
777: }
1.33 minaeibi 778:
1.43 stredwic 779: return @order;
1.30 minaeibi 780: }
1.1 www 781:
1.44 stredwic 782: sub TestCacheData {
783: my ($ChartDB)=@_;
784: my $isCached=-1;
785: my %testData;
786: my $tieTries=0;
1.43 stredwic 787:
1.44 stredwic 788: if ((-e "$ChartDB") && ($ENV{'form.sort'} ne 'Recalculate Chart')) {
789: $isCached = 1;
790: } else {
791: $isCached = 0;
1.43 stredwic 792: }
793:
1.44 stredwic 794: while($tieTries < 3) {
795: my $result=0;
796: if($isCached) {
797: $result=tie(%testData,'GDBM_File',$ChartDB,&GDBM_READER,0640);
798: } else {
799: $result=tie(%testData,'GDBM_File',$ChartDB,&GDBM_NEWDB,0640);
800: }
801: if($result) {
802: last;
803: }
804: $tieTries++;
805: sleep 1;
806: }
807: if($tieTries >= 3) {
808: return -1;
1.43 stredwic 809: }
810:
1.44 stredwic 811: untie(%testData);
1.30 minaeibi 812:
1.44 stredwic 813: return $isCached;
1.43 stredwic 814: }
1.30 minaeibi 815:
1.44 stredwic 816: sub ExtractStudentData {
817: my ($courseData, $name, $ChartDB)=@_;
818:
819: my %CacheData;
820: if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
821: my ($checkForError) = keys(%$courseData);
822: if($checkForError =~ /^(con_lost|error|no_such_host)/i) {
823: $CacheData{$name.':error'}='Could not download course data.';
824: } else {
825: foreach my $key (keys (%$courseData)) {
826: $CacheData{$name.':'.$key}=$courseData->{$key};
827: }
828: }
829: untie(%CacheData);
1.30 minaeibi 830: }
1.1 www 831:
1.44 stredwic 832: return;
833: }
834:
835: # ----- END HELPER FUNCTIONS --------------------------------------------
836:
837: sub BuildChart {
838: my ($r)=@_;
839: my $c = $r->connection;
1.1 www 840:
1.44 stredwic 841: # Start the lonchart document
842: $r->content_type('text/html');
843: $r->send_http_header;
844: $r->print(&StartDocument());
845: $r->rflush();
1.43 stredwic 846:
1.44 stredwic 847: # Test for access to the CacheData
848: my $isCached=0;
1.43 stredwic 849: my $cid=$ENV{'request.course.id'};
850: my $ChartDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".
851: "_$ENV{'user.domain'}_$cid\_chart.db";
1.44 stredwic 852:
853: $isCached=&TestCacheData($ChartDB);
854: if($isCached < 0) {
855: $r->print("Unable to tie hash to db file");
856: $r->rflush();
857: return;
858: }
859:
860: # Download class list information if not using cached data
861: my @students=();
862: my @studentInformation=('username','domain','section','id','fullname');
863: my @headings=('User Name','Domain','Section','PID','Full Name');
864: my $spacePadding=' ';
865: if(!$isCached) {
866: my $processTopResourceMapReturn=&ProcessTopResourceMap($ChartDB,$c);
867: if($processTopResourceMapReturn ne 'OK') {
868: $r->print($processTopResourceMapReturn);
869: return;
870: }
871: if($c->aborted()) { return; }
872: my $classlist=&DownloadPrerequisiteData($cid, $c);
873: my ($checkForError)=keys(%$classlist);
874: if($checkForError =~ /^(con_lost|error|no_such_host)/i ||
875: defined($classlist->{'error'})) {
876: return;
877: }
878: if($c->aborted()) { return; }
879: @students=&ProcessClassList($classlist,$cid,$ChartDB,$c);
880: if($c->aborted()) { return; }
881: &SpaceColumns(\@students,\@studentInformation,\@headings,
882: $ChartDB);
883: if($c->aborted()) { return; }
884: }
885:
886: # Sort students and print out table desciptive data
887: my %CacheData;
888: if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_READER,0640)) {
889: if(!$c->aborted()) { @students=&SortStudents(\%CacheData); }
890: if(!$c->aborted()) { $r->print(&CreateLegend()); }
891: if(!$c->aborted()) { $r->print(&CreateForm()); }
892: if(!$c->aborted()) { $r->print('<h3>'.(scalar @students).
893: ' students</h3>'); }
894: if(!$c->aborted()) { $r->rflush(); }
895: if(!$c->aborted()) { $r->print(&CreateTableHeadings(
896: \%CacheData,
897: \@studentInformation,
898: \@headings,
899: $spacePadding)); }
900: untie(%CacheData);
1.43 stredwic 901: } else {
1.44 stredwic 902: $r->print("Init2: Unable to tie hash to db file");
903: return;
1.43 stredwic 904: }
905:
906: my @updateStudentList = ();
1.44 stredwic 907: my $courseData;
908: foreach (@students) {
909: if($c->aborted()) {
910: if(!$isCached &&
911: tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
912: $CacheData{'NamesOfStudents'}=join(":::", @updateStudentList);
913: # $CacheData{'NamesOfStudents'}=
914: # &Apache::lonnet::arrayref2str(\@updateStudentList);
915: untie(%CacheData);
916: }
917: last;
918: }
919:
920: if(!$isCached) {
921: $courseData=&DownloadStudentCourseInformation($_, $cid);
922: if($c->aborted()) { next; }
923: push(@updateStudentList, $_);
924: &ExtractStudentData($courseData, $_, $ChartDB);
925: }
926: $r->print(&FormatStudentData($_, $cid, \@studentInformation,
927: $spacePadding, $ChartDB));
928: $r->rflush();
1.43 stredwic 929: }
930:
1.44 stredwic 931: $r->print('</body></html>');
1.30 minaeibi 932: $r->rflush();
1.1 www 933:
1.43 stredwic 934: return;
1.30 minaeibi 935: }
1.1 www 936:
1.30 minaeibi 937: # ================================================================ Main Handler
1.1 www 938:
1.30 minaeibi 939: sub handler {
1.44 stredwic 940: my $r=shift;
941: # $jr=$r;
942: unless(&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) {
1.30 minaeibi 943: $ENV{'user.error.msg'}=
1.1 www 944: $r->uri.":vgr:0:0:Cannot view grades for complete course";
1.30 minaeibi 945: return HTTP_NOT_ACCEPTABLE;
946: }
1.44 stredwic 947:
948: # Set document type for header only
949: if ($r->header_only) {
950: if($ENV{'browser.mathml'}) {
951: $r->content_type('text/xml');
952: } else {
953: $r->content_type('text/html');
954: }
955: &Apache::loncommon::no_cache($r);
956: $r->send_http_header;
957: return OK;
958: }
959:
960: unless($ENV{'request.course.fn'}) {
961: my $requrl=$r->uri;
962: $ENV{'user.error.msg'}="$requrl:bre:0:0:Course not initialized";
963: return HTTP_NOT_ACCEPTABLE;
964: }
965:
966: &BuildChart($r);
967:
968: return OK;
1.1 www 969: }
970: 1;
971: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>