Annotation of loncom/interface/statistics/lonstudentassessment.pm, revision 1.34
1.1 stredwic 1: # The LearningOnline Network with CAPA
2: #
1.34 ! matthew 3: # $Id: lonstudentassessment.pm,v 1.33 2003/03/04 14:21:36 matthew Exp $
1.1 stredwic 4: #
5: # Copyright Michigan State University Board of Trustees
6: #
7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
8: # LON-CAPA is free software; you can redistribute it and/or modify
9: # it under the terms of the GNU General Public License as published by
10: # the Free Software Foundation; either version 2 of the License, or
11: # (at your option) any later version.
12: #
13: # LON-CAPA is distributed in the hope that it will be useful,
14: # but WITHOUT ANY WARRANTY; without even the implied warranty of
15: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16: # GNU General Public License for more details.
17: #
18: # You should have received a copy of the GNU General Public License
19: # along with LON-CAPA; if not, write to the Free Software
20: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21: #
22: # /home/httpd/html/adm/gpl.txt
23: #
24: # http://www.lon-capa.org/
25: #
26: # (Navigate problems for statistical reports
1.28 matthew 27: #
28: #######################################################
29: #######################################################
30:
31: =pod
32:
33: =head1 NAME
34:
35: lonstudentassessment
36:
37: =head1 SYNOPSIS
38:
39: Presents assessment data about a student or a group of students.
40:
41: =head1 Subroutines
42:
43: =over 4
44:
45: =cut
46:
47: #######################################################
48: #######################################################
1.1 stredwic 49:
1.21 minaeibi 50: package Apache::lonstudentassessment;
1.1 stredwic 51:
52: use strict;
1.28 matthew 53: use Apache::lonstatistics;
1.1 stredwic 54: use Apache::lonhtmlcommon;
55: use Apache::loncoursedata;
1.28 matthew 56: use Apache::lonnet; # for logging porpoises
1.31 matthew 57: use Spreadsheet::WriteExcel;
58:
59: #######################################################
60: #######################################################
61: =pod
62:
63: =item Package Variables
64:
65: =over 4
66:
67: =item $Statistics Hash ref to store student data. Indexed by symb,
68: contains hashes with keys 'score' and 'max'.
69:
70: =cut
71:
72: #######################################################
73: #######################################################
1.1 stredwic 74:
1.30 matthew 75: my $Statistics;
76:
1.28 matthew 77: #######################################################
78: #######################################################
79:
80: =pod
81:
1.31 matthew 82: =item $show_links 'yes' or 'no' for linking to student performance data
83:
84: =item $output_mode 'html', 'excel', or 'csv' for output mode
85:
1.32 matthew 86: =item $show 'all', 'totals', or 'scores' determines how much data is output
1.31 matthew 87:
88: =cut
89:
90: #######################################################
91: #######################################################
92: my $show_links;
93: my $output_mode;
94: my $show;
1.28 matthew 95:
1.31 matthew 96: #######################################################
97: #######################################################
98: # End of package variable declarations
1.28 matthew 99:
1.31 matthew 100: =pod
1.28 matthew 101:
1.31 matthew 102: =back
1.28 matthew 103:
1.31 matthew 104: =cut
1.28 matthew 105:
1.31 matthew 106: #######################################################
107: #######################################################
1.28 matthew 108:
1.31 matthew 109: =pod
1.28 matthew 110:
1.31 matthew 111: =item &BuildStudentAssessmentPage()
1.28 matthew 112:
1.31 matthew 113: Inputs:
1.4 stredwic 114:
1.31 matthew 115: =over 4
1.28 matthew 116:
117: =item $r Apache Request
118:
119: =item $c Apache Connection
120:
121: =back
122:
123: =cut
124:
125: #######################################################
126: #######################################################
1.1 stredwic 127: sub BuildStudentAssessmentPage {
1.30 matthew 128: my ($r,$c)=@_;
129: undef($Statistics);
130: #
1.31 matthew 131: # Print out the HTML headers for the interface
132: # This also parses the output mode selector
133: # This step must always be done.
1.30 matthew 134: $r->print(&CreateInterface());
1.31 matthew 135: $r->print('<input type="hidden" name="notfirstrun" value="true" />');
1.7 stredwic 136: $r->rflush();
1.31 matthew 137: if (! exists($ENV{'form.notfirstrun'})) {
138: $r->print(<<ENDMSG);
139: <p>
140: <font size="+1">
141: Please make your selections in the boxes above and hit
142: the button marked "Update Display".
143: </font>
144: </p>
145: ENDMSG
1.33 matthew 146: # $r->print(&OutputDescriptions());
1.31 matthew 147: return;
148: }
149: #
150: #
151: my $initialize = \&html_initialize;
152: my $output_student = \&html_outputstudent;
153: my $finish = \&html_finish;
154: #
155: if ($output_mode eq 'excel') {
156: $initialize = \&excel_initialize;
157: $output_student = \&excel_outputstudent;
158: $finish = \&excel_finish;
159: } elsif ($output_mode eq 'csv') {
160: $initialize = \&csv_initialize;
161: $output_student = \&csv_outputstudent;
162: $finish = \&csv_finish;
163: }
1.30 matthew 164: #
165: if($c->aborted()) { return ; }
1.31 matthew 166: #
167: # Call the initialize routine selected above
168: $initialize->($r);
1.30 matthew 169: foreach my $student (@Apache::lonstatistics::Students) {
1.31 matthew 170: if($c->aborted()) {
171: $finish->($r);
172: return ;
1.1 stredwic 173: }
1.31 matthew 174: # Call the output_student routine selected above
175: $output_student->($r,$student);
176: }
177: # Call the "finish" routine selected above
178: $finish->($r);
179: #
180: return;
181: }
182:
183: #######################################################
184: #######################################################
1.30 matthew 185:
1.31 matthew 186: sub get_student_fields_to_show {
187: my @to_show = @Apache::lonstatistics::SelectedStudentData;
188: foreach (@to_show) {
189: if ($_ eq 'all') {
190: @to_show = @Apache::lonstatistics::StudentDataOrder;
191: last;
192: }
193: }
194: return @to_show;
195: }
196:
197: sub get_sequences_to_show {
198: my @Sequences;
199: foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {
200: foreach my $sequence (@Apache::lonstatistics::Sequences) {
201: next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all');
202: next if ($sequence->{'num_assess'} < 1);
203: push (@Sequences,$sequence);
204: }
1.1 stredwic 205: }
1.31 matthew 206: return @Sequences;
1.2 stredwic 207: }
1.30 matthew 208:
1.31 matthew 209:
1.28 matthew 210: #######################################################
211: #######################################################
212:
213: =pod
1.2 stredwic 214:
1.28 matthew 215: =item &CreateInterface()
1.21 minaeibi 216:
1.28 matthew 217: Called by &BuildStudentAssessmentPage to create the top part of the
218: page which displays the chart.
219:
1.30 matthew 220: Inputs: None
1.28 matthew 221:
222: Returns: A string containing the HTML for the headers and top table for
223: the chart page.
224:
225: =cut
226:
227: #######################################################
228: #######################################################
1.2 stredwic 229: sub CreateInterface {
1.4 stredwic 230: my $Str = '';
1.30 matthew 231: # $Str .= &CreateLegend();
232: $Str .= '<table cellspacing="5">'."\n";
233: $Str .= '<tr>';
234: $Str .= '<td align="center"><b>Sections</b></td>';
235: $Str .= '<td align="center"><b>Student Data</b></td>';
236: $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
1.31 matthew 237: $Str .= '<td align="center"><b>Output Format</b></td>';
1.30 matthew 238: $Str .= '</tr>'."\n";
239: #
1.4 stredwic 240: $Str .= '<tr><td align="center">'."\n";
1.29 matthew 241: $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
1.4 stredwic 242: $Str .= '</td><td align="center">';
1.30 matthew 243: my $only_seq_with_assessments = sub {
244: my $s=shift;
245: if ($s->{'num_assess'} < 1) {
246: return 0;
247: } else {
248: return 1;
249: }
250: };
251: $Str .= &Apache::lonstatistics::StudentDataSelect('StudentData','multiple',
252: 5,undef);
1.4 stredwic 253: $Str .= '</td><td>'."\n";
1.30 matthew 254: $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
255: $only_seq_with_assessments);
1.31 matthew 256: $Str .= '</td><td>'."\n";
257: $Str .= &CreateAndParseOutputSelector();
1.30 matthew 258: $Str .= '</td></tr>'."\n";
259: $Str .= '</table>'."\n";
1.4 stredwic 260: return $Str;
1.1 stredwic 261: }
1.30 matthew 262:
263: #######################################################
264: #######################################################
265:
266: =pod
267:
1.31 matthew 268: =item &CreateAndParseOutputSelector()
1.30 matthew 269:
270: =cut
271:
272: #######################################################
273: #######################################################
1.32 matthew 274: my @OutputOptions =
275: ({ name => 'HTML, with links',
276: value => 'html, with links',
1.33 matthew 277: description => 'Output HTML with each symbol linked to the problem '.
278: 'which generated it.'},
1.32 matthew 279: { name => 'HTML, without links',
280: value => 'html, without links',
1.33 matthew 281: description => 'Output HTML. By not including links, the size of the'.
282: ' web page is greatly reduced. If your browser crashes on the '.
283: 'full display, try this.'},
284: { name => 'HTML, scores only',
285: value => 'html, scores only',
286: description => 'Output HTML, only showing the total number of correct'.
287: ' problems (or problem parts) and not the maximum possible for '.
288: 'each student'},
1.32 matthew 289: { name => 'HTML, totals',
290: value => 'html, totals',
1.33 matthew 291: description => 'Output HTML, but only the summary statistics for each'.
292: ' sequence selected.'},
293: { name => 'Excel, scores only',
294: value => 'excel, scores only',
295: description => 'Output an Excel file (compatable with Excel 95), '.
296: 'with a single column for each sequence showing the students '.
297: 'score.'},
298: # { name => 'Excel, everything',
299: # value => 'excel, everything',
300: # description => 'Output an Excel file (compatable with Excel 95), '.
301: # 'with a seperate worksheet for each sequence you have selected '.
302: # 'the data for each problem part '.
303: # '(number of tries, status, points awarded) '.
304: # 'will be listed.'},
1.32 matthew 305: { name => 'Excel, totals',
306: value => 'excel, totals',
1.33 matthew 307: description => 'Output an Excel file (compatable with Excel 95), '.
308: 'with two columns for each sequence, the students score on the '.
309: 'sequence and the students maximum possible on the sequence'},
310: { name => 'CSV, everything',
311: value => 'csv, everything',
1.32 matthew 312: description => ''},
1.33 matthew 313: { name => 'CSV, scores only',
314: value => 'csv, scores only',
1.32 matthew 315: description => ''},
316: { name => 'CSV, totals',
317: value => 'csv, totals',
318: description => ''},
319: );
320:
1.33 matthew 321: sub OutputDescriptions {
322: my $Str = '';
323: $Str .= "<h2>Output Modes</h2>\n";
324: $Str .= "<dl>\n";
325: foreach my $outputmode (@OutputOptions) {
326: $Str .=" <dt>".$outputmode->{'name'}."</dt>\n";
327: $Str .=" <dd>".$outputmode->{'description'}."</dd>\n";
328: }
329: $Str .= "</dl>\n";
330: return $Str;
331: }
332:
1.31 matthew 333: sub CreateAndParseOutputSelector {
334: my $Str = '';
335: my $elementname = 'outputmode';
336: #
337: # Format for output options is 'mode, restrictions';
338: my $selected = 'html, with links';
339: if (exists($ENV{'form.'.$elementname})) {
340: if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
341: $selected = $ENV{'form.'.$elementname}->[0];
342: } else {
343: $selected = $ENV{'form.'.$elementname};
344: }
345: }
346: #
347: # Set package variables describing output mode
348: $show_links = 'no';
349: $output_mode = 'html';
350: $show = 'all';
351: my ($mode,$restriction) = split(',',$selected);
352: $restriction =~ s/^\s*//;
353: if ($mode =~ /^(html|excel|csv)$/) {
354: $output_mode = $mode;
355: } else {
356: $output_mode = 'html';
357: }
358: if ($restriction eq 'with links') {
359: $show_links = 'yes';
360: } else {
361: $show_links = 'no';
362: }
1.32 matthew 363: if ($restriction eq 'totals') {
1.31 matthew 364: $show = 'totals';
1.32 matthew 365: } elsif ($restriction eq 'scores only') {
366: $show = 'scores';
1.31 matthew 367: } else {
368: $show = 'everything';
369: }
370: #
371: # Build the form element
372: $Str = qq/<select size="5" name="$elementname">/;
1.32 matthew 373: foreach my $option (@OutputOptions) {
374: $Str .= "\n".' <option value="'.$option->{'value'}.'"';
375: $Str .= " selected " if ($option->{'value'} eq $selected);
376: $Str .= ">".$option->{'name'}."<\/option>";
1.31 matthew 377: }
378: $Str .= "\n</select>";
379: return $Str;
380: }
1.30 matthew 381:
1.28 matthew 382: #######################################################
383: #######################################################
1.1 stredwic 384:
1.28 matthew 385: =pod
386:
1.31 matthew 387: =head2 HTML output routines
1.28 matthew 388:
1.31 matthew 389: =item &html_initialize($r)
1.28 matthew 390:
1.31 matthew 391: Create labels for the columns of student data to show.
1.28 matthew 392:
1.31 matthew 393: =item &html_outputstudent($r,$student)
1.28 matthew 394:
1.31 matthew 395: Return a line of the chart for a student.
1.28 matthew 396:
1.31 matthew 397: =item &html_finish($r)
1.28 matthew 398:
399: =cut
400:
401: #######################################################
402: #######################################################
1.31 matthew 403: {
404: my $padding;
405: my $count;
406:
407: sub html_initialize {
408: my ($r) = @_;
1.30 matthew 409: #
410: $padding = ' 'x3;
1.31 matthew 411: $count = 1;
1.30 matthew 412: #
1.31 matthew 413: my $Str = "<pre>\n";
1.30 matthew 414: # First, the @StudentData fields need to be listed
1.31 matthew 415: my @to_show = &get_student_fields_to_show();
1.30 matthew 416: foreach my $field (@to_show) {
417: my $title=$Apache::lonstatistics::StudentData{$field}->{'title'};
418: my $base =$Apache::lonstatistics::StudentData{$field}->{'base_width'};
419: my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
420: $Str .= $title.' 'x($width-$base).$padding;
421: }
422: # Now the selected sequences need to be listed
1.31 matthew 423: foreach my $sequence (&get_sequences_to_show) {
424: my $title = $sequence->{'title'};
425: my $base = $sequence->{'base_width'};
426: my $width = $sequence->{'width'};
427: $Str .= $title.' 'x($width-$base).$padding;
1.30 matthew 428: }
1.31 matthew 429: $Str .= "total (of shown problems)</pre>\n";
430: $Str .= "<pre>";
431: $r->print($Str);
432: $r->rflush();
433: return;
1.30 matthew 434: }
435:
1.31 matthew 436: sub html_outputstudent {
437: my ($r,$student) = @_;
1.2 stredwic 438: my $Str = '';
1.30 matthew 439: # First, the @StudentData fields need to be listed
1.31 matthew 440: my @to_show = &get_student_fields_to_show();
1.30 matthew 441: foreach my $field (@to_show) {
442: my $title=$student->{$field};
1.31 matthew 443: my $base = length($title);
1.30 matthew 444: my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
445: $Str .= $title.' 'x($width-$base).$padding;
446: }
447: # Get ALL the students data
448: my %StudentsData;
449: my @tmp = &Apache::loncoursedata::get_current_state
450: ($student->{'username'},$student->{'domain'},undef,
451: $ENV{'request.course.id'});
452: if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
453: %StudentsData = @tmp;
454: }
455: if (scalar(@tmp) < 1) {
456: $Str .= '<font color="blue">No Course Data</font>'."\n";
1.31 matthew 457: $r->print($Str);
458: $r->rflush();
459: return;
1.30 matthew 460: }
461: #
462: # By sequence build up the data
463: my $studentstats;
1.31 matthew 464: my $PerformanceStr = '';
465: foreach my $seq (&get_sequences_to_show) {
466: my ($performance,$score,$seq_max) =
467: &StudentPerformanceOnSequence($student,\%StudentsData,
468: $seq,$show_links);
469: my $ratio = $score.'/'.$seq_max;
470: #
471: if ($show eq 'totals') {
472: $performance = ' 'x(length($seq_max)-length($score)).$ratio;
473: $performance .= ' 'x($seq->{'width'}-length($performance));
1.32 matthew 474: } elsif ($show eq 'scores') {
475: $performance = $score;
476: $performance .= ' 'x($seq->{'width'}-length($performance));
1.31 matthew 477: } else {
478: # Pad with extra spaces
479: $performance .= ' 'x($seq->{'width'}-$seq_max-
480: length($ratio)
481: ).$ratio;
1.30 matthew 482: }
1.31 matthew 483: #
484: $Str .= $performance.$padding;
485: #
486: $studentstats->{$seq->{'symb'}}->{'score'}= $score;
487: $studentstats->{$seq->{'symb'}}->{'max'} = $seq_max;
1.30 matthew 488: }
489: #
490: # Total it up and store the statistics info.
491: my ($score,$max) = (0,0);
492: while (my ($symb,$seq_stats) = each (%{$studentstats})) {
493: $Statistics->{$symb}->{'score'} += $seq_stats->{'score'};
494: $Statistics->{$symb}->{'max'} += $seq_stats->{'max'};
495: $score += $seq_stats->{'score'};
496: $max += $seq_stats->{'max'};
497: }
1.31 matthew 498: $Str .= ' '.' 'x(length($max)-length($score)).$score.'/'.$max;
1.30 matthew 499: $Str .= " \n";
1.31 matthew 500: $r->print($Str);
501: #
502: $count++;
503: if($count % 5 == 0) {
504: $r->print("</pre><pre>");
505: }
506: #
507: $r->rflush();
508: return;
1.30 matthew 509: }
1.2 stredwic 510:
1.31 matthew 511: sub html_finish {
512: my ($r) = @_;
513: $r->print("</pre>\n");
514: $r->rflush();
515: return;
516: }
517:
518: }
519:
520: #######################################################
521: #######################################################
522:
523: =pod
524:
525: =head2 EXCEL subroutines
526:
527: =item &excel_initialize($r)
528:
529: =item &excel_outputstudent($r,$student)
530:
531: =item &excel_finish($r)
532:
533: =cut
534:
535: #######################################################
536: #######################################################
537: {
538:
539: my $excel_sheet;
1.32 matthew 540: my $excel_workbook;
541:
542: my $filename;
543: my $rows_output;
544: my $cols_output;
545:
546: my $num_students;
547: my $start_time;
1.31 matthew 548:
549: sub excel_initialize {
550: my ($r) = @_;
551: #
1.32 matthew 552: $filename = '/prtspool/'.
1.31 matthew 553: $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
554: time.'_'.rand(1000000000).'.xls';
1.32 matthew 555: #
556: $excel_workbook = undef;
557: $excel_sheet = undef;
558: #
559: $rows_output = 0;
560: $cols_output = 0;
561: #
562: $num_students = 0;
563: $start_time = time;
564: #
565: # Create sheet
566: $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
567: #
568: # Check for errors
569: if (! defined($excel_workbook)) {
1.31 matthew 570: $r->log_error("Error creating excel spreadsheet $filename: $!");
571: $r->print("Problems creating new Excel file. ".
572: "This error has been logged. ".
573: "Please alert your LON-CAPA administrator");
1.32 matthew 574: return ;
1.31 matthew 575: }
576: #
577: # The excel spreadsheet stores temporary data in files, then put them
578: # together. If needed we should be able to disable this (memory only).
579: # The temporary directory must be specified before calling 'addworksheet'.
580: # File::Temp is used to determine the temporary directory.
1.32 matthew 581: $excel_workbook->set_tempdir($Apache::lonnet::tmpdir);
582: #
583: # Add a worksheet
1.33 matthew 584: my $sheetname = $ENV{'course.'.$ENV{'request.course.id'}.'.description'};
585: if (length($sheetname) > 31) {
586: $sheetname = substr($sheetname,0,31);
587: }
588: $excel_sheet = $excel_workbook->addworksheet($sheetname);
1.32 matthew 589: #
1.34 ! matthew 590: # Put the course description in the header
! 591: $excel_sheet->write($rows_output,$cols_output++,
! 592: $ENV{'course.'.$ENV{'request.course.id'}.'.description'});
! 593: $cols_output += 3;
! 594: #
! 595: # Put a description of the sections listed
! 596: my $sectionstring = '';
! 597: my @Sections = @Apache::lonstatistics::SelectedSections;
! 598: if (scalar(@Sections) > 1) {
! 599: if (scalar(@Sections) > 2) {
! 600: my $last = pop(@Sections);
! 601: $sectionstring = "Sections ".join(', ',@Sections).', and '.$last;
! 602: } else {
! 603: $sectionstring = "Sections ".join(' and ',@Sections);
! 604: }
! 605: } else {
! 606: if ($Sections[0] eq 'all') {
! 607: $sectionstring = "All sections";
! 608: } else {
! 609: $sectionstring = "Section ".$Sections[0];
! 610: }
! 611: }
! 612: $excel_sheet->write($rows_output,$cols_output++,$sectionstring);
! 613: $cols_output += scalar(@Sections);
! 614: #
! 615: # Put the date in there too
! 616: $excel_sheet->write($rows_output,$cols_output++,
! 617: 'Compiled on '.localtime(time));
! 618: #
! 619: $rows_output++;
! 620: #
1.32 matthew 621: # Add the student headers
1.34 ! matthew 622: $cols_output = 0;
1.32 matthew 623: foreach my $field (&get_student_fields_to_show()) {
1.34 ! matthew 624: $excel_sheet->write($rows_output,$cols_output++,$field);
1.32 matthew 625: }
626: #
627: # Add the Sequence Headers
628: foreach my $seq (&get_sequences_to_show) {
1.34 ! matthew 629: $excel_sheet->write($rows_output,$cols_output,$seq->{'title'});
1.32 matthew 630: if ($show eq 'totals') {
1.34 ! matthew 631: $excel_sheet->write($rows_output+1,$cols_output,'score');
! 632: $excel_sheet->write($rows_output+1,$cols_output+1,'maximum');
1.32 matthew 633: $cols_output += 2;
634: } else {
635: $cols_output++;
636: }
637: }
638: #
639: # Bookkeeping
640: if ($show eq 'totals') {
1.34 ! matthew 641: $rows_output += 2;
1.32 matthew 642: } else {
1.34 ! matthew 643: $rows_output += 1;
1.32 matthew 644: }
645: #
646: # Let the user know what we are doing
647: my $studentcount = scalar(@Apache::lonstatistics::Students);
648: $r->print("<h1>Compiling Excel spreadsheet for ".
649: $studentcount.' student');
650: $r->print('s') if ($studentcount > 1);
651: $r->print("</h1>\n");
652: $r->rflush();
1.31 matthew 653: #
654: return;
655: }
656:
657: sub excel_outputstudent {
658: my ($r,$student) = @_;
1.32 matthew 659: return if (! defined($excel_sheet));
660: $cols_output=0;
661: #
662: # Write out student data
663: my @to_show = &get_student_fields_to_show();
664: foreach my $field (@to_show) {
665: $excel_sheet->write($rows_output,$cols_output++,$student->{$field});
666: }
667: #
668: # Get student assessment data
669: my %StudentsData;
670: my @tmp = &Apache::loncoursedata::get_current_state($student->{'username'},
671: $student->{'domain'},
672: undef,
673: $ENV{'request.course.id'});
674: if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
675: %StudentsData = @tmp;
676: }
677: #
678: # Write out sequence scores and totals data
679: foreach my $seq (&get_sequences_to_show) {
680: my ($performance,$score,$seq_max) =
681: &StudentPerformanceOnSequence($student,\%StudentsData,
682: $seq,'no');
683: if ($show eq 'totals' || $show eq 'scores') {
684: $excel_sheet->write($rows_output,$cols_output++,$score);
685: }
686: if ($show eq 'totals') {
687: $excel_sheet->write($rows_output,$cols_output++,$seq_max);
688: }
689: }
690: #
691: # Bookkeeping
692: $rows_output++;
693: $cols_output=0;
694: #
695: # Time estimate
696: $num_students++;
697: if ($num_students % 10 == 0) {
698: my $time_est = (time - $start_time)/$num_students *
699: (scalar(@Apache::lonstatistics::Students)-$num_students);
700: $time_est = int($time_est);
701: if (int ($time_est/60) > 0) {
702: my $min = int($time_est/60);
703: my $sec = $time_est % 60;
704: $time_est = $min.' minutes';
705: if ($sec > 1) {
706: $time_est.= ', '.$sec.' seconds';
707: } elsif ($sec > 0) {
708: $time_est.= ', '.$sec.' second';
709: }
710: } else {
711: $time_est .= ' seconds';
712: }
713: $r->print($num_students.' out of '.
714: (scalar(@Apache::lonstatistics::Students)).
715: " students processed. ".
1.34 ! matthew 716: $time_est." remain. Elapsed: ".(time - $start_time).
! 717: "<br />\n");
1.32 matthew 718: $r->rflush();
719: }
720: return;
1.31 matthew 721: }
722:
723: sub excel_finish {
724: my ($r) = @_;
1.32 matthew 725: return if (! defined($excel_sheet));
726: #
727: # Write the excel file
728: $excel_workbook->close();
729: my $c = $r->connection();
730: #
731: return if($c->aborted());
732: #
733: # Tell the user where to get their excel file
734: $r->print('<br /><br />'.
735: '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
736: my $total_time = time - $start_time;
737: if (int ($total_time / 60) > 0) {
738: $total_time = int($total_time/60).' minutes, '.($total_time % 60);
739: }
740: $r->print('<br />'.$total_time.' seconds total');
741: $r->rflush();
742: return;
1.31 matthew 743: }
744:
745: }
1.30 matthew 746: #######################################################
747: #######################################################
748:
749: =pod
750:
1.31 matthew 751: =head2 CSV output routines
752:
753: =item &csv_initialize($r)
754:
755: =item &csv_outputstudent($r,$student)
756:
757: =item &csv_finish($r)
1.30 matthew 758:
759: =cut
760:
761: #######################################################
762: #######################################################
1.31 matthew 763: {
764:
765: sub csv_initialize{
766: my ($r) = @_;
767: $r->print("<h1>Not implemented yet</h1>");
768: return;
769: }
770:
771: sub csv_outputstudent {
772: my ($r,$student) = @_;
773: }
774:
775: sub csv_finish {
776: my ($r) = @_;
777: }
1.2 stredwic 778:
779: }
780:
1.28 matthew 781: #######################################################
782: #######################################################
783:
1.2 stredwic 784: =pod
785:
1.30 matthew 786: =item &StudentPerformanceOnSequence()
1.2 stredwic 787:
1.30 matthew 788: Inputs:
1.2 stredwic 789:
790: =over 4
791:
1.30 matthew 792: =item $student
1.28 matthew 793:
1.30 matthew 794: =item $studentdata Hash ref to all student data
1.2 stredwic 795:
1.30 matthew 796: =item $seq Hash ref, the sequence we are working on
1.2 stredwic 797:
1.30 matthew 798: =item $links if defined we will output links to each resource.
1.2 stredwic 799:
1.28 matthew 800: =back
1.2 stredwic 801:
802: =cut
1.1 stredwic 803:
1.28 matthew 804: #######################################################
805: #######################################################
1.30 matthew 806: sub StudentPerformanceOnSequence {
1.32 matthew 807: my ($student,$studentdata,$seq,$links) = @_;
1.31 matthew 808: $links = 'no' if (! defined($links));
1.1 stredwic 809: my $Str = '';
1.30 matthew 810: my ($sum,$max) = (0,0);
811: foreach my $resource (@{$seq->{'contents'}}) {
812: next if ($resource->{'type'} ne 'assessment');
813: my $resource_data = $studentdata->{$resource->{'symb'}};
814: my $value = '';
815: foreach my $partnum (@{$resource->{'parts'}}) {
816: $max++;
817: my $symbol = ' '; # default to space
818: #
819: if (exists($resource_data->{'resource.'.$partnum.'.solved'})) {
820: my $status = $resource_data->{'resource.'.$partnum.'.solved'};
821: if ($status eq 'correct_by_override') {
822: $symbol = '+';
823: $sum++;
824: } elsif ($status eq 'incorrect_by_override') {
825: $symbol = '-';
826: } elsif ($status eq 'ungraded_attempted') {
827: $symbol = '#';
828: } elsif ($status eq 'incorrect_attempted') {
829: $symbol = '.';
830: } elsif ($status eq 'excused') {
831: $symbol = 'x';
832: $max--;
833: } elsif ($status eq 'correct_by_student' &&
834: exists($resource_data->{'resource.'.$partnum.'.tries'})){
835: my $num = $resource_data->{'resource.'.$partnum.'.tries'};
836: if ($num > 9) {
837: $symbol = '*';
838: } elsif ($num > 0) {
839: $symbol = $num;
840: } else {
841: $symbol = ' ';
842: }
843: $sum++;
844: } else {
845: $symbol = ' ';
1.2 stredwic 846: }
1.30 matthew 847: } else {
848: # Unsolved. Did they try?
849: if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
850: $symbol = '.';
851: } else {
852: $symbol = ' ';
1.18 matthew 853: }
1.2 stredwic 854: }
1.30 matthew 855: #
1.31 matthew 856: if ($links eq 'yes' && $symbol ne ' ') {
1.30 matthew 857: $symbol = '<a href="/adm/grades'.
858: '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
859: '&student='.$student->{'username'}.
860: '&domain='.$student->{'domain'}.
861: '&command=submission">'.$symbol.'</a>';
862: }
863: $value .= $symbol;
1.2 stredwic 864: }
1.30 matthew 865: $Str .= $value;
1.17 minaeibi 866: }
1.30 matthew 867: return ($Str,$sum,$max);
1.17 minaeibi 868: }
869:
1.28 matthew 870: #######################################################
871: #######################################################
1.17 minaeibi 872: sub StudentAverageTotal {
1.21 minaeibi 873: my ($cache, $students, $sequenceKeys)=@_;
1.23 minaeibi 874: my $Str = "\n<b>Summary Tables:</b>\n";
1.21 minaeibi 875: my %Correct = ();
876: my $ProblemsSolved = 0;
877: my $TotalProblems = 0;
878: my $StudentCount = 0;
879:
880: foreach my $name (@$students) {
881: $StudentCount++;
882: foreach my $sequence (@$sequenceKeys) {
1.23 minaeibi 883: $Correct{$sequence} +=
884: $cache->{$name.':'.$sequence.':problemsCorrect'};
1.17 minaeibi 885: }
1.21 minaeibi 886: $ProblemsSolved += $cache->{$name.':problemsSolved'};
887: $TotalProblems += $cache->{$name.':totalProblems'};
1.1 stredwic 888: }
1.25 matthew 889: if ($StudentCount) {
1.27 minaeibi 890: $ProblemsSolved = sprintf( "%.2f",
891: $ProblemsSolved/$StudentCount);
1.25 matthew 892: $TotalProblems /= $StudentCount;
893: } else {
894: $ProblemsSolved = 0;
895: $TotalProblems = 0;
896: }
1.27 minaeibi 897:
1.24 minaeibi 898: $Str .= '<table border=2 cellspacing="1">'."\n";
1.23 minaeibi 899: $Str .= '<tr><td><b>Students Count</b></td><td><b>'.
900: $StudentCount.'</b></td></tr>'."\n";
901: $Str .= '<tr><td><b>Total Problems</b></td><td><b>'.
902: $TotalProblems.'</b></td></tr>'."\n";
903: $Str .= '<tr><td><b>Average Correct</b></td><td><b>'.
904: $ProblemsSolved.'</b></td></tr>'."\n";
905: $Str .= '</table>'."\n";
906:
1.24 minaeibi 907: $Str .= '<table border=2 cellspacing="1">'."\n";
1.23 minaeibi 908: $Str .= '<tr><th>Title</th><th>Total Problems</th>'.
909: '<th>Average Correct</th></tr>'."\n";
910: foreach my $S(@$sequenceKeys) {
911: my $title=$cache->{$S.':title'};
912: #$Str .= $cache->{$S.':problems'};
1.24 minaeibi 913: #my @problems=split(':', $cache->{$S.':problems'});
1.23 minaeibi 914: #my $pCount=scalar @problems;
915: my $pCount=MaxSeqPr($cache,@$students[0],$S);
1.25 matthew 916: my $crr;
917: if ($StudentCount) {
918: $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
919: } else {
920: $crr="0.00";
921: }
1.23 minaeibi 922: $Str .= '<tr><td>'.$title.
923: '</td><td align=center>'.$pCount.
924: '</td><td align=center>'.$crr.
925: '</td></tr>'."\n";
1.10 stredwic 926: }
1.1 stredwic 927:
1.23 minaeibi 928: $Str .= '</table>'."\n";
929:
1.1 stredwic 930: return $Str;
931: }
1.23 minaeibi 932:
1.28 matthew 933: #######################################################
934: #######################################################
1.23 minaeibi 935:
1.2 stredwic 936: =pod
937:
938: =item &CreateLegend()
939:
940: This function returns a formatted string containing the legend for the
941: chart. The legend describes the symbols used to represent grades for
942: problems.
943:
944: =cut
945:
1.28 matthew 946: #######################################################
947: #######################################################
1.2 stredwic 948: sub CreateLegend {
949: my $Str = "<p><pre>".
1.13 minaeibi 950: " 1 correct by student in 1 try\n".
951: " 7 correct by student in 7 tries\n".
1.12 minaeibi 952: " * correct by student in more than 9 tries\n".
1.20 minaeibi 953: " + correct by hand grading or override\n".
1.12 minaeibi 954: " - incorrect by override\n".
955: " . incorrect attempted\n".
956: " # ungraded attempted\n".
1.13 minaeibi 957: " not attempted (blank field)\n".
1.12 minaeibi 958: " x excused".
1.17 minaeibi 959: "</pre><p>";
1.2 stredwic 960: return $Str;
961: }
962:
1.28 matthew 963: #######################################################
964: #######################################################
965:
1.30 matthew 966: =pod
1.2 stredwic 967:
968: =back
969:
970: =cut
971:
1.28 matthew 972: #######################################################
973: #######################################################
1.2 stredwic 974:
1.28 matthew 975: 1;
1.2 stredwic 976:
1.1 stredwic 977: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>