Annotation of loncom/interface/statistics/lonstudentassessment.pm, revision 1.33
1.1 stredwic 1: # The LearningOnline Network with CAPA
2: #
1.33 ! matthew 3: # $Id: lonstudentassessment.pm,v 1.32 2003/03/03 22:54:05 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: #
590: # Add the student headers
591: foreach my $field (&get_student_fields_to_show()) {
592: $excel_sheet->write(1,$cols_output++,$field);
593: }
594: #
595: # Add the Sequence Headers
596: foreach my $seq (&get_sequences_to_show) {
597: $excel_sheet->write(0,$cols_output,$seq->{'title'});
598: if ($show eq 'totals') {
599: $excel_sheet->write(1,$cols_output,'score');
600: $excel_sheet->write(1,$cols_output+1,'maximum');
601: $cols_output += 2;
602: } else {
603: $cols_output++;
604: }
605: }
606: #
607: # Bookkeeping
608: if ($show eq 'totals') {
609: $rows_output = 2;
610: } else {
611: $rows_output = 1;
612: }
613: #
614: # Let the user know what we are doing
615: my $studentcount = scalar(@Apache::lonstatistics::Students);
616: $r->print("<h1>Compiling Excel spreadsheet for ".
617: $studentcount.' student');
618: $r->print('s') if ($studentcount > 1);
619: $r->print("</h1>\n");
620: $r->rflush();
1.31 matthew 621: #
622: return;
623: }
624:
625: sub excel_outputstudent {
626: my ($r,$student) = @_;
1.32 matthew 627: return if (! defined($excel_sheet));
628: $cols_output=0;
629: #
630: # Write out student data
631: my @to_show = &get_student_fields_to_show();
632: foreach my $field (@to_show) {
633: $excel_sheet->write($rows_output,$cols_output++,$student->{$field});
634: }
635: #
636: # Get student assessment data
637: my %StudentsData;
638: my @tmp = &Apache::loncoursedata::get_current_state($student->{'username'},
639: $student->{'domain'},
640: undef,
641: $ENV{'request.course.id'});
642: if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
643: %StudentsData = @tmp;
644: }
645: #
646: # Write out sequence scores and totals data
647: foreach my $seq (&get_sequences_to_show) {
648: my ($performance,$score,$seq_max) =
649: &StudentPerformanceOnSequence($student,\%StudentsData,
650: $seq,'no');
651: if ($show eq 'totals' || $show eq 'scores') {
652: $excel_sheet->write($rows_output,$cols_output++,$score);
653: }
654: if ($show eq 'totals') {
655: $excel_sheet->write($rows_output,$cols_output++,$seq_max);
656: }
657: }
658: #
659: # Bookkeeping
660: $rows_output++;
661: $cols_output=0;
662: #
663: # Time estimate
664: $num_students++;
665: if ($num_students % 10 == 0) {
666: my $time_est = (time - $start_time)/$num_students *
667: (scalar(@Apache::lonstatistics::Students)-$num_students);
668: $time_est = int($time_est);
669: if (int ($time_est/60) > 0) {
670: my $min = int($time_est/60);
671: my $sec = $time_est % 60;
672: $time_est = $min.' minutes';
673: if ($sec > 1) {
674: $time_est.= ', '.$sec.' seconds';
675: } elsif ($sec > 0) {
676: $time_est.= ', '.$sec.' second';
677: }
678: } else {
679: $time_est .= ' seconds';
680: }
681: $r->print($num_students.' out of '.
682: (scalar(@Apache::lonstatistics::Students)).
683: " students processed. ".
684: $time_est." remain. <br />\n");
685: $r->rflush();
686: }
687: return;
1.31 matthew 688: }
689:
690: sub excel_finish {
691: my ($r) = @_;
1.32 matthew 692: return if (! defined($excel_sheet));
693: #
694: # Write the excel file
695: $excel_workbook->close();
696: my $c = $r->connection();
697: #
698: return if($c->aborted());
699: #
700: # Tell the user where to get their excel file
701: $r->print('<br /><br />'.
702: '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
703: my $total_time = time - $start_time;
704: if (int ($total_time / 60) > 0) {
705: $total_time = int($total_time/60).' minutes, '.($total_time % 60);
706: }
707: $r->print('<br />'.$total_time.' seconds total');
708: $r->rflush();
709: return;
1.31 matthew 710: }
711:
712: }
1.30 matthew 713: #######################################################
714: #######################################################
715:
716: =pod
717:
1.31 matthew 718: =head2 CSV output routines
719:
720: =item &csv_initialize($r)
721:
722: =item &csv_outputstudent($r,$student)
723:
724: =item &csv_finish($r)
1.30 matthew 725:
726: =cut
727:
728: #######################################################
729: #######################################################
1.31 matthew 730: {
731:
732: sub csv_initialize{
733: my ($r) = @_;
734: $r->print("<h1>Not implemented yet</h1>");
735: return;
736: }
737:
738: sub csv_outputstudent {
739: my ($r,$student) = @_;
740: }
741:
742: sub csv_finish {
743: my ($r) = @_;
744: }
1.2 stredwic 745:
746: }
747:
1.28 matthew 748: #######################################################
749: #######################################################
750:
1.2 stredwic 751: =pod
752:
1.30 matthew 753: =item &StudentPerformanceOnSequence()
1.2 stredwic 754:
1.30 matthew 755: Inputs:
1.2 stredwic 756:
757: =over 4
758:
1.30 matthew 759: =item $student
1.28 matthew 760:
1.30 matthew 761: =item $studentdata Hash ref to all student data
1.2 stredwic 762:
1.30 matthew 763: =item $seq Hash ref, the sequence we are working on
1.2 stredwic 764:
1.30 matthew 765: =item $links if defined we will output links to each resource.
1.2 stredwic 766:
1.28 matthew 767: =back
1.2 stredwic 768:
769: =cut
1.1 stredwic 770:
1.28 matthew 771: #######################################################
772: #######################################################
1.30 matthew 773: sub StudentPerformanceOnSequence {
1.32 matthew 774: my ($student,$studentdata,$seq,$links) = @_;
1.31 matthew 775: $links = 'no' if (! defined($links));
1.1 stredwic 776: my $Str = '';
1.30 matthew 777: my ($sum,$max) = (0,0);
778: foreach my $resource (@{$seq->{'contents'}}) {
779: next if ($resource->{'type'} ne 'assessment');
780: my $resource_data = $studentdata->{$resource->{'symb'}};
781: my $value = '';
782: foreach my $partnum (@{$resource->{'parts'}}) {
783: $max++;
784: my $symbol = ' '; # default to space
785: #
786: if (exists($resource_data->{'resource.'.$partnum.'.solved'})) {
787: my $status = $resource_data->{'resource.'.$partnum.'.solved'};
788: if ($status eq 'correct_by_override') {
789: $symbol = '+';
790: $sum++;
791: } elsif ($status eq 'incorrect_by_override') {
792: $symbol = '-';
793: } elsif ($status eq 'ungraded_attempted') {
794: $symbol = '#';
795: } elsif ($status eq 'incorrect_attempted') {
796: $symbol = '.';
797: } elsif ($status eq 'excused') {
798: $symbol = 'x';
799: $max--;
800: } elsif ($status eq 'correct_by_student' &&
801: exists($resource_data->{'resource.'.$partnum.'.tries'})){
802: my $num = $resource_data->{'resource.'.$partnum.'.tries'};
803: if ($num > 9) {
804: $symbol = '*';
805: } elsif ($num > 0) {
806: $symbol = $num;
807: } else {
808: $symbol = ' ';
809: }
810: $sum++;
811: } else {
812: $symbol = ' ';
1.2 stredwic 813: }
1.30 matthew 814: } else {
815: # Unsolved. Did they try?
816: if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
817: $symbol = '.';
818: } else {
819: $symbol = ' ';
1.18 matthew 820: }
1.2 stredwic 821: }
1.30 matthew 822: #
1.31 matthew 823: if ($links eq 'yes' && $symbol ne ' ') {
1.30 matthew 824: $symbol = '<a href="/adm/grades'.
825: '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
826: '&student='.$student->{'username'}.
827: '&domain='.$student->{'domain'}.
828: '&command=submission">'.$symbol.'</a>';
829: }
830: $value .= $symbol;
1.2 stredwic 831: }
1.30 matthew 832: $Str .= $value;
1.17 minaeibi 833: }
1.30 matthew 834: return ($Str,$sum,$max);
1.17 minaeibi 835: }
836:
1.28 matthew 837: #######################################################
838: #######################################################
1.17 minaeibi 839: sub StudentAverageTotal {
1.21 minaeibi 840: my ($cache, $students, $sequenceKeys)=@_;
1.23 minaeibi 841: my $Str = "\n<b>Summary Tables:</b>\n";
1.21 minaeibi 842: my %Correct = ();
843: my $ProblemsSolved = 0;
844: my $TotalProblems = 0;
845: my $StudentCount = 0;
846:
847: foreach my $name (@$students) {
848: $StudentCount++;
849: foreach my $sequence (@$sequenceKeys) {
1.23 minaeibi 850: $Correct{$sequence} +=
851: $cache->{$name.':'.$sequence.':problemsCorrect'};
1.17 minaeibi 852: }
1.21 minaeibi 853: $ProblemsSolved += $cache->{$name.':problemsSolved'};
854: $TotalProblems += $cache->{$name.':totalProblems'};
1.1 stredwic 855: }
1.25 matthew 856: if ($StudentCount) {
1.27 minaeibi 857: $ProblemsSolved = sprintf( "%.2f",
858: $ProblemsSolved/$StudentCount);
1.25 matthew 859: $TotalProblems /= $StudentCount;
860: } else {
861: $ProblemsSolved = 0;
862: $TotalProblems = 0;
863: }
1.27 minaeibi 864:
1.24 minaeibi 865: $Str .= '<table border=2 cellspacing="1">'."\n";
1.23 minaeibi 866: $Str .= '<tr><td><b>Students Count</b></td><td><b>'.
867: $StudentCount.'</b></td></tr>'."\n";
868: $Str .= '<tr><td><b>Total Problems</b></td><td><b>'.
869: $TotalProblems.'</b></td></tr>'."\n";
870: $Str .= '<tr><td><b>Average Correct</b></td><td><b>'.
871: $ProblemsSolved.'</b></td></tr>'."\n";
872: $Str .= '</table>'."\n";
873:
1.24 minaeibi 874: $Str .= '<table border=2 cellspacing="1">'."\n";
1.23 minaeibi 875: $Str .= '<tr><th>Title</th><th>Total Problems</th>'.
876: '<th>Average Correct</th></tr>'."\n";
877: foreach my $S(@$sequenceKeys) {
878: my $title=$cache->{$S.':title'};
879: #$Str .= $cache->{$S.':problems'};
1.24 minaeibi 880: #my @problems=split(':', $cache->{$S.':problems'});
1.23 minaeibi 881: #my $pCount=scalar @problems;
882: my $pCount=MaxSeqPr($cache,@$students[0],$S);
1.25 matthew 883: my $crr;
884: if ($StudentCount) {
885: $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
886: } else {
887: $crr="0.00";
888: }
1.23 minaeibi 889: $Str .= '<tr><td>'.$title.
890: '</td><td align=center>'.$pCount.
891: '</td><td align=center>'.$crr.
892: '</td></tr>'."\n";
1.10 stredwic 893: }
1.1 stredwic 894:
1.23 minaeibi 895: $Str .= '</table>'."\n";
896:
1.1 stredwic 897: return $Str;
898: }
1.23 minaeibi 899:
1.28 matthew 900: #######################################################
901: #######################################################
1.23 minaeibi 902:
1.2 stredwic 903: =pod
904:
905: =item &CreateLegend()
906:
907: This function returns a formatted string containing the legend for the
908: chart. The legend describes the symbols used to represent grades for
909: problems.
910:
911: =cut
912:
1.28 matthew 913: #######################################################
914: #######################################################
1.2 stredwic 915: sub CreateLegend {
916: my $Str = "<p><pre>".
1.13 minaeibi 917: " 1 correct by student in 1 try\n".
918: " 7 correct by student in 7 tries\n".
1.12 minaeibi 919: " * correct by student in more than 9 tries\n".
1.20 minaeibi 920: " + correct by hand grading or override\n".
1.12 minaeibi 921: " - incorrect by override\n".
922: " . incorrect attempted\n".
923: " # ungraded attempted\n".
1.13 minaeibi 924: " not attempted (blank field)\n".
1.12 minaeibi 925: " x excused".
1.17 minaeibi 926: "</pre><p>";
1.2 stredwic 927: return $Str;
928: }
929:
1.28 matthew 930: #######################################################
931: #######################################################
932:
1.30 matthew 933: =pod
1.2 stredwic 934:
935: =back
936:
937: =cut
938:
1.28 matthew 939: #######################################################
940: #######################################################
1.2 stredwic 941:
1.28 matthew 942: 1;
1.2 stredwic 943:
1.1 stredwic 944: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>