Annotation of loncom/interface/lonchart.pm, revision 1.60
1.1 www 1: # The LearningOnline Network with CAPA
1.25 minaeibi 2: # (Publication Handler
3: #
1.60 ! stredwic 4: # $Id: lonchart.pm,v 1.59 2002/07/09 15:43:49 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:
1.51 stredwic 49: =pod
50:
1.55 stredwic 51: =head1 NAME
52:
53: lonchart
54:
55: =head1 SYNOPSIS
56:
57: Quick display of students grades for a course in a compressed table format.
58:
59: =head1 DESCRIPTION
60:
61: This module process all student grades for a course and turns them into a
62: table like structure.
63:
64: This is part of the LearningOnline Network with CAPA project
65: described at http://www.lon-capa.org
66:
67: lonchart presents the user with a condensed view all a course's data. The
68: class title, the number of students, and the date for the last update of the
69: displayed data. There is also a legend that describes the chart values.
70:
71: For each valid grade for a student is linked with a submission record for that
72: problem. The ability to add and remove columns of data from the chart was
73: added for reducing the burden of having to scroll through large quantities
74: of data. The interface also allows for sorting of students by username,
75: last name, and section number of class. Active and expired students are
76: also available.
77:
78: The interface is controlled by three primary buttons: Recalculate Chart,
79: Refresh Chart, and Reset Selections. Recalculate Chart will update
80: the chart to the most recent data and keep the display settings for the chart
81: the same. Refresh Chart is used to redisplay the chart after selecting
82: different output formatting. Reset Selections is used to set the chart
83: display options back to default values.
84:
85: =head1 CODE LAYOUT DESCRIPTION
86:
1.59 stredwic 87: The code is broken down into three components: formatting data for printing,
88: helper functions, and the central processing functions. The module is broken
89: into chunks for each component.
1.55 stredwic 90:
91: =head1 PACKAGES USED
92:
93: Apache::Constants qw(:common :http)
94: Apache::lonnet()
95: Apache::loncommon()
96: HTML::TokeParser
97: GDBM_File
98:
1.51 stredwic 99: =cut
100:
1.1 www 101: package Apache::lonchart;
102:
103: use strict;
104: use Apache::Constants qw(:common :http);
105: use Apache::lonnet();
1.28 albertel 106: use Apache::loncommon();
1.59 stredwic 107: use Apache::loncoursedata();
1.1 www 108: use HTML::TokeParser;
109: use GDBM_File;
110:
1.51 stredwic 111: #my $jr;
1.55 stredwic 112:
113: =pod
114:
115: =head1 FORMAT DATA FOR PRINTING
116:
117: =cut
118:
1.44 stredwic 119: # ----- FORMAT PRINT DATA ----------------------------------------------
1.1 www 120:
1.55 stredwic 121: =pod
122:
123: =item &FormatStudentInformation()
124:
125: This function produces a formatted string of the student's information:
126: username, domain, section, full name, and PID.
127:
128: =over 4
129:
130: Input: $cache, $name, $studentInformation, $spacePadding
131:
132: $cache: This is a pointer to a hash that is tied to the cached data
133:
134: $name: The name and domain of the current student in name:domain format
135:
136: $studentInformation: A pointer to an array holding the names used to
137:
138: remove data from the hash. They represent the name of the data to be removed.
139:
140: $spacePadding: Extra spaces that represent the space between columns
141:
142: Output: $Str
143:
144: $Str: Formatted string.
145:
146: =back
147:
148: =cut
149:
1.44 stredwic 150: sub FormatStudentInformation {
1.51 stredwic 151: my ($cache,$name,$studentInformation,$spacePadding)=@_;
1.50 stredwic 152: my $Str='';
1.60 ! stredwic 153: my $data;
1.44 stredwic 154:
1.49 stredwic 155: for(my $index=0; $index<(scalar @$studentInformation); $index++) {
1.59 stredwic 156: if(!&ShouldShowColumn($cache, 'ChartHeading'.$index)) {
1.49 stredwic 157: next;
158: }
1.60 ! stredwic 159: $data=$cache->{$name.':'.$studentInformation->[$index]};
1.44 stredwic 160: $Str .= $data;
161:
162: my @dataLength=split(//,$data);
163: my $length=scalar @dataLength;
1.49 stredwic 164: $Str .= (' 'x($cache->{$studentInformation->[$index].'Length'}-
165: $length));
1.44 stredwic 166: $Str .= $spacePadding;
167: }
168:
169: return $Str;
170: }
171:
1.55 stredwic 172: =pod
173:
174: =item &FormatStudentData()
175:
176: First, FormatStudentInformation is called and prefixes the course information.
177: This function produces a formatted string of the student's course information.
178: Each column of data represents all the problems for a given sequence. For
179: valid grade data, a link is created for that problem to a submission record
180: for that problem.
181:
182: =over 4
183:
184: Input: $name, $studentInformation, $spacePadding, $ChartDB
185:
186: $name: The name and domain of the current student in name:domain format
187:
188: $studentInformation: A pointer to an array holding the names used to
189: remove data from the hash. They represent
190: the name of the data to be removed.
191:
192: $spacePadding: Extra spaces that represent the space between columns
193:
194: $ChartDB: The name of the cached data database which will be tied to that
195: database.
196:
197: Output: $Str
198:
199: $Str: Formatted string that is an entire row of the chart. It is a
200: concatenation of student information and student course information.
201:
202: =back
203:
204: =cut
205:
1.44 stredwic 206: sub FormatStudentData {
1.55 stredwic 207: my ($name,$studentInformation,$spacePadding,$ChartDB)=@_;
1.43 stredwic 208: my ($sname,$sdom) = split(/\:/,$name);
209: my $Str;
1.44 stredwic 210: my %CacheData;
1.43 stredwic 211:
1.44 stredwic 212: unless(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_READER,0640)) {
213: return '';
214: }
1.60 ! stredwic 215:
! 216: # my $section = &Apache::loncoursedata::CheckStatus($name, \%CacheData,
! 217: # $CacheData{'form.Status'});
! 218: # if($section eq 'not found') {
! 219: # untie(%CacheData);
! 220: # return;
! 221: # }
! 222:
1.43 stredwic 223: # Handle Student information ------------------------------------------
1.44 stredwic 224: # Handle user data
225: $Str=&FormatStudentInformation(\%CacheData, $name, $studentInformation,
1.51 stredwic 226: $spacePadding);
1.44 stredwic 227:
1.43 stredwic 228: # Handle errors
1.44 stredwic 229: if($CacheData{$name.':error'} =~ /environment/) {
1.50 stredwic 230: $Str .= '<br>';
1.44 stredwic 231: untie(%CacheData);
232: return $Str;
233: }
1.43 stredwic 234:
1.44 stredwic 235: if($CacheData{$name.':error'} =~ /course/) {
1.50 stredwic 236: $Str .= '<br>';
1.44 stredwic 237: untie(%CacheData);
1.50 stredwic 238: return $Str;
1.40 stredwic 239: }
240:
1.43 stredwic 241: # Handle problem data ------------------------------------------------
1.44 stredwic 242: my $Version;
243: my $problemsCorrect = 0;
244: my $totalProblems = 0;
245: my $problemsSolved = 0;
246: my $numberOfParts = 0;
247: foreach my $sequence (split(/\:/,$CacheData{'orderedSequences'})) {
1.59 stredwic 248: if(!&ShouldShowColumn(\%CacheData, 'ChartSequence'.$sequence)) {
1.49 stredwic 249: next;
250: }
251:
1.44 stredwic 252: my $characterCount=0;
253: foreach my $problemID (split(/\:/,$CacheData{$sequence.':problems'})) {
254: my $problem = $CacheData{$problemID.':problem'};
255: my $LatestVersion = $CacheData{$name.":version:$problem"};
256:
1.58 stredwic 257: # Output blanks for all the parts of this problem if there
258: # is no version information about the current problem.
1.44 stredwic 259: if(!$LatestVersion) {
260: foreach my $part (split(/\:/,$CacheData{$sequence.':'.
261: $problemID.
262: ':parts'})) {
263: $Str .= ' ';
264: $totalProblems++;
265: $characterCount++;
266: }
267: next;
268: }
269:
270: my %partData=undef;
1.58 stredwic 271: # Initialize part data, display skips correctly
272: # Skip refers to when a student made no submissions on that
273: # part/problem.
1.44 stredwic 274: foreach my $part (split(/\:/,$CacheData{$sequence.':'.
275: $problemID.
276: ':parts'})) {
277: $partData{$part.':tries'}=0;
278: $partData{$part.':code'}=' ';
279: }
1.58 stredwic 280:
281: # Looping through all the versions of each part, starting with the
282: # oldest version. Basically, it gets the most recent
283: # set of grade data for each part.
1.44 stredwic 284: for(my $Version=1; $Version<=$LatestVersion; $Version++) {
285: foreach my $part (split(/\:/,$CacheData{$sequence.':'.
286: $problemID.
287: ':parts'})) {
288:
289: if(!defined($CacheData{$name.":$Version:$problem".
290: ":resource.$part.solved"})) {
1.58 stredwic 291: # No grade for this submission, so skip
1.44 stredwic 292: next;
293: }
294:
295: my $tries=0;
296: my $code=' ';
297:
298: $tries = $CacheData{$name.":$Version:$problem".
299: ":resource.$part.tries"};
300: $partData{$part.':tries'}=($tries) ? $tries : 0;
301:
302: my $val = $CacheData{$name.":$Version:$problem".
303: ":resource.$part.solved"};
304: if ($val eq 'correct_by_student') {$code = '*';}
305: elsif ($val eq 'correct_by_override') {$code = '+';}
306: elsif ($val eq 'incorrect_attempted') {$code = '.';}
307: elsif ($val eq 'incorrect_by_override'){$code = '-';}
308: elsif ($val eq 'excused') {$code = 'x';}
309: elsif ($val eq 'ungraded_attempted') {$code = '#';}
310: else {$code = ' ';}
311: $partData{$part.':code'}=$code;
312: }
313: }
314:
1.58 stredwic 315: # All grades (except for versionless parts) are displayed as links
316: # to their submission record. Loop through all the parts for the
317: # current problem in the correct order and prepare the output links
1.44 stredwic 318: $Str.='<a href="/adm/grades?symb='.
319: &Apache::lonnet::escape($problem).
320: '&student='.$sname.'&domain='.$sdom.'&command=submission">';
321: foreach(split(/\:/,$CacheData{$sequence.':'.$problemID.
322: ':parts'})) {
323: if($partData{$_.':code'} eq '*') {
324: $problemsCorrect++;
325: if (($partData{$_.':tries'}<10) &&
326: ($partData{$_.':tries'} ne '')) {
327: $partData{$_.':code'}=$partData{$_.':tries'};
328: }
329: } elsif($partData{$_.':code'} eq '+') {
330: $problemsCorrect++;
331: }
332:
333: $Str .= $partData{$_.':code'};
334: $characterCount++;
335:
336: if($partData{$_.':code'} ne 'x') {
337: $totalProblems++;
338: }
339: }
340: $Str.='</a>';
341: }
342:
1.58 stredwic 343: # Output the number of correct answers for the current sequence.
344: # This part takes up 6 character slots, but is formated right
345: # justified.
1.44 stredwic 346: my $spacesNeeded=$CacheData{$sequence.':columnWidth'}-$characterCount;
347: $spacesNeeded -= 3;
348: $Str .= (' 'x$spacesNeeded);
349:
350: my $outputProblemsCorrect = sprintf( "%3d", $problemsCorrect );
351: $Str .= '<font color="#007700">'.$outputProblemsCorrect.'</font>';
352: $problemsSolved += $problemsCorrect;
353: $problemsCorrect=0;
354:
355: $Str .= $spacePadding;
356: }
1.11 minaeibi 357:
1.58 stredwic 358: # Output the total correct problems over the total number of problems.
359: # I don't like this type of formatting, but it is a solution. Need
360: # a way to dynamically determine the space requirements.
1.51 stredwic 361: my $outputProblemsSolved = sprintf( "%4d", $problemsSolved );
362: my $outputTotalProblems = sprintf( "%4d", $totalProblems );
363: $Str .= '<font color="#000088">'.$outputProblemsSolved.
364: ' / '.$outputTotalProblems.'</font><br>';
1.39 stredwic 365:
1.44 stredwic 366: untie(%CacheData);
367: return $Str;
368: }
1.43 stredwic 369:
1.55 stredwic 370: =pod
371:
372: =item &CreateTableHeadings()
373:
374: This function generates the column headings for the chart.
375:
376: =over 4
377:
378: Inputs: $CacheData, $studentInformation, $headings, $spacePadding
379:
380: $CacheData: pointer to a hash tied to the cached data database
381:
382: $studentInformation: a pointer to an array containing the names of the data
383: held in a column and is used as part of a key into $CacheData
384:
385: $headings: The names of the headings for the student information
386:
387: $spacePadding: The spaces to go between columns
388:
389: Output: $Str
390:
391: $Str: A formatted string of the table column headings.
392:
393: =back
394:
395: =cut
396:
1.44 stredwic 397: sub CreateTableHeadings {
1.51 stredwic 398: my ($CacheData,$studentInformation,$headings,$spacePadding)=@_;
1.53 stredwic 399: my $Str='<tr>';
1.43 stredwic 400:
1.44 stredwic 401: for(my $index=0; $index<(scalar @$headings); $index++) {
1.59 stredwic 402: if(!&ShouldShowColumn($CacheData, 'ChartHeading'.$index)) {
1.49 stredwic 403: next;
404: }
405:
1.53 stredwic 406: $Str .= '<td align="left"><pre>';
1.44 stredwic 407: my $data=$$headings[$index];
408: $Str .= $data;
409:
410: my @dataLength=split(//,$data);
411: my $length=scalar @dataLength;
412: $Str .= (' 'x($CacheData->{$$studentInformation[$index].'Length'}-
413: $length));
414: $Str .= $spacePadding;
1.53 stredwic 415: $Str .= '</pre></td>';
1.44 stredwic 416: }
417:
418: foreach my $sequence (split(/\:/,$CacheData->{'orderedSequences'})) {
1.59 stredwic 419: if(!&ShouldShowColumn($CacheData, 'ChartSequence'.$sequence)) {
1.49 stredwic 420: next;
421: }
422:
1.53 stredwic 423: $Str .= '<td align="left"><pre>';
1.49 stredwic 424: my $name = $CacheData->{$sequence.':title'};
425: $Str .= $name;
1.44 stredwic 426: my @titleLength=split(//,$CacheData->{$sequence.':title'});
427: my $leftover=$CacheData->{$sequence.':columnWidth'}-
428: (scalar @titleLength);
429: $Str .= (' 'x$leftover);
430: $Str .= $spacePadding;
1.53 stredwic 431: $Str .= '</pre></td>';
1.1 www 432: }
1.39 stredwic 433:
1.54 stredwic 434: $Str .= '<td><pre>Total Solved/Total Problems</pre></td>';
1.55 stredwic 435: $Str .= '</tr>';
1.11 minaeibi 436:
1.43 stredwic 437: return $Str;
438: }
439:
1.55 stredwic 440: =pod
441:
442: =item &CreateColumnSelectionBox()
443:
444: If there are columns not being displayed then this selection box is created
445: with a list of those columns. When selections are made and the page
446: refreshed, the columns will be removed from this box and the column is
447: put back in the chart. If there is no columns to select, no row is added
448: to the interface table.
449:
450: =over 4
451: Input: $CacheData, $headings
452:
453:
454: $CacheData: A pointer to a hash tied to the cached data
455:
456: $headings: An array of the names of the columns for the student information.
457: They are used for displaying which columns are missing.
458:
459: Output: $notThere
460:
461: $notThere: The string contains one row of a table. The first column has the
462: name of the selection box. The second contains the selection box
463: which has a size of four.
464:
465: =back
466:
467: =cut
468:
1.49 stredwic 469: sub CreateColumnSelectionBox {
1.55 stredwic 470: my ($CacheData,$headings)=@_;
1.46 stredwic 471:
1.49 stredwic 472: my $missing=0;
1.50 stredwic 473: my $notThere='<tr><td align="right"><b>Select column to view:</b>';
1.49 stredwic 474: my $name;
1.50 stredwic 475: $notThere .= '<td align="left">';
1.59 stredwic 476: $notThere .= '<select name="ChartReselect" size="4" multiple="true">'."\n";
1.46 stredwic 477:
478: for(my $index=0; $index<(scalar @$headings); $index++) {
1.59 stredwic 479: if(&ShouldShowColumn($CacheData, 'ChartHeading'.$index)) {
1.49 stredwic 480: next;
481: }
482: $name = $headings->[$index];
1.59 stredwic 483: $notThere .= '<option value="ChartHeading'.$index.'">';
1.49 stredwic 484: $notThere .= $name.'</option>'."\n";
485: $missing++;
486: }
487:
488: foreach my $sequence (split(/\:/,$CacheData->{'orderedSequences'})) {
1.59 stredwic 489: if(&ShouldShowColumn($CacheData, 'ChartSequence'.$sequence)) {
1.49 stredwic 490: next;
491: }
492: $name = $CacheData->{$sequence.':title'};
1.59 stredwic 493: $notThere .= '<option value="ChartSequence'.$sequence.'">';
1.49 stredwic 494: $notThere .= $name.'</option>'."\n";
495: $missing++;
496: }
497:
498: if($missing) {
1.50 stredwic 499: $notThere .= '</select>';
1.49 stredwic 500: } else {
1.50 stredwic 501: $notThere='<tr><td>';
1.49 stredwic 502: }
503:
1.55 stredwic 504: return $notThere.'</td></tr>';
1.49 stredwic 505: }
506:
1.55 stredwic 507: =pod
508:
509: =item &CreateColumnSelectors()
510:
511: This function generates the checkboxes above the column headings. The
512: column will be removed if the checkbox is unchecked.
513:
514: =over 4
515:
516: Input: $CacheData, $headings
517:
518: $CacheData: A pointer to a hash tied to the cached data
519:
520: $headings: An array of the names of the columns for the student
521: information. They are used to know what are the student information columns
522:
523: Output: $present
524:
525: $present: The string contains the first row of a table. Each column contains
526: a checkbox which is left justified. Currently left justification is used
527: for consistency of location over the column in which it presides.
528:
529: =back
530:
531: =cut
532:
1.49 stredwic 533: sub CreateColumnSelectors {
1.55 stredwic 534: my ($CacheData,$headings)=@_;
1.46 stredwic 535:
1.49 stredwic 536: my $found=0;
537: my ($name, $length, $position);
1.54 stredwic 538:
1.55 stredwic 539: my $present = '<tr>';
1.49 stredwic 540: for(my $index=0; $index<(scalar @$headings); $index++) {
1.59 stredwic 541: if(!&ShouldShowColumn($CacheData, 'ChartHeading'.$index)) {
1.49 stredwic 542: next;
543: }
1.54 stredwic 544: $present .= '<td align="left">';
1.49 stredwic 545: $present .= '<input type="checkbox" checked="on" ';
1.59 stredwic 546: $present .= 'name="ChartHeading'.$index.'" />';
1.53 stredwic 547: $present .= '</td>';
1.49 stredwic 548: $found++;
1.46 stredwic 549: }
550:
551: foreach my $sequence (split(/\:/,$CacheData->{'orderedSequences'})) {
1.59 stredwic 552: if(!&ShouldShowColumn($CacheData, 'ChartSequence'.$sequence)) {
1.49 stredwic 553: next;
554: }
1.54 stredwic 555: $present .= '<td align="left">';
1.49 stredwic 556: $present .= '<input type="checkbox" checked="on" ';
1.59 stredwic 557: $present .= 'name="ChartSequence'.$sequence.'" />';
1.53 stredwic 558: $present .= '</td>';
1.49 stredwic 559: $found++;
560: }
561:
1.54 stredwic 562: if(!$found) {
563: $present = '';
1.46 stredwic 564: }
565:
1.54 stredwic 566: return $present.'<td></td></tr></form>'."\n";;
1.46 stredwic 567: }
568:
1.55 stredwic 569: =pod
570:
571: =item &CreateForm()
572:
573: The interface for this module consists primarily of the controls in this
574: function. The student status selection (active, expired, any) is set here.
575: The sort buttons: username, last name, and section are set here. The
576: other buttons are Recalculate Chart, Refresh Chart, and Reset Selections.
577: These controls are in a table to clean up the interface.
578:
579: =over 4
580:
581: Input: $CacheData
582:
583: $CacheData is a hash pointer to tied database for cached data.
584:
585: Output: $Ptr
586:
587: $Ptr is a string containing all the html for the above mentioned buttons.
588:
589: =back
590:
591: =cut
592:
1.43 stredwic 593: sub CreateForm {
1.51 stredwic 594: my ($CacheData)=@_;
1.43 stredwic 595: my $OpSel1='';
596: my $OpSel2='';
597: my $OpSel3='';
1.60 ! stredwic 598: my $Status = $CacheData->{'form.Status'};
1.43 stredwic 599: if ( $Status eq 'Any' ) { $OpSel3='selected'; }
600: elsif ($Status eq 'Expired' ) { $OpSel2 = 'selected'; }
601: else { $OpSel1 = 'selected'; }
602:
1.59 stredwic 603: my $Ptr .= '<form name="ChartStat" method="post" action="/adm/chart" >';
604: $Ptr .= "\n";
1.50 stredwic 605: $Ptr .= '<tr><td align="right">';
606: $Ptr .= '</td><td align="left">';
1.59 stredwic 607: $Ptr .= '<input type="submit" name="ChartRecalculate" ';
1.50 stredwic 608: $Ptr .= 'value="Recalculate Chart"/>'."\n";
1.43 stredwic 609: $Ptr .= ' ';
1.59 stredwic 610: $Ptr .= '<input type="submit" name="ChartRefresh" ';
1.51 stredwic 611: $Ptr .= 'value="Refresh Chart"/>'."\n";
612: $Ptr .= ' ';
1.59 stredwic 613: $Ptr .= '<input type="submit" name="ChartReset" ';
1.51 stredwic 614: $Ptr .= 'value="Reset Selections"/></td>'."\n";
1.50 stredwic 615: $Ptr .= '</tr><tr><td align="right">';
616: $Ptr .= '<b> Sort by: </b>'."\n";
617: $Ptr .= '</td><td align="left">';
1.59 stredwic 618: $Ptr .= '<input type="submit" name="ChartSort" value="User Name" />'."\n";
1.43 stredwic 619: $Ptr .= ' ';
1.59 stredwic 620: $Ptr .= '<input type="submit" name="ChartSort" value="Last Name" />'."\n";
1.43 stredwic 621: $Ptr .= ' ';
1.59 stredwic 622: $Ptr .= '<input type="submit" name="ChartSort" value="Section"/>'."\n";
1.50 stredwic 623: $Ptr .= '</td></tr><tr><td align="right">';
1.43 stredwic 624: $Ptr .= '<b> Student Status: </b>'."\n".
1.50 stredwic 625: '</td><td align="left">'.
1.60 ! stredwic 626: '<select name="Status">'.
1.43 stredwic 627: '<option '.$OpSel1.' >Active</option>'."\n".
628: '<option '.$OpSel2.' >Expired</option>'."\n".
629: '<option '.$OpSel3.' >Any</option> </select> '."\n";
1.50 stredwic 630: $Ptr .= '</td></tr>';
1.44 stredwic 631:
632: return $Ptr;
633: }
634:
1.55 stredwic 635: =pod
636:
637: =item &CreateLegend()
638:
639: This function returns a formatted string containing the legend for the
640: chart. The legend describes the symbols used to represent grades for
641: problems.
642:
643: =cut
644:
1.44 stredwic 645: sub CreateLegend {
1.50 stredwic 646: my $Str = "<p><pre>".
647: "1..9: correct by student in 1..9 tries\n".
1.44 stredwic 648: " *: correct by student in more than 9 tries\n".
649: " +: correct by override\n".
650: " -: incorrect by override\n".
651: " .: incorrect attempted\n".
652: " #: ungraded attempted\n".
653: " : not attempted\n".
1.50 stredwic 654: " x: excused".
655: "</pre><p>";
1.44 stredwic 656: return $Str;
657: }
658:
1.55 stredwic 659: =pod
660:
661: =item &StartDocument()
662:
663: Returns a string containing the header information for the chart: title,
664: logo, and course title.
665:
666: =cut
667:
1.44 stredwic 668: sub StartDocument {
1.59 stredwic 669: my ($title, $header)=@_;
1.44 stredwic 670: my $Str = '';
671: $Str .= '<html>';
672: $Str .= '<head><title>';
1.59 stredwic 673: $Str .= $title.'</title></head>';
1.44 stredwic 674: $Str .= '<body bgcolor="#FFFFFF">';
675: $Str .= '<script>window.focus();</script>';
676: $Str .= '<img align=right src=/adm/lonIcons/lonlogos.gif>';
1.59 stredwic 677: $Str .= '<h1>'.$header.'</h1>';
1.50 stredwic 678: $Str .= '<h1>'.$ENV{'course.'.$ENV{'request.course.id'}.'.description'};
679: $Str .= '</h1>';
1.44 stredwic 680:
681: return $Str;
682: }
683:
684: # ----- END FORMAT PRINT DATA ------------------------------------------
685:
1.55 stredwic 686: =pod
687:
1.59 stredwic 688: =head1 HELPER FUNCTIONS
1.55 stredwic 689:
1.59 stredwic 690: These are just a couple of functions do various odd and end
691: jobs.
1.55 stredwic 692:
693: =cut
694:
1.59 stredwic 695: # ----- HELPER FUNCTIONS -----------------------------------------------
1.55 stredwic 696:
697: =pod
698:
699: =item &ProcessFormData()
700:
701: Cache form data and set default form data (sort, status, heading.$number,
702: sequence.$number, reselect, reset, recalculate, and refresh)
703:
704: =over 4
705:
706: Input: $ChartDB, $isCached
707:
708: $ChartDB: The name of the database for cached data
709:
710: $isCached: Is there already data for this course cached. This is used in
711: conjunction with the absence of all form data to know to display all selection
712: types.
713:
714: Output: None
715:
716: =back
717:
718: =cut
719:
1.58 stredwic 720: # For all data, if ENV data doesn't exist for it, default values is used.
1.55 stredwic 721: sub ProcessFormData {
722: my ($ChartDB, $isCached)=@_;
723: my %CacheData;
724:
725: if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
1.59 stredwic 726: # Ignore $ENV{'form.ChartRefresh'}
727: # Ignore $ENV{'form.ChartRecalculate'}
1.58 stredwic 728:
1.59 stredwic 729: if(defined($ENV{'form.ChartSort'})) {
730: $CacheData{'form.ChartSort'}=$ENV{'form.ChartSort'};
731: } elsif(!defined($CacheData{'form.ChartSort'})) {
732: $CacheData{'form.ChartSort'}='username';
1.55 stredwic 733: }
734:
1.60 ! stredwic 735: if(defined($ENV{'form.Status'})) {
! 736: $CacheData{'form.Status'}=$ENV{'form.Status'};
! 737: } elsif(!defined($CacheData{'form.Status'})) {
! 738: $CacheData{'form.Status'}='Active';
1.55 stredwic 739: }
740:
1.58 stredwic 741: # $found checks for any instances of form data in the ENV. If it is
742: # missing I assume the chrt button on the remote has been pressed.
1.55 stredwic 743: my @headings=();
744: my @sequences=();
745: my $found=0;
746: foreach (keys(%ENV)) {
1.59 stredwic 747: if(/form\.ChartHeading/) {
1.55 stredwic 748: $found++;
749: push(@headings, $_);
1.59 stredwic 750: } elsif(/form\.ChartSequence/) {
1.55 stredwic 751: $found++;
752: push(@sequences, $_);
753: } elsif(/form\./) {
754: $found++;
755: }
756: }
757:
758: if($found) {
1.59 stredwic 759: $CacheData{'form.ChartHeadings'}=join(":::",@headings);
760: $CacheData{'form.ChartSequences'}=join(":::",@sequences);
1.55 stredwic 761: }
762:
1.59 stredwic 763: if(defined($ENV{'form.ChartReselect'})) {
764: my @reselected = (ref($ENV{'form.ChartReselect'}) ?
765: @{$ENV{'form.ChartReselect'}}
766: : ($ENV{'form.ChartReselect'}));
1.55 stredwic 767: foreach (@reselected) {
1.59 stredwic 768: if(/ChartHeading/) {
769: $CacheData{'form.ChartHeadings'}.=":::".$_;
770: } elsif(/ChartSequence/) {
771: $CacheData{'form.ChartSequences'}.=":::".$_;
1.55 stredwic 772: }
773: }
774: }
775:
1.58 stredwic 776: # !$found and !$isCached are how I determine if the chrt button
777: # on the remote was pressed and needs to reset all the selections
1.59 stredwic 778: if(defined($ENV{'form.ChartReset'}) || (!$found && !$isCached)) {
779: $CacheData{'form.ChartReset'}='true';
1.60 ! stredwic 780: $CacheData{'form.Status'}='Active';
1.59 stredwic 781: $CacheData{'form.ChartSort'}='username';
782: $CacheData{'form.ChartHeadings'}='ALLHEADINGS';
783: $CacheData{'form.ChartSequences'}='ALLSEQUENCES';
1.55 stredwic 784: } else {
1.59 stredwic 785: $CacheData{'form.ChartReset'}='false';
1.55 stredwic 786: }
787:
788: untie(%CacheData);
789: }
790:
791: return;
792: }
793:
794: =pod
795:
796: =item &SpaceColumns()
797:
798: Determines the width of all the columns in the chart. It is based on
799: the max of the data for that column and its header.
800:
801: =over 4
802:
803: Input: $students, $studentInformation, $headings, $ChartDB
804:
805: $students: An array pointer to a list of students (username:domain)
806:
807: $studentInformatin: The type of data for the student information. It is
808: used as part of the key in $CacheData.
809:
810: $headings: The name of the student information columns.
811:
812: $ChartDB: The name of the cache database which is opened for read/write.
813:
814: Output: None - All data stored in cache.
815:
816: =back
1.44 stredwic 817:
1.55 stredwic 818: =cut
1.44 stredwic 819:
820: sub SpaceColumns {
1.59 stredwic 821: my ($students,$studentInformation,$headings,$cache)=@_;
1.44 stredwic 822:
1.59 stredwic 823: # Initialize Lengths
824: for(my $index=0; $index<(scalar @$headings); $index++) {
825: my @titleLength=split(//,$$headings[$index]);
826: $cache->{$$studentInformation[$index].'Length'}=
827: scalar @titleLength;
828: }
1.44 stredwic 829:
1.59 stredwic 830: foreach my $name (@$students) {
831: foreach (@$studentInformation) {
832: my @dataLength=split(//,$cache->{$name.':'.$_});
833: my $length=scalar @dataLength;
834: if($length > $cache->{$_.'Length'}) {
835: $cache->{$_.'Length'}=$length;
1.44 stredwic 836: }
837: }
838: }
839:
840: return;
841: }
842:
1.55 stredwic 843: =pod
844:
845: =item &SortStudents()
846:
847: Determines which students to display and in which order. Which are
848: displayed are determined by their status(active/expired). The order
849: is determined by the sort button pressed (default to username). The
850: type of sorting is username, lastname, or section.
851:
852: =over 4
853:
854: Input: $students, $CacheData
855:
856: $students: A array pointer to a list of students (username:domain)
857:
858: $CacheData: A pointer to the hash tied to the cached data
859:
860: Output: @order
861:
862: @order: An ordered list of students (username:domain)
863:
864: =back
865:
866: =cut
867:
1.44 stredwic 868: sub SortStudents {
1.48 stredwic 869: my ($students,$CacheData)=@_;
1.44 stredwic 870:
871: my @sorted1Students=();
1.48 stredwic 872: foreach (@$students) {
1.44 stredwic 873: my ($end,$start)=split(/\:/,$CacheData->{$_.':date'});
874: my $active=1;
875: my $now=time;
1.60 ! stredwic 876: my $Status=$CacheData->{'form.Status'};
1.44 stredwic 877: $Status = ($Status) ? $Status : 'Active';
878: if((($end) && $now > $end) && (($Status eq 'Active'))) {
879: $active=0;
880: }
881: if(($Status eq 'Expired') && ($end == 0 || $now < $end)) {
882: $active=0;
883: }
884: if($active) {
885: push(@sorted1Students, $_);
886: }
1.43 stredwic 887: }
1.1 www 888:
1.59 stredwic 889: my $Pos = $CacheData->{'form.ChartSort'};
1.43 stredwic 890: my %sortData;
891: if($Pos eq 'Last Name') {
1.44 stredwic 892: for(my $index=0; $index<scalar @sorted1Students; $index++) {
893: $sortData{$CacheData->{$sorted1Students[$index].':fullname'}}=
894: $sorted1Students[$index];
1.43 stredwic 895: }
896: } elsif($Pos eq 'Section') {
1.44 stredwic 897: for(my $index=0; $index<scalar @sorted1Students; $index++) {
898: $sortData{$CacheData->{$sorted1Students[$index].':section'}.
899: $sorted1Students[$index]}=$sorted1Students[$index];
1.43 stredwic 900: }
901: } else {
902: # Sort by user name
1.44 stredwic 903: for(my $index=0; $index<scalar @sorted1Students; $index++) {
904: $sortData{$sorted1Students[$index]}=$sorted1Students[$index];
1.43 stredwic 905: }
906: }
907:
908: my @order = ();
1.48 stredwic 909: foreach my $key (sort(keys(%sortData))) {
1.43 stredwic 910: push (@order,$sortData{$key});
911: }
1.33 minaeibi 912:
1.43 stredwic 913: return @order;
1.30 minaeibi 914: }
1.1 www 915:
1.55 stredwic 916: =pod
917:
918: =item &ShouldShowColumn()
919:
920: Determine if a specified column should be shown on the chart.
921:
922: =over 4
923:
924: Input: $cache, $test
925:
926: $cache: A pointer to the hash tied to the cached data
927:
928: $test: The form name of the column (heading.$headingIndex) or
929: (sequence.$sequenceIndex)
930:
931: Output: 0 (false), 1 (true)
1.44 stredwic 932:
1.55 stredwic 933: =back
1.1 www 934:
1.55 stredwic 935: =cut
1.44 stredwic 936:
1.49 stredwic 937: sub ShouldShowColumn {
1.51 stredwic 938: my ($cache,$test)=@_;
1.49 stredwic 939:
1.59 stredwic 940: if($cache->{'form.ChartReset'} eq 'true') {
1.49 stredwic 941: return 1;
942: }
943:
1.59 stredwic 944: my $headings=$cache->{'form.ChartHeadings'};
945: my $sequences=$cache->{'form.ChartSequences'};
1.51 stredwic 946: if($headings eq 'ALLHEADINGS' || $sequences eq 'ALLSEQUENCES' ||
947: $headings=~/$test/ || $sequences=~/$test/) {
1.49 stredwic 948: return 1;
949: }
950:
1.51 stredwic 951: return 0;
1.49 stredwic 952: }
953:
1.55 stredwic 954: # ----- END HELPER FUNCTIONS --------------------------------------------
955:
956: =pod
957:
958: =head1 Handler and main function(BuildChart)
959:
960: The handler does some initial error checking and then passes the torch to
961: BuildChart. BuildChart calls all the appropriate functions to get the
962: job done. These are the only two functions that use print ($r). All other
963: functions return strings to BuildChart to be printed.
964:
965: =cut
1.51 stredwic 966:
1.55 stredwic 967: =pod
1.51 stredwic 968:
1.55 stredwic 969: =item &BuildChart()
1.51 stredwic 970:
1.57 stredwic 971: The following is the process that BuildChart goes through to
972: create the html document.
1.51 stredwic 973:
1.55 stredwic 974: -Start the lonchart document
975: -Test for access to the CacheData
976: -Download class list information if not using cached data
977: -Sort students and print out table desciptive data
978: -Output student data
1.57 stredwic 979: -If recalculating, store a list of students, but only if all
980: their data was downloaded. Leave off the others.
1.55 stredwic 981: -End document
1.51 stredwic 982:
1.55 stredwic 983: =over 4
1.51 stredwic 984:
1.55 stredwic 985: Input: $r
1.51 stredwic 986:
1.55 stredwic 987: $r: Used to print html
1.51 stredwic 988:
1.55 stredwic 989: Output: None
1.51 stredwic 990:
1.55 stredwic 991: =back
1.49 stredwic 992:
1.55 stredwic 993: =cut
1.44 stredwic 994:
995: sub BuildChart {
996: my ($r)=@_;
997: my $c = $r->connection;
1.1 www 998:
1.44 stredwic 999: # Start the lonchart document
1000: $r->content_type('text/html');
1001: $r->send_http_header;
1.59 stredwic 1002: $r->print(&StartDocument('LON-CAPA Assessment Chart', 'Assessment Chart'));
1.44 stredwic 1003: $r->rflush();
1.43 stredwic 1004:
1.44 stredwic 1005: # Test for access to the CacheData
1006: my $isCached=0;
1.43 stredwic 1007: my $cid=$ENV{'request.course.id'};
1008: my $ChartDB = "/home/httpd/perl/tmp/$ENV{'user.name'}".
1009: "_$ENV{'user.domain'}_$cid\_chart.db";
1.59 stredwic 1010: my $isRecalculate=0;
1011: if(defined($ENV{'form.ChartRecalculate'})) {
1012: $isRecalculate=1;
1013: }
1014: $isCached=&Apache::loncoursedata::TestCacheData($ChartDB, $isRecalculate);
1.44 stredwic 1015: if($isCached < 0) {
1.59 stredwic 1016: $r->print("Unable to tie hash to db file</body></html>");
1.44 stredwic 1017: $r->rflush();
1018: return;
1019: }
1.55 stredwic 1020: &ProcessFormData($ChartDB, $isCached);
1.44 stredwic 1021:
1022: # Download class list information if not using cached data
1.48 stredwic 1023: my %CacheData;
1.44 stredwic 1024: my @students=();
1025: my @studentInformation=('username','domain','section','id','fullname');
1026: my @headings=('User Name','Domain','Section','PID','Full Name');
1027: my $spacePadding=' ';
1028: if(!$isCached) {
1.59 stredwic 1029: unless(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
1030: $r->print("Unable to tie hash to db file</body></html>");
1031: $r->rflush();
1032: return;
1033: }
1034:
1035: my $processTopResourceMapReturn=
1036: &Apache::loncoursedata::ProcessTopResourceMap(\%CacheData,$c);
1.44 stredwic 1037: if($processTopResourceMapReturn ne 'OK') {
1.59 stredwic 1038: $r->print($processTopResourceMapReturn.'</body></html>');
1039: untie(%CacheData);
1.44 stredwic 1040: return;
1041: }
1.59 stredwic 1042:
1043: if($c->aborted()) {
1044: untie(%CacheData);
1045: $r->print('</body></html>');
1046: return;
1047: }
1048:
1049: my $classlist=&Apache::loncoursedata::DownloadStudentNamePIDSection(
1050: $cid, $c);
1.44 stredwic 1051: my ($checkForError)=keys(%$classlist);
1052: if($checkForError =~ /^(con_lost|error|no_such_host)/i ||
1053: defined($classlist->{'error'})) {
1.59 stredwic 1054: $r->print("Error getting student data.</body></html>");
1055: $r->rflush();
1056: untie(%CacheData);
1.44 stredwic 1057: return;
1058: }
1.59 stredwic 1059:
1060: if($c->aborted()) {
1061: untie(%CacheData);
1062: $r->print('</body></html>');
1063: return;
1064: }
1065:
1066:
1067: @students=&Apache::loncoursedata::ProcessClassList(\%CacheData,
1.60 ! stredwic 1068: $classlist, $cid,
! 1069: $CacheData{'form.Status'},
! 1070: $c);
1.59 stredwic 1071:
1072: if($c->aborted()) {
1073: untie(%CacheData);
1074: $r->print('</body></html>');
1075: return;
1076: }
1077:
1.44 stredwic 1078: &SpaceColumns(\@students,\@studentInformation,\@headings,
1.59 stredwic 1079: \%CacheData);
1080:
1081: if($c->aborted()) {
1082: untie(%CacheData);
1083: $r->print('</body></html>');
1084: return;
1085: }
1086:
1087: untie(%CacheData);
1.48 stredwic 1088: } else {
1089: if(!$c->aborted() && tie(%CacheData,'GDBM_File',$ChartDB,
1090: &GDBM_READER,0640)) {
1091: @students=split(/:::/,$CacheData{'NamesOfStudents'});
1092: }
1.44 stredwic 1093: }
1094:
1095: # Sort students and print out table desciptive data
1.55 stredwic 1096: my $downloadTime=0;
1.44 stredwic 1097: if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_READER,0640)) {
1.48 stredwic 1098: if(!$c->aborted()) { @students=&SortStudents(\@students,\%CacheData); }
1.54 stredwic 1099: if(defined($CacheData{'time'})) { $downloadTime=$CacheData{'time'}; }
1100: else { $downloadTime=localtime(); }
1101: if(!$c->aborted()) { $r->print('<h3>'.$downloadTime.'</h3>'); }
1.50 stredwic 1102: if(!$c->aborted()) { $r->print('<h1>'.(scalar @students).
1103: ' students</h1>'); }
1104: if(!$c->aborted()) { $r->rflush(); }
1.44 stredwic 1105: if(!$c->aborted()) { $r->print(&CreateLegend()); }
1.55 stredwic 1106: if(!$c->aborted()) { $r->print('<table border="0"><tbody>'); }
1.51 stredwic 1107: if(!$c->aborted()) { $r->print(&CreateForm(\%CacheData)); }
1.49 stredwic 1108: if(!$c->aborted()) { $r->print(&CreateColumnSelectionBox(
1109: \%CacheData,
1.55 stredwic 1110: \@headings)); }
1111: if(!$c->aborted()) { $r->print('</tbody></table>'); }
1112: if(!$c->aborted()) { $r->print('<b>Note: Uncheck the boxes above a'); }
1113: if(!$c->aborted()) { $r->print(' column to remove that column from'); }
1114: if(!$c->aborted()) { $r->print(' the display.</b></pre>'); }
1115: if(!$c->aborted()) { $r->print('<table border="0" cellpadding="0" '); }
1116: if(!$c->aborted()) { $r->print('cellspacing="0"><tbody>'); }
1.49 stredwic 1117: if(!$c->aborted()) { $r->print(&CreateColumnSelectors(
1118: \%CacheData,
1.55 stredwic 1119: \@headings)); }
1.44 stredwic 1120: if(!$c->aborted()) { $r->print(&CreateTableHeadings(
1121: \%CacheData,
1122: \@studentInformation,
1123: \@headings,
1124: $spacePadding)); }
1.55 stredwic 1125: if(!$c->aborted()) { $r->print('</tbody></table>'); }
1.49 stredwic 1126: if(!$c->aborted()) { $r->rflush(); }
1.44 stredwic 1127: untie(%CacheData);
1.43 stredwic 1128: } else {
1.44 stredwic 1129: $r->print("Init2: Unable to tie hash to db file");
1130: return;
1.43 stredwic 1131: }
1132:
1.55 stredwic 1133: # Output student data
1.43 stredwic 1134: my @updateStudentList = ();
1.44 stredwic 1135: my $courseData;
1.50 stredwic 1136: $r->print('<pre>');
1.44 stredwic 1137: foreach (@students) {
1138: if($c->aborted()) {
1139: last;
1140: }
1141:
1142: if(!$isCached) {
1.59 stredwic 1143: $courseData=
1144: &Apache::loncoursedata::DownloadStudentCourseInformation($_,
1145: $cid);
1.50 stredwic 1146: if($c->aborted()) { last; }
1.59 stredwic 1147: if(tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
1148: &Apache::loncoursedata::ProcessStudentData(\%CacheData,
1149: $courseData, $_);
1150: push(@updateStudentList, $_);
1151: untie(%CacheData);
1152: } else {
1153: next;
1154: }
1.44 stredwic 1155: }
1.55 stredwic 1156: $r->print(&FormatStudentData($_, \@studentInformation,
1.44 stredwic 1157: $spacePadding, $ChartDB));
1158: $r->rflush();
1.43 stredwic 1159: }
1160:
1.55 stredwic 1161: # If recalculating, store a list of students, but only if all their
1162: # data was downloaded. Leave off the others.
1.50 stredwic 1163: if(!$isCached && tie(%CacheData,'GDBM_File',$ChartDB,&GDBM_WRCREAT,0640)) {
1164: $CacheData{'NamesOfStudents'}=join(":::", @updateStudentList);
1165: # $CacheData{'NamesOfStudents'}=
1166: # &Apache::lonnet::arrayref2str(\@updateStudentList);
1167: untie(%CacheData);
1168: }
1169:
1.55 stredwic 1170: # End document
1.50 stredwic 1171: $r->print('</pre></body></html>');
1.30 minaeibi 1172: $r->rflush();
1.1 www 1173:
1.43 stredwic 1174: return;
1.30 minaeibi 1175: }
1.1 www 1176:
1.30 minaeibi 1177: # ================================================================ Main Handler
1.55 stredwic 1178:
1179: =pod
1180:
1181: =item &handler()
1182:
1183: The handler checks for permission to access the course data and for
1184: initial header problem. Then it passes the torch to the work horse
1185: function BuildChart.
1186:
1187: =over 4
1188:
1189: Input: $r
1190:
1191: $r: This is the object that is used to print.
1192:
1193: Output: A Value (OK or HTTP_NOT_ACCEPTABLE)
1194:
1195: =back
1196:
1197: =cut
1.1 www 1198:
1.30 minaeibi 1199: sub handler {
1.44 stredwic 1200: my $r=shift;
1.51 stredwic 1201: # $jr=$r;
1.44 stredwic 1202: unless(&Apache::lonnet::allowed('vgr',$ENV{'request.course.id'})) {
1.30 minaeibi 1203: $ENV{'user.error.msg'}=
1.1 www 1204: $r->uri.":vgr:0:0:Cannot view grades for complete course";
1.30 minaeibi 1205: return HTTP_NOT_ACCEPTABLE;
1206: }
1.44 stredwic 1207:
1208: # Set document type for header only
1209: if ($r->header_only) {
1210: if($ENV{'browser.mathml'}) {
1211: $r->content_type('text/xml');
1212: } else {
1213: $r->content_type('text/html');
1214: }
1215: &Apache::loncommon::no_cache($r);
1216: $r->send_http_header;
1217: return OK;
1218: }
1.58 stredwic 1219:
1.44 stredwic 1220: unless($ENV{'request.course.fn'}) {
1221: my $requrl=$r->uri;
1222: $ENV{'user.error.msg'}="$requrl:bre:0:0:Course not initialized";
1223: return HTTP_NOT_ACCEPTABLE;
1224: }
1225:
1226: &BuildChart($r);
1227:
1228: return OK;
1.1 www 1229: }
1230: 1;
1231: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>