Annotation of loncom/interface/statistics/lonstudentassessment.pm, revision 1.32
1.1 stredwic 1: # The LearningOnline Network with CAPA
2: #
1.32 ! matthew 3: # $Id: lonstudentassessment.pm,v 1.31 2003/03/03 19:28:29 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
146: return;
147: }
148: #
149: #
150: my $initialize = \&html_initialize;
151: my $output_student = \&html_outputstudent;
152: my $finish = \&html_finish;
153: #
154: if ($output_mode eq 'excel') {
155: $initialize = \&excel_initialize;
156: $output_student = \&excel_outputstudent;
157: $finish = \&excel_finish;
158: } elsif ($output_mode eq 'csv') {
159: $initialize = \&csv_initialize;
160: $output_student = \&csv_outputstudent;
161: $finish = \&csv_finish;
162: }
1.30 matthew 163: #
164: if($c->aborted()) { return ; }
1.31 matthew 165: #
166: # Call the initialize routine selected above
167: $initialize->($r);
1.30 matthew 168: foreach my $student (@Apache::lonstatistics::Students) {
1.31 matthew 169: if($c->aborted()) {
170: $finish->($r);
171: return ;
1.1 stredwic 172: }
1.31 matthew 173: # Call the output_student routine selected above
174: $output_student->($r,$student);
175: }
176: # Call the "finish" routine selected above
177: $finish->($r);
178: #
179: return;
180: }
181:
182: #######################################################
183: #######################################################
1.30 matthew 184:
1.31 matthew 185: sub get_student_fields_to_show {
186: my @to_show = @Apache::lonstatistics::SelectedStudentData;
187: foreach (@to_show) {
188: if ($_ eq 'all') {
189: @to_show = @Apache::lonstatistics::StudentDataOrder;
190: last;
191: }
192: }
193: return @to_show;
194: }
195:
196: sub get_sequences_to_show {
197: my @Sequences;
198: foreach my $map_symb (@Apache::lonstatistics::SelectedMaps) {
199: foreach my $sequence (@Apache::lonstatistics::Sequences) {
200: next if ($sequence->{'symb'} ne $map_symb && $map_symb ne 'all');
201: next if ($sequence->{'num_assess'} < 1);
202: push (@Sequences,$sequence);
203: }
1.1 stredwic 204: }
1.31 matthew 205: return @Sequences;
1.2 stredwic 206: }
1.30 matthew 207:
1.31 matthew 208:
1.28 matthew 209: #######################################################
210: #######################################################
211:
212: =pod
1.2 stredwic 213:
1.28 matthew 214: =item &CreateInterface()
1.21 minaeibi 215:
1.28 matthew 216: Called by &BuildStudentAssessmentPage to create the top part of the
217: page which displays the chart.
218:
1.30 matthew 219: Inputs: None
1.28 matthew 220:
221: Returns: A string containing the HTML for the headers and top table for
222: the chart page.
223:
224: =cut
225:
226: #######################################################
227: #######################################################
1.2 stredwic 228: sub CreateInterface {
1.4 stredwic 229: my $Str = '';
1.30 matthew 230: # $Str .= &CreateLegend();
231: $Str .= '<table cellspacing="5">'."\n";
232: $Str .= '<tr>';
233: $Str .= '<td align="center"><b>Sections</b></td>';
234: $Str .= '<td align="center"><b>Student Data</b></td>';
235: $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
1.31 matthew 236: $Str .= '<td align="center"><b>Output Format</b></td>';
1.30 matthew 237: $Str .= '</tr>'."\n";
238: #
1.4 stredwic 239: $Str .= '<tr><td align="center">'."\n";
1.29 matthew 240: $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
1.4 stredwic 241: $Str .= '</td><td align="center">';
1.30 matthew 242: my $only_seq_with_assessments = sub {
243: my $s=shift;
244: if ($s->{'num_assess'} < 1) {
245: return 0;
246: } else {
247: return 1;
248: }
249: };
250: $Str .= &Apache::lonstatistics::StudentDataSelect('StudentData','multiple',
251: 5,undef);
1.4 stredwic 252: $Str .= '</td><td>'."\n";
1.30 matthew 253: $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
254: $only_seq_with_assessments);
1.31 matthew 255: $Str .= '</td><td>'."\n";
256: $Str .= &CreateAndParseOutputSelector();
1.30 matthew 257: $Str .= '</td></tr>'."\n";
258: $Str .= '</table>'."\n";
1.4 stredwic 259: return $Str;
1.1 stredwic 260: }
1.30 matthew 261:
262: #######################################################
263: #######################################################
264:
265: =pod
266:
1.31 matthew 267: =item &CreateAndParseOutputSelector()
1.30 matthew 268:
269: =cut
270:
271: #######################################################
272: #######################################################
1.32 ! matthew 273: my @OutputOptions =
! 274: ({ name => 'HTML, with links',
! 275: value => 'html, with links',
! 276: description => ''},
! 277: { name => 'HTML, without links',
! 278: value => 'html, without links',
! 279: description => ''},
! 280: { name => 'HTML, totals',
! 281: value => 'html, totals',
! 282: description => ''},
! 283: { name => 'HTML, scores only',
! 284: value => 'html, scores only',
! 285: description => ''},
! 286: { name => 'Excel, totals',
! 287: value => 'excel, totals',
! 288: description => ''},
! 289: { name => 'Excel, scores only',
! 290: value => 'excel, scores only',
! 291: description => ''},
! 292: { name => 'CSV, totals',
! 293: value => 'csv, totals',
! 294: description => ''},
! 295: { name => 'CSV, scores only',
! 296: value => 'csv, scores only',
! 297: description => ''},
! 298: { name => 'CSV, everything',
! 299: value => 'csv, everything',
! 300: description => ''}
! 301: );
! 302:
1.31 matthew 303: sub CreateAndParseOutputSelector {
304: my $Str = '';
305: my $elementname = 'outputmode';
306: #
307: # Format for output options is 'mode, restrictions';
308: my $selected = 'html, with links';
309: if (exists($ENV{'form.'.$elementname})) {
310: if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
311: $selected = $ENV{'form.'.$elementname}->[0];
312: } else {
313: $selected = $ENV{'form.'.$elementname};
314: }
315: }
316: #
317: # Set package variables describing output mode
318: $show_links = 'no';
319: $output_mode = 'html';
320: $show = 'all';
321: my ($mode,$restriction) = split(',',$selected);
322: $restriction =~ s/^\s*//;
323: if ($mode =~ /^(html|excel|csv)$/) {
324: $output_mode = $mode;
325: } else {
326: $output_mode = 'html';
327: }
328: if ($restriction eq 'with links') {
329: $show_links = 'yes';
330: } else {
331: $show_links = 'no';
332: }
1.32 ! matthew 333: if ($restriction eq 'totals') {
1.31 matthew 334: $show = 'totals';
1.32 ! matthew 335: } elsif ($restriction eq 'scores only') {
! 336: $show = 'scores';
1.31 matthew 337: } else {
338: $show = 'everything';
339: }
340: #
341: # Build the form element
342: $Str = qq/<select size="5" name="$elementname">/;
1.32 ! matthew 343: foreach my $option (@OutputOptions) {
! 344: $Str .= "\n".' <option value="'.$option->{'value'}.'"';
! 345: $Str .= " selected " if ($option->{'value'} eq $selected);
! 346: $Str .= ">".$option->{'name'}."<\/option>";
1.31 matthew 347: }
348: $Str .= "\n</select>";
349: return $Str;
350: }
1.30 matthew 351:
1.28 matthew 352: #######################################################
353: #######################################################
1.1 stredwic 354:
1.28 matthew 355: =pod
356:
1.31 matthew 357: =head2 HTML output routines
1.28 matthew 358:
1.31 matthew 359: =item &html_initialize($r)
1.28 matthew 360:
1.31 matthew 361: Create labels for the columns of student data to show.
1.28 matthew 362:
1.31 matthew 363: =item &html_outputstudent($r,$student)
1.28 matthew 364:
1.31 matthew 365: Return a line of the chart for a student.
1.28 matthew 366:
1.31 matthew 367: =item &html_finish($r)
1.28 matthew 368:
369: =cut
370:
371: #######################################################
372: #######################################################
1.31 matthew 373: {
374: my $padding;
375: my $count;
376:
377: sub html_initialize {
378: my ($r) = @_;
1.30 matthew 379: #
380: $padding = ' 'x3;
1.31 matthew 381: $count = 1;
1.30 matthew 382: #
1.31 matthew 383: my $Str = "<pre>\n";
1.30 matthew 384: # First, the @StudentData fields need to be listed
1.31 matthew 385: my @to_show = &get_student_fields_to_show();
1.30 matthew 386: foreach my $field (@to_show) {
387: my $title=$Apache::lonstatistics::StudentData{$field}->{'title'};
388: my $base =$Apache::lonstatistics::StudentData{$field}->{'base_width'};
389: my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
390: $Str .= $title.' 'x($width-$base).$padding;
391: }
392: # Now the selected sequences need to be listed
1.31 matthew 393: foreach my $sequence (&get_sequences_to_show) {
394: my $title = $sequence->{'title'};
395: my $base = $sequence->{'base_width'};
396: my $width = $sequence->{'width'};
397: $Str .= $title.' 'x($width-$base).$padding;
1.30 matthew 398: }
1.31 matthew 399: $Str .= "total (of shown problems)</pre>\n";
400: $Str .= "<pre>";
401: $r->print($Str);
402: $r->rflush();
403: return;
1.30 matthew 404: }
405:
1.31 matthew 406: sub html_outputstudent {
407: my ($r,$student) = @_;
1.2 stredwic 408: my $Str = '';
1.30 matthew 409: # First, the @StudentData fields need to be listed
1.31 matthew 410: my @to_show = &get_student_fields_to_show();
1.30 matthew 411: foreach my $field (@to_show) {
412: my $title=$student->{$field};
1.31 matthew 413: my $base = length($title);
1.30 matthew 414: my $width=$Apache::lonstatistics::StudentData{$field}->{'width'};
415: $Str .= $title.' 'x($width-$base).$padding;
416: }
417: # Get ALL the students data
418: my %StudentsData;
419: my @tmp = &Apache::loncoursedata::get_current_state
420: ($student->{'username'},$student->{'domain'},undef,
421: $ENV{'request.course.id'});
422: if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
423: %StudentsData = @tmp;
424: }
425: if (scalar(@tmp) < 1) {
426: $Str .= '<font color="blue">No Course Data</font>'."\n";
1.31 matthew 427: $r->print($Str);
428: $r->rflush();
429: return;
1.30 matthew 430: }
431: #
432: # By sequence build up the data
433: my $studentstats;
1.31 matthew 434: my $PerformanceStr = '';
435: foreach my $seq (&get_sequences_to_show) {
436: my ($performance,$score,$seq_max) =
437: &StudentPerformanceOnSequence($student,\%StudentsData,
438: $seq,$show_links);
439: my $ratio = $score.'/'.$seq_max;
440: #
441: if ($show eq 'totals') {
442: $performance = ' 'x(length($seq_max)-length($score)).$ratio;
443: $performance .= ' 'x($seq->{'width'}-length($performance));
1.32 ! matthew 444: } elsif ($show eq 'scores') {
! 445: $performance = $score;
! 446: $performance .= ' 'x($seq->{'width'}-length($performance));
1.31 matthew 447: } else {
448: # Pad with extra spaces
449: $performance .= ' 'x($seq->{'width'}-$seq_max-
450: length($ratio)
451: ).$ratio;
1.30 matthew 452: }
1.31 matthew 453: #
454: $Str .= $performance.$padding;
455: #
456: $studentstats->{$seq->{'symb'}}->{'score'}= $score;
457: $studentstats->{$seq->{'symb'}}->{'max'} = $seq_max;
1.30 matthew 458: }
459: #
460: # Total it up and store the statistics info.
461: my ($score,$max) = (0,0);
462: while (my ($symb,$seq_stats) = each (%{$studentstats})) {
463: $Statistics->{$symb}->{'score'} += $seq_stats->{'score'};
464: $Statistics->{$symb}->{'max'} += $seq_stats->{'max'};
465: $score += $seq_stats->{'score'};
466: $max += $seq_stats->{'max'};
467: }
1.31 matthew 468: $Str .= ' '.' 'x(length($max)-length($score)).$score.'/'.$max;
1.30 matthew 469: $Str .= " \n";
1.31 matthew 470: $r->print($Str);
471: #
472: $count++;
473: if($count % 5 == 0) {
474: $r->print("</pre><pre>");
475: }
476: #
477: $r->rflush();
478: return;
1.30 matthew 479: }
1.2 stredwic 480:
1.31 matthew 481: sub html_finish {
482: my ($r) = @_;
483: $r->print("</pre>\n");
484: $r->rflush();
485: return;
486: }
487:
488: }
489:
490: #######################################################
491: #######################################################
492:
493: =pod
494:
495: =head2 EXCEL subroutines
496:
497: =item &excel_initialize($r)
498:
499: =item &excel_outputstudent($r,$student)
500:
501: =item &excel_finish($r)
502:
503: =cut
504:
505: #######################################################
506: #######################################################
507: {
508:
509: my $excel_sheet;
1.32 ! matthew 510: my $excel_workbook;
! 511:
! 512: my $filename;
! 513: my $rows_output;
! 514: my $cols_output;
! 515:
! 516: my $num_students;
! 517: my $start_time;
1.31 matthew 518:
519: sub excel_initialize {
520: my ($r) = @_;
521: #
1.32 ! matthew 522: $filename = '/prtspool/'.
1.31 matthew 523: $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
524: time.'_'.rand(1000000000).'.xls';
1.32 ! matthew 525: #
! 526: $excel_workbook = undef;
! 527: $excel_sheet = undef;
! 528: #
! 529: $rows_output = 0;
! 530: $cols_output = 0;
! 531: #
! 532: $num_students = 0;
! 533: $start_time = time;
! 534: #
! 535: # Create sheet
! 536: $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
! 537: #
! 538: # Check for errors
! 539: if (! defined($excel_workbook)) {
1.31 matthew 540: $r->log_error("Error creating excel spreadsheet $filename: $!");
541: $r->print("Problems creating new Excel file. ".
542: "This error has been logged. ".
543: "Please alert your LON-CAPA administrator");
1.32 ! matthew 544: return ;
1.31 matthew 545: }
546: #
547: # The excel spreadsheet stores temporary data in files, then put them
548: # together. If needed we should be able to disable this (memory only).
549: # The temporary directory must be specified before calling 'addworksheet'.
550: # File::Temp is used to determine the temporary directory.
1.32 ! matthew 551: $excel_workbook->set_tempdir($Apache::lonnet::tmpdir);
! 552: #
! 553: # Add a worksheet
! 554: $excel_sheet = $excel_workbook->addworksheet
! 555: ($ENV{'course.'.$ENV{'request.course.id'}.'.description'});
! 556: #
! 557: # Add the student headers
! 558: foreach my $field (&get_student_fields_to_show()) {
! 559: $excel_sheet->write(1,$cols_output++,$field);
! 560: }
! 561: #
! 562: # Add the Sequence Headers
! 563: foreach my $seq (&get_sequences_to_show) {
! 564: $excel_sheet->write(0,$cols_output,$seq->{'title'});
! 565: if ($show eq 'totals') {
! 566: $excel_sheet->write(1,$cols_output,'score');
! 567: $excel_sheet->write(1,$cols_output+1,'maximum');
! 568: $cols_output += 2;
! 569: } else {
! 570: $cols_output++;
! 571: }
! 572: }
! 573: #
! 574: # Bookkeeping
! 575: if ($show eq 'totals') {
! 576: $rows_output = 2;
! 577: } else {
! 578: $rows_output = 1;
! 579: }
! 580: #
! 581: # Let the user know what we are doing
! 582: my $studentcount = scalar(@Apache::lonstatistics::Students);
! 583: $r->print("<h1>Compiling Excel spreadsheet for ".
! 584: $studentcount.' student');
! 585: $r->print('s') if ($studentcount > 1);
! 586: $r->print("</h1>\n");
! 587: $r->rflush();
1.31 matthew 588: #
589: return;
590: }
591:
592: sub excel_outputstudent {
593: my ($r,$student) = @_;
1.32 ! matthew 594: return if (! defined($excel_sheet));
! 595: $cols_output=0;
! 596: #
! 597: # Write out student data
! 598: my @to_show = &get_student_fields_to_show();
! 599: foreach my $field (@to_show) {
! 600: $excel_sheet->write($rows_output,$cols_output++,$student->{$field});
! 601: }
! 602: #
! 603: # Get student assessment data
! 604: my %StudentsData;
! 605: my @tmp = &Apache::loncoursedata::get_current_state($student->{'username'},
! 606: $student->{'domain'},
! 607: undef,
! 608: $ENV{'request.course.id'});
! 609: if ((scalar @tmp > 0) && ($tmp[0] !~ /^error:/)) {
! 610: %StudentsData = @tmp;
! 611: }
! 612: #
! 613: # Write out sequence scores and totals data
! 614: foreach my $seq (&get_sequences_to_show) {
! 615: my ($performance,$score,$seq_max) =
! 616: &StudentPerformanceOnSequence($student,\%StudentsData,
! 617: $seq,'no');
! 618: if ($show eq 'totals' || $show eq 'scores') {
! 619: $excel_sheet->write($rows_output,$cols_output++,$score);
! 620: }
! 621: if ($show eq 'totals') {
! 622: $excel_sheet->write($rows_output,$cols_output++,$seq_max);
! 623: }
! 624: }
! 625: #
! 626: # Bookkeeping
! 627: $rows_output++;
! 628: $cols_output=0;
! 629: #
! 630: # Time estimate
! 631: $num_students++;
! 632: if ($num_students % 10 == 0) {
! 633: my $time_est = (time - $start_time)/$num_students *
! 634: (scalar(@Apache::lonstatistics::Students)-$num_students);
! 635: $time_est = int($time_est);
! 636: if (int ($time_est/60) > 0) {
! 637: my $min = int($time_est/60);
! 638: my $sec = $time_est % 60;
! 639: $time_est = $min.' minutes';
! 640: if ($sec > 1) {
! 641: $time_est.= ', '.$sec.' seconds';
! 642: } elsif ($sec > 0) {
! 643: $time_est.= ', '.$sec.' second';
! 644: }
! 645: } else {
! 646: $time_est .= ' seconds';
! 647: }
! 648: $r->print($num_students.' out of '.
! 649: (scalar(@Apache::lonstatistics::Students)).
! 650: " students processed. ".
! 651: $time_est." remain. <br />\n");
! 652: $r->rflush();
! 653: }
! 654: return;
1.31 matthew 655: }
656:
657: sub excel_finish {
658: my ($r) = @_;
1.32 ! matthew 659: return if (! defined($excel_sheet));
! 660: #
! 661: # Write the excel file
! 662: $excel_workbook->close();
! 663: my $c = $r->connection();
! 664: #
! 665: return if($c->aborted());
! 666: #
! 667: # Tell the user where to get their excel file
! 668: $r->print('<br /><br />'.
! 669: '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
! 670: my $total_time = time - $start_time;
! 671: if (int ($total_time / 60) > 0) {
! 672: $total_time = int($total_time/60).' minutes, '.($total_time % 60);
! 673: }
! 674: $r->print('<br />'.$total_time.' seconds total');
! 675: $r->rflush();
! 676: return;
1.31 matthew 677: }
678:
679: }
1.30 matthew 680: #######################################################
681: #######################################################
682:
683: =pod
684:
1.31 matthew 685: =head2 CSV output routines
686:
687: =item &csv_initialize($r)
688:
689: =item &csv_outputstudent($r,$student)
690:
691: =item &csv_finish($r)
1.30 matthew 692:
693: =cut
694:
695: #######################################################
696: #######################################################
1.31 matthew 697: {
698:
699: sub csv_initialize{
700: my ($r) = @_;
701: $r->print("<h1>Not implemented yet</h1>");
702: return;
703: }
704:
705: sub csv_outputstudent {
706: my ($r,$student) = @_;
707: }
708:
709: sub csv_finish {
710: my ($r) = @_;
711: }
1.2 stredwic 712:
713: }
714:
1.28 matthew 715: #######################################################
716: #######################################################
717:
1.2 stredwic 718: =pod
719:
1.30 matthew 720: =item &StudentPerformanceOnSequence()
1.2 stredwic 721:
1.30 matthew 722: Inputs:
1.2 stredwic 723:
724: =over 4
725:
1.30 matthew 726: =item $student
1.28 matthew 727:
1.30 matthew 728: =item $studentdata Hash ref to all student data
1.2 stredwic 729:
1.30 matthew 730: =item $seq Hash ref, the sequence we are working on
1.2 stredwic 731:
1.30 matthew 732: =item $links if defined we will output links to each resource.
1.2 stredwic 733:
1.28 matthew 734: =back
1.2 stredwic 735:
736: =cut
1.1 stredwic 737:
1.28 matthew 738: #######################################################
739: #######################################################
1.30 matthew 740: sub StudentPerformanceOnSequence {
1.32 ! matthew 741: my ($student,$studentdata,$seq,$links) = @_;
1.31 matthew 742: $links = 'no' if (! defined($links));
1.1 stredwic 743: my $Str = '';
1.30 matthew 744: my ($sum,$max) = (0,0);
745: foreach my $resource (@{$seq->{'contents'}}) {
746: next if ($resource->{'type'} ne 'assessment');
747: my $resource_data = $studentdata->{$resource->{'symb'}};
748: my $value = '';
749: foreach my $partnum (@{$resource->{'parts'}}) {
750: $max++;
751: my $symbol = ' '; # default to space
752: #
753: if (exists($resource_data->{'resource.'.$partnum.'.solved'})) {
754: my $status = $resource_data->{'resource.'.$partnum.'.solved'};
755: if ($status eq 'correct_by_override') {
756: $symbol = '+';
757: $sum++;
758: } elsif ($status eq 'incorrect_by_override') {
759: $symbol = '-';
760: } elsif ($status eq 'ungraded_attempted') {
761: $symbol = '#';
762: } elsif ($status eq 'incorrect_attempted') {
763: $symbol = '.';
764: } elsif ($status eq 'excused') {
765: $symbol = 'x';
766: $max--;
767: } elsif ($status eq 'correct_by_student' &&
768: exists($resource_data->{'resource.'.$partnum.'.tries'})){
769: my $num = $resource_data->{'resource.'.$partnum.'.tries'};
770: if ($num > 9) {
771: $symbol = '*';
772: } elsif ($num > 0) {
773: $symbol = $num;
774: } else {
775: $symbol = ' ';
776: }
777: $sum++;
778: } else {
779: $symbol = ' ';
1.2 stredwic 780: }
1.30 matthew 781: } else {
782: # Unsolved. Did they try?
783: if (exists($resource_data->{'resource.'.$partnum.'.tries'})){
784: $symbol = '.';
785: } else {
786: $symbol = ' ';
1.18 matthew 787: }
1.2 stredwic 788: }
1.30 matthew 789: #
1.31 matthew 790: if ($links eq 'yes' && $symbol ne ' ') {
1.30 matthew 791: $symbol = '<a href="/adm/grades'.
792: '?symb='.&Apache::lonnet::escape($resource->{'symb'}).
793: '&student='.$student->{'username'}.
794: '&domain='.$student->{'domain'}.
795: '&command=submission">'.$symbol.'</a>';
796: }
797: $value .= $symbol;
1.2 stredwic 798: }
1.30 matthew 799: $Str .= $value;
1.17 minaeibi 800: }
1.30 matthew 801: return ($Str,$sum,$max);
1.17 minaeibi 802: }
803:
1.28 matthew 804: #######################################################
805: #######################################################
1.17 minaeibi 806: sub StudentAverageTotal {
1.21 minaeibi 807: my ($cache, $students, $sequenceKeys)=@_;
1.23 minaeibi 808: my $Str = "\n<b>Summary Tables:</b>\n";
1.21 minaeibi 809: my %Correct = ();
810: my $ProblemsSolved = 0;
811: my $TotalProblems = 0;
812: my $StudentCount = 0;
813:
814: foreach my $name (@$students) {
815: $StudentCount++;
816: foreach my $sequence (@$sequenceKeys) {
1.23 minaeibi 817: $Correct{$sequence} +=
818: $cache->{$name.':'.$sequence.':problemsCorrect'};
1.17 minaeibi 819: }
1.21 minaeibi 820: $ProblemsSolved += $cache->{$name.':problemsSolved'};
821: $TotalProblems += $cache->{$name.':totalProblems'};
1.1 stredwic 822: }
1.25 matthew 823: if ($StudentCount) {
1.27 minaeibi 824: $ProblemsSolved = sprintf( "%.2f",
825: $ProblemsSolved/$StudentCount);
1.25 matthew 826: $TotalProblems /= $StudentCount;
827: } else {
828: $ProblemsSolved = 0;
829: $TotalProblems = 0;
830: }
1.27 minaeibi 831:
1.24 minaeibi 832: $Str .= '<table border=2 cellspacing="1">'."\n";
1.23 minaeibi 833: $Str .= '<tr><td><b>Students Count</b></td><td><b>'.
834: $StudentCount.'</b></td></tr>'."\n";
835: $Str .= '<tr><td><b>Total Problems</b></td><td><b>'.
836: $TotalProblems.'</b></td></tr>'."\n";
837: $Str .= '<tr><td><b>Average Correct</b></td><td><b>'.
838: $ProblemsSolved.'</b></td></tr>'."\n";
839: $Str .= '</table>'."\n";
840:
1.24 minaeibi 841: $Str .= '<table border=2 cellspacing="1">'."\n";
1.23 minaeibi 842: $Str .= '<tr><th>Title</th><th>Total Problems</th>'.
843: '<th>Average Correct</th></tr>'."\n";
844: foreach my $S(@$sequenceKeys) {
845: my $title=$cache->{$S.':title'};
846: #$Str .= $cache->{$S.':problems'};
1.24 minaeibi 847: #my @problems=split(':', $cache->{$S.':problems'});
1.23 minaeibi 848: #my $pCount=scalar @problems;
849: my $pCount=MaxSeqPr($cache,@$students[0],$S);
1.25 matthew 850: my $crr;
851: if ($StudentCount) {
852: $crr=sprintf( "%.2f", $Correct{$S}/$StudentCount );
853: } else {
854: $crr="0.00";
855: }
1.23 minaeibi 856: $Str .= '<tr><td>'.$title.
857: '</td><td align=center>'.$pCount.
858: '</td><td align=center>'.$crr.
859: '</td></tr>'."\n";
1.10 stredwic 860: }
1.1 stredwic 861:
1.23 minaeibi 862: $Str .= '</table>'."\n";
863:
1.1 stredwic 864: return $Str;
865: }
1.23 minaeibi 866:
1.28 matthew 867: #######################################################
868: #######################################################
1.23 minaeibi 869:
1.2 stredwic 870: =pod
871:
872: =item &CreateLegend()
873:
874: This function returns a formatted string containing the legend for the
875: chart. The legend describes the symbols used to represent grades for
876: problems.
877:
878: =cut
879:
1.28 matthew 880: #######################################################
881: #######################################################
1.2 stredwic 882: sub CreateLegend {
883: my $Str = "<p><pre>".
1.13 minaeibi 884: " 1 correct by student in 1 try\n".
885: " 7 correct by student in 7 tries\n".
1.12 minaeibi 886: " * correct by student in more than 9 tries\n".
1.20 minaeibi 887: " + correct by hand grading or override\n".
1.12 minaeibi 888: " - incorrect by override\n".
889: " . incorrect attempted\n".
890: " # ungraded attempted\n".
1.13 minaeibi 891: " not attempted (blank field)\n".
1.12 minaeibi 892: " x excused".
1.17 minaeibi 893: "</pre><p>";
1.2 stredwic 894: return $Str;
895: }
896:
1.28 matthew 897: #######################################################
898: #######################################################
899:
1.30 matthew 900: =pod
1.2 stredwic 901:
902: =back
903:
904: =cut
905:
1.28 matthew 906: #######################################################
907: #######################################################
1.2 stredwic 908:
1.28 matthew 909: 1;
1.2 stredwic 910:
1.1 stredwic 911: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>