Annotation of loncom/interface/statistics/lonproblemstatistics.pm, revision 1.47
1.1 stredwic 1: # The LearningOnline Network with CAPA
2: #
1.47 ! matthew 3: # $Id: lonproblemstatistics.pm,v 1.46 2003/03/26 21:55:18 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: #
9: # LON-CAPA is free software; you can redistribute it and/or modify
10: # it under the terms of the GNU General Public License as published by
11: # the Free Software Foundation; either version 2 of the License, or
12: # (at your option) any later version.
13: #
14: # LON-CAPA is distributed in the hope that it will be useful,
15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17: # GNU General Public License for more details.
18: #
19: # You should have received a copy of the GNU General Public License
20: # along with LON-CAPA; if not, write to the Free Software
21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22: #
23: # /home/httpd/html/adm/gpl.txt
24: #
25: # http://www.lon-capa.org/
26: #
27: # (Navigate problems for statistical reports
28: #
1.47 ! matthew 29: ###############################################
! 30: ###############################################
! 31:
! 32: =pod
! 33:
! 34: =head1 NAME
! 35:
! 36: lonproblemstatistics
! 37:
! 38: =head1 SYNOPSIS
! 39:
! 40: Routines to present problem statistics to instructors via tables,
! 41: Excel files, and plots.
! 42:
! 43: =over 4
! 44:
! 45: =cut
! 46:
! 47: ###############################################
! 48: ###############################################
1.1 stredwic 49:
1.36 minaeibi 50: package Apache::lonproblemstatistics;
1.1 stredwic 51:
52: use strict;
53: use Apache::lonnet();
54: use Apache::lonhtmlcommon;
55: use Apache::loncoursedata;
1.41 matthew 56: use Apache::lonstatistics;
1.44 matthew 57: use Spreadsheet::WriteExcel;
1.1 stredwic 58:
1.47 ! matthew 59: ###############################################
! 60: ###############################################
! 61:
! 62: =pod
! 63:
! 64: =item &CreateInterface()
! 65:
! 66: Create the main intereface for the statistics page. Allows the user to
! 67: select sections, maps, and output.
! 68:
! 69: =cut
1.1 stredwic 70:
1.47 ! matthew 71: ###############################################
! 72: ###############################################
1.41 matthew 73: sub CreateInterface {
74: my $Str = '';
75: $Str .= '<table cellspacing="5">'."\n";
76: $Str .= '<tr>';
77: $Str .= '<td align="center"><b>Sections</b></td>';
78: $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
79: $Str .= '<td align="center"><b>Output</b></td>';
80: $Str .= '</tr>'."\n";
81: #
82: $Str .= '<tr><td align="center">'."\n";
83: $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
84: $Str .= '</td><td align="center">';
85: #
86: my $only_seq_with_assessments = sub {
87: my $s=shift;
88: if ($s->{'num_assess'} < 1) {
89: return 0;
90: } else {
91: return 1;
92: }
93: };
94: $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
95: $only_seq_with_assessments);
96: $Str .= '</td><td>'."\n";
97: $Str .= &CreateAndParseOutputSelector();
98: $Str .= '</td></tr>'."\n";
99: $Str .= '</table>'."\n";
100: return $Str;
101: }
102:
103: #######################################################
104: #######################################################
105:
106: =pod
107:
108: =item &CreateAndParseOutputSelector()
109:
1.47 ! matthew 110: Construct a selection list of options for output and parse output selections.
! 111: The current output selected is indicated by the values of the two package
! 112: variables $output_mode and $show. @OutputOptions holds the descriptions of
! 113: the output options and the values for $output_mode and $show.
! 114:
! 115: Based on code from lonstudentassessment.pm.
! 116:
1.41 matthew 117: =cut
1.16 minaeibi 118:
1.41 matthew 119: #######################################################
120: #######################################################
121: my $output_mode;
122: my $show;
123:
124: my @OutputOptions =
125: (
126: { name => 'problem statistics grouped by sequence',
127: value => 'HTML problem statistics grouped',
128: description => 'Output statistics for the problem parts.',
129: mode => 'html',
130: show => 'grouped',
131: },
132: { name => 'problem statistics ungrouped',
133: value => 'HTML problem statistics ungrouped',
134: description => 'Output statistics for the problem parts.',
135: mode => 'html',
136: show => 'ungrouped',
137: },
138: { name => 'problem statistics, Excel',
139: value => 'Excel problem statistics',
140: description => 'Output statistics for the problem parts '.
141: 'in an Excel workbook',
142: mode => 'excel',
143: show => 'all',
144: },
145: { name => 'Degree of Difficulty Plot',
146: value => 'plot deg diff',
147: description => 'Generate a plot of the degree of difficulty of each '.
148: 'problem part.',
149: mode => 'plot',
150: show => 'deg of diff',
151: },
152: { name => 'Percent Wrong Plot',
153: value => 'plot per wrong',
154: description => 'Generate a plot showing the percent of students who '.
155: 'were unable to complete each problem part',
156: mode => 'plot',
157: show => 'per wrong',
158: },
159: );
160:
161: sub OutputDescriptions {
162: my $Str = '';
163: $Str .= "<h2>Output Modes</h2>\n";
164: $Str .= "<dl>\n";
165: foreach my $outputmode (@OutputOptions) {
166: $Str .=" <dt>".$outputmode->{'name'}."</dt>\n";
167: $Str .=" <dd>".$outputmode->{'description'}."</dd>\n";
1.1 stredwic 168: }
1.41 matthew 169: $Str .= "</dl>\n";
170: return $Str;
171: }
1.1 stredwic 172:
1.41 matthew 173: sub CreateAndParseOutputSelector {
174: my $Str = '';
1.47 ! matthew 175: my $elementname = 'statsoutputmode';
1.41 matthew 176: #
177: # Format for output options is 'mode, restrictions';
1.47 ! matthew 178: my $selected = 'HTML problem statistics grouped';
1.41 matthew 179: if (exists($ENV{'form.'.$elementname})) {
180: if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
181: $selected = $ENV{'form.'.$elementname}->[0];
182: } else {
183: $selected = $ENV{'form.'.$elementname};
1.25 stredwic 184: }
185: }
1.41 matthew 186: #
187: # Set package variables describing output mode
188: $output_mode = 'html';
189: $show = 'all';
190: foreach my $option (@OutputOptions) {
191: next if ($option->{'value'} ne $selected);
192: $output_mode = $option->{'mode'};
193: $show = $option->{'show'};
194: }
195: #
196: # Build the form element
197: $Str = qq/<select size="5" name="$elementname">/;
198: foreach my $option (@OutputOptions) {
199: $Str .= "\n".' <option value="'.$option->{'value'}.'"';
200: $Str .= " selected " if ($option->{'value'} eq $selected);
201: $Str .= ">".$option->{'name'}."<\/option>";
202: }
203: $Str .= "\n</select>";
204: return $Str;
205: }
1.25 stredwic 206:
1.41 matthew 207: ###############################################
208: ###############################################
1.28 stredwic 209:
1.47 ! matthew 210: =pod
! 211:
! 212: =item &Gather_Student_Data()
! 213:
! 214: Ensures all student data is up to date.
! 215:
! 216: =cut
! 217:
1.41 matthew 218: ###############################################
219: ###############################################
220: sub Gather_Student_Data {
221: my ($r) = @_;
222: my $c = $r->connection();
223: #
224: my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
225: #
226: my @Students = @Apache::lonstatistics::Students;
227: #
228: # Open the progress window
229: my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
230: ($r,'Statistics Compilation Status',
231: 'Statistics Compilation Progress', scalar(@Students));
232: #
233: while (my $student = shift @Students) {
234: return if ($c->aborted());
235: my ($status,undef) = &Apache::loncoursedata::ensure_current_data
236: ($student->{'username'},$student->{'domain'},
237: $ENV{'request.course.id'});
238: &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
239: 'last student');
1.28 stredwic 240: }
1.41 matthew 241: &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
242: $r->rflush();
243: }
1.28 stredwic 244:
1.41 matthew 245: ###############################################
246: ###############################################
1.21 stredwic 247:
1.47 ! matthew 248: =pod
! 249:
! 250: =item &BuildProblemStatisticsPage()
! 251:
! 252: Main interface to problem statistics.
! 253:
! 254: =cut
! 255:
1.41 matthew 256: ###############################################
257: ###############################################
258: sub BuildProblemStatisticsPage {
259: my ($r,$c)=@_;
260: #
261: $output_mode = 'html';
262: $show = 'grouped';
263: #
264: $r->print(&CreateInterface());
265: $r->print('<input type="hidden" name="statsfirstcall" value="no" />');
266: $r->print('<input type="hidden" name="sortby" value="'.$ENV{'form.sortby'}.
267: '" />');
268: if (! exists($ENV{'form.statsfirstcall'})) {
1.47 ! matthew 269: $r->print(<<ENDMSG);
! 270: <p>
! 271: <font size="+1">
! 272: Please make your selections in the boxes above and hit
! 273: the button marked "Update Display".
! 274: </font>
! 275: </p>
! 276: ENDMSG
1.41 matthew 277: return;
1.28 stredwic 278: }
1.41 matthew 279: #
280: &Gather_Student_Data($r);
281: #
282: #
283: if ($output_mode eq 'html') {
284: $r->print("<h2>".
285: $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.
286: "</h2>\n");
287: $r->print("<h3>".localtime(time)."</h3>");
288: $r->rflush();
289: if ($show eq 'grouped') {
290: &output_html_grouped_by_sequence($r);
291: } elsif ($show eq 'ungrouped') {
292: &output_html_ungrouped($r);
293: }
1.44 matthew 294: } elsif ($output_mode eq 'excel') {
295: $r->print("<h2>Preparing Excel Spreadsheet</h2>");
296: &output_excel($r);
1.45 matthew 297: } elsif ($output_mode eq 'plot') {
298: if ($show eq 'deg of diff') {
299: &plot_statistics($r,'DoDiff');
300: } elsif ($show eq 'per wrong') {
301: &plot_statistics($r,'%Wrng');
302: }
1.41 matthew 303: } else {
304: $r->print("<h1>Not implemented</h1>");
1.28 stredwic 305: }
1.41 matthew 306: return;
307: }
1.21 stredwic 308:
1.44 matthew 309: ###############################################
310: ###############################################
311:
1.47 ! matthew 312: =pod
! 313:
! 314: =item &output_html_grouped_by_sequence()
! 315:
! 316: Presents the statistics data as an html table organized by the order
! 317: the assessments appear in the course.
! 318:
! 319: =cut
! 320:
1.44 matthew 321: ###############################################
322: ###############################################
1.41 matthew 323: sub output_html_grouped_by_sequence {
324: my ($r) = @_;
1.45 matthew 325: my $problem_num = 0;
1.41 matthew 326: #$r->print(&ProblemStatisticsLegend());
327: my @Header = ("Title","Part","#Stdnts","Tries","Mod",
328: "Mean","#YES","#yes","%Wrng","DoDiff",
329: "S.D.","Skew.");#,"D.F.1st","D.F.2nd");
330: # #FFFFE6 #EEFFCC #DDFFFF FFDDDD #DDFFDD #FFDDFF
331: foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
332: next if ($sequence->{'num_assess'}<1);
333: $r->print("<h3>".$sequence->{'title'}."</h3>");
334: $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
335: $r->print('<table border="0" cellpadding="3">'."\n");
336: $r->print('<tr bgcolor="#FFFFE6"><th>'.
337: join("</th><th>",@Header)."</th></tr>\n");
338: foreach my $resource (@{$sequence->{'contents'}}) {
339: next if ($resource->{'type'} ne 'assessment');
340: foreach my $part (@{$resource->{'parts'}}) {
1.45 matthew 341: $problem_num++;
1.41 matthew 342: my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
343: $SKEW) = &Apache::loncoursedata::get_problem_statistics
344: (undef,$resource->{'symb'},$part,
345: $ENV{'request.course.id'});
1.43 matthew 346: #
347: $part = ' ' if ($part == 0);
348: #
1.41 matthew 349: my $wrongpercent = 0;
350: if (defined($num) && $num > 0) {
351: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
352: }
1.45 matthew 353: my $option = '';
1.41 matthew 354: $r->print('<tr>'.&statistics_html_table_data
355: ($resource,$part,$num,$tries,$mod,$mean,$Solved,
1.43 matthew 356: $solved,$wrongpercent,$DegOfDiff,$STD,$SKEW,
1.45 matthew 357: $option).
1.41 matthew 358: "</tr>\n");
359: }
360: }
361: $r->print("</table>\n");
362: $r->print("</td></tr></table>\n");
363: $r->rflush();
1.21 stredwic 364: }
1.41 matthew 365: #
366: return;
367: }
1.25 stredwic 368:
1.41 matthew 369: ###############################################
370: ###############################################
1.26 stredwic 371:
1.47 ! matthew 372: =pod
! 373:
! 374: =item &output_html_ungrouped()
! 375:
! 376: Presents the statistics data in a single html table which can be sorted by
! 377: different columns.
! 378:
! 379: =cut
! 380:
1.41 matthew 381: ###############################################
382: ###############################################
383: sub output_html_ungrouped {
1.45 matthew 384: my ($r,$option) = @_;
1.41 matthew 385: #
1.45 matthew 386: my $problem_num = 0;
1.41 matthew 387: my $show_container = 0;
1.43 matthew 388: my $show_part = 0;
1.41 matthew 389: #$r->print(&ProblemStatisticsLegend());
390: my @Header = ("Title","Part","#Stdnts","Tries","Mod",
1.26 stredwic 391: "Mean","#YES","#yes","%Wrng","DoDiff",
1.43 matthew 392: "S.D.","Skew");#,"D.F.1st","D.F.2nd");
1.42 matthew 393: #
394: my $sortby = undef;
395: foreach (@Header) {
396: if ($ENV{'form.sortby'} eq $_) {
397: $sortby = $_;
398: }
399: }
400: if (! defined($sortby) || $sortby eq '') {
401: $sortby = 'Container';
402: }
1.45 matthew 403: # If there is more than one sequence, list their titles
1.41 matthew 404: my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
405: if (@Sequences > 1) {
406: unshift(@Header,"Container");
407: $show_container = 1;
408: }
1.45 matthew 409: #
410: # If the option for showing the problem number is needed, push that
411: # on the list too
412: if (defined($option) && $option =~ /show probnum/) {
413: unshift(@Header,"P#");
414: }
1.41 matthew 415: #
416: $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
417: $r->rflush();
418: #
1.42 matthew 419: # Compile the data
420: my @Statsarray;
1.41 matthew 421: foreach my $sequence (@Sequences) {
422: next if ($sequence->{'num_assess'}<1);
423: foreach my $resource (@{$sequence->{'contents'}}) {
424: next if ($resource->{'type'} ne 'assessment');
425: foreach my $part (@{$resource->{'parts'}}) {
1.45 matthew 426: $problem_num++;
1.41 matthew 427: my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
428: $SKEW) = &Apache::loncoursedata::get_problem_statistics
429: (undef,$resource->{'symb'},$part,
430: $ENV{'request.course.id'});
1.43 matthew 431: #
432: $show_part = 1 if ($part ne '0');
433: $part = ' ' if ($part == 0);
434: #
1.41 matthew 435: my $wrongpercent = 0;
436: if (defined($num) && $num > 0) {
437: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
438: }
1.43 matthew 439: push (@Statsarray,
440: { 'sequence' => $sequence,
1.42 matthew 441: 'resource' => $resource,
442: 'Title' => $resource->{'title'},
443: 'Part' => $part,
444: '#Stdnts' => $num,
445: 'Tries' => $tries,
446: 'Mod' => $mod,
447: 'Mean' => $mean,
448: '#YES' => $Solved,
449: '#yes' => $solved,
450: '%Wrng' => $wrongpercent,
451: 'DoDiff' => $DegOfDiff,
452: 'S.D.' => $STD,
453: 'Skew' => $SKEW,
1.45 matthew 454: 'problem_num' => $problem_num,
1.43 matthew 455: });
1.42 matthew 456: }
457: }
458: }
459: #
1.43 matthew 460: # Table Headers
461: $r->print('<table border="0" cellpadding="3">'."\n");
462: my $Str = '';
463: foreach (@Header) {
464: next if ($_ eq 'Part' && !$show_part);
465: # Do not allow sorting on some fields
1.45 matthew 466: if ($_ eq $sortby || /^(Part|P\#)$/) {
1.43 matthew 467: $Str .= '<th>'.$_.'</th>';
468: } else {
469: $Str .= '<th>'.
470: '<a href="javascript:document.Statistics.sortby.value='."'$_'".
471: ';document.Statistics.submit();">'.
472: $_.'</a></th>';
473: }
474: }
475: $r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n");
476: #
1.42 matthew 477: # Sort the data
1.43 matthew 478: my @OutputOrder;
1.42 matthew 479: if ($sortby eq 'Container') {
1.43 matthew 480: @OutputOrder = @Statsarray;
1.42 matthew 481: } else {
482: # $sortby is already defined, so we can charge ahead
483: if ($sortby =~ /^(title|part)$/i) {
484: # Alpha comparison
485: @OutputOrder = sort {
1.43 matthew 486: lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
487: lc($a->{'Title'}) cmp lc($b->{'Title'}) ||
488: lc($a->{'Part'}) cmp lc($b->{'Part'});
1.42 matthew 489: } @Statsarray;
490: } else {
491: # Numerical comparison
492: @OutputOrder = sort {
493: my $retvalue = 0;
494: if ($b->{$sortby} eq 'nan') {
495: if ($a->{$sortby} ne 'nan') {
496: $retvalue = -1;
497: } else {
498: $retvalue = 0;
499: }
500: }
501: if ($a->{$sortby} eq 'nan') {
502: if ($b->{$sortby} ne 'nan') {
503: $retvalue = 1;
504: }
505: }
506: if ($retvalue eq '0') {
507: $retvalue = $b->{$sortby} <=> $a->{$sortby} ||
1.43 matthew 508: lc($a->{'Title'}) <=> lc($b->{'Title'}) ||
509: lc($a->{'Part'}) <=> lc($b->{'Part'});
1.41 matthew 510: }
1.42 matthew 511: $retvalue;
512: } @Statsarray;
513: }
1.43 matthew 514: }
1.45 matthew 515: $option .= ',no part' if (! $show_part);
1.43 matthew 516: foreach my $row (@OutputOrder) {
517: $r->print('<tr>');
1.45 matthew 518: if (defined($option) && $option =~ /show probnum/) {
519: $r->print('<td bgcolor="#FFFFE6">'.$row->{'problem_num'}.'</td>');
520: }
1.43 matthew 521: if ($show_container) {
522: $r->print('<td bgcolor="#FFFFE6">'
523: .$row->{'sequence'}->{'title'}.'</td>');
1.41 matthew 524: }
1.45 matthew 525: $r->print(&statistics_html_table_data
526: ($row->{'resource'},$row->{'Part'},$row->{'#Stdnts'},
527: $row->{'Tries'},$row->{'Mod'},$row->{'Mean'},
528: $row->{'#YES'},$row->{'#yes'},$row->{"\%Wrng"},
529: $row->{'DoDiff'},$row->{'S.D.'},$row->{'Skew'},
530: $option));
1.43 matthew 531: $r->print("</tr>\n");
1.26 stredwic 532: }
1.41 matthew 533: $r->print("</table>\n");
534: $r->print("</td></tr></table>\n");
1.26 stredwic 535: $r->rflush();
1.41 matthew 536: #
537: return;
1.42 matthew 538: }
539:
1.41 matthew 540: ###############################################
541: ###############################################
1.26 stredwic 542:
1.47 ! matthew 543: =pod
! 544:
! 545: =item &output_excel()
! 546:
! 547: Presents the statistical data in an Excel 95 compatable spreadsheet file.
! 548:
! 549: =cut
! 550:
1.41 matthew 551: ###############################################
552: ###############################################
1.44 matthew 553: sub output_excel {
554: my ($r) = @_;
555: my $filename = '/prtspool/'.
556: $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
557: time.'_'.rand(1000000000).'.xls';
558: #
559: my $excel_workbook = undef;
560: my $excel_sheet = undef;
561: #
562: my $rows_output = 0;
563: my $cols_output = 0;
564: #
565: # Create sheet
566: $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
567: #
568: # Check for errors
569: if (! defined($excel_workbook)) {
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");
574: return ;
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.
581: $excel_workbook->set_tempdir($Apache::lonnet::tmpdir);
582: #
583: # Add a worksheet
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);
589: #
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: $cols_output=0;
621: #
622: # Add the headers
623: my @Header = ("Container","Title","Part","#Stdnts","Tries","Mod",
624: "Mean","#YES","#yes","%Wrng","DoDiff",
625: "S.D.","Skew.");#,"D.F.1st","D.F.2nd");
626: foreach (@Header) {
627: $excel_sheet->write($rows_output,$cols_output++,$_);
628: }
629: $rows_output++;
630: #
631: # Write the data
632: foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
633: next if ($sequence->{'num_assess'}<1);
634: foreach my $resource (@{$sequence->{'contents'}}) {
635: next if ($resource->{'type'} ne 'assessment');
636: foreach my $part (@{$resource->{'parts'}}) {
637: $cols_output=0;
638: my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
639: $SKEW) = &Apache::loncoursedata::get_problem_statistics
640: (undef,$resource->{'symb'},$part,
641: $ENV{'request.course.id'});
642: #
643: if (!defined($part) || $part eq '') {
644: $part = ' ';
645: }
646: my $wrongpercent = 0;
647: if (defined($num) && $num > 0) {
648: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
649: }
650: foreach ($sequence->{'title'},$resource->{'title'},$part,
651: $num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent,
652: $DegOfDiff,$STD,$SKEW) {
653: $excel_sheet->write($rows_output,$cols_output++,$_);
654: }
655: $rows_output++;
656: }
657: }
658: }
659: #
660: # Write the excel file
661: $excel_workbook->close();
662: # Tell the user where to get their excel file
663: $r->print('<br />'.
664: '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
665: $r->rflush();
666: return;
667: }
668:
1.45 matthew 669: ###############################################
670: ###############################################
1.44 matthew 671:
1.47 ! matthew 672: =pod
! 673:
! 674: =item &statistics_html_table_data()
! 675:
! 676: Help function used to format the rows for HTML table output.
! 677:
! 678: =cut
! 679:
1.45 matthew 680: ###############################################
681: ###############################################
1.41 matthew 682: sub statistics_html_table_data {
683: my ($resource,$part,$num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent,
1.45 matthew 684: $DegOfDiff,$STD,$SKEW,$options) = @_;
1.41 matthew 685: my $row = '';
686: $row .= '<td bgcolor="#FFFFE6">'.
687: '<a href="'.$resource->{'src'}.'" target="_blank" >'.
688: $resource->{'title'}.'</a>'.
689: '</td>';
1.45 matthew 690: $row .= '<td bgcolor="#FFFFE6">'.$part.'</td>' if ($options !~ /no part/);
1.41 matthew 691: foreach ($num,$tries) {
692: $row .= '<td bgcolor="#EEFFCC" align="right">'.$_.'</td>';
693: }
1.47 ! matthew 694: foreach ($mod) {
! 695: $row .= '<td bgcolor="#DDFFFF" align="right">'.$_.'</td>';
! 696: }
! 697: foreach ($mean) {
1.41 matthew 698: $row .= '<td bgcolor="#DDFFFF" align="right">'.
699: sprintf("%5.2f",$_).'</td>';
700: }
701: foreach ($Solved,$solved) {
702: $row .= '<td bgcolor="#DDFFFF" align="right">'.$_.'</td>';
703: }
704: foreach ($wrongpercent) {
705: $row .= '<td bgcolor="#DDFFFF" align="right">'.
706: sprintf("%5.1f",$_).'</td>';
707: }
708: foreach ($DegOfDiff,$STD,$SKEW) {
709: $row .= '<td bgcolor="#FFDDDD" align="right">'.
710: sprintf("%5.2f",$_).'</td>';
1.26 stredwic 711: }
1.41 matthew 712: return $row;
713: }
1.26 stredwic 714:
1.41 matthew 715: ###############################################
716: ###############################################
1.47 ! matthew 717:
! 718: =pod
! 719:
! 720: =item &plot_statistics()
! 721:
! 722: =cut
! 723:
! 724: ###############################################
! 725: ###############################################
1.45 matthew 726: sub plot_statistics {
727: my ($r,$datafield) = @_;
728: my @Data;
729: #
730: my %Fields = ('#Stdnts'=> 0,
731: 'Tries' => 1,
732: 'Mod' => 2,
733: 'Mean' => 3,
734: '#YES' => 4,
735: '#yes' => 5,
736: '%Wrng' => 9,
737: 'DoDiff' => 6,
738: 'S.D.' => 7,
739: 'Skew' => 8,);
740: #
741: my $field = '%Wrng';
742: foreach (keys(%Fields)) {
743: $field = $_ if ($datafield eq $_);
1.34 minaeibi 744: }
1.45 matthew 745: my $fieldindex = $Fields{$field};
746: #
747: my $Max = 0;
748: foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
749: next if ($sequence->{'num_assess'}<1);
750: foreach my $resource (@{$sequence->{'contents'}}) {
751: next if ($resource->{'type'} ne 'assessment');
752: foreach my $part (@{$resource->{'parts'}}) {
753: my @Results = &Apache::loncoursedata::get_problem_statistics
754: (undef,$resource->{'symb'},$part,
755: $ENV{'request.course.id'});
756: my ($num,$Solved,$solved) = @Results[0,4,5];
757: my $wrongpercent = 0;
758: if (defined($num) && $num > 0) {
759: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
760: }
761: push (@Results,$wrongpercent);
762: my $data = $Results[$fieldindex];
1.46 matthew 763: $data = 0 if ($data eq 'nan');
1.45 matthew 764: $Max = $data if ($Max<$data);
765: push (@Data,$data);
766: }
767: }
1.26 stredwic 768: }
1.45 matthew 769: #
770: # Print out plot request
771: my $title = 'Percent Wrong';
772: if ($field eq 'DoDiff') {
773: $title = 'Degree of Difficulty';
774: }
775: my $yaxis = 'Percent';
776: if ($field eq 'DoDiff') {
777: $yaxis = '';
778: } elsif ($field ne '%Wrng') {
779: $yaxis = '';
780: }
781: #
782: # Determine appropriate value for $Max
783: if ($field eq 'DoDiff') {
784: if ($Max > 0.5) {
785: $Max = 1;
786: } elsif ($Max > 0.2) {
787: $Max = 0.5;
788: } elsif ($Max > 0.1) {
789: $Max = 0.2;
790: }
791: } elsif ($field eq '%Wrng') {
792: if ($Max > 50) {
793: $Max = 100;
794: } elsif ($Max > 25) {
795: $Max = 50;
796: } elsif ($Max > 20) {
797: $Max = 25;
798: } elsif ($Max > 10) {
799: $Max = 20;
800: } elsif ($Max > 5) {
801: $Max = 10;
1.24 stredwic 802: } else {
1.45 matthew 803: $Max = 5;
1.24 stredwic 804: }
805: }
1.45 matthew 806:
807: $r->print("<p>".&DrawGraph(\@Data,$title,'Problem Number',$yaxis,
808: $Max)."</p>\n");
809: #
810: # Print out the data
811: $ENV{'form.sortby'} = 'Contents';
812: &output_html_ungrouped($r,'show probnum');
1.24 stredwic 813: return;
814: }
1.1 stredwic 815:
1.45 matthew 816: ###############################################
817: ###############################################
818:
1.47 ! matthew 819: =pod
! 820:
! 821: =item &DrawGraph()
! 822:
! 823: =cut
! 824:
1.45 matthew 825: ###############################################
826: ###############################################
1.35 minaeibi 827: sub DrawGraph {
1.45 matthew 828: my ($values,$title,$xaxis,$yaxis,$Max)=@_;
829: $title = '' if (! defined($title));
830: $xaxis = '' if (! defined($xaxis));
831: $yaxis = '' if (! defined($yaxis));
832: #
1.35 minaeibi 833: my $sendValues = join(',', @$values);
834: my $sendCount = scalar(@$values);
835: if ( $Max > 1 ) {
836: if ($Max % 10) {
1.36 minaeibi 837: if ( int($Max) < $Max ) {
838: $Max++;
839: $Max = int($Max);
840: }
1.35 minaeibi 841: }
1.45 matthew 842: } else {
843: $Max = 1;
1.1 stredwic 844: }
1.45 matthew 845: my @GData = ($title,$xaxis,$yaxis,$Max,$sendCount,$sendValues);
846: return '<IMG src="/cgi-bin/graph.png?'.
847: (join('&', @GData)).'" border="1" />';
1.1 stredwic 848: }
1.12 minaeibi 849:
1.45 matthew 850: ###############################################
851: ###############################################
1.47 ! matthew 852:
! 853: =pod
! 854:
! 855: =item &ProblemStatisticsLegend()
! 856:
! 857: =cut
1.1 stredwic 858:
1.45 matthew 859: ###############################################
860: ###############################################
1.1 stredwic 861: sub ProblemStatisticsLegend {
862: my $Ptr = '';
863: $Ptr = '<table border="0">';
864: $Ptr .= '<tr><td>';
1.6 minaeibi 865: $Ptr .= '<b>#Stdnts</b></td>';
1.19 stredwic 866: $Ptr .= '<td>Total number of students attempted the problem.';
1.1 stredwic 867: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 868: $Ptr .= '<b>Tries</b></td>';
1.19 stredwic 869: $Ptr .= '<td>Total number of tries for solving the problem.';
1.1 stredwic 870: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 871: $Ptr .= '<b>Mod</b></td>';
1.19 stredwic 872: $Ptr .= '<td>Largest number of tries for solving the problem by a student.';
1.1 stredwic 873: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 874: $Ptr .= '<b>Mean</b></td>';
1.19 stredwic 875: $Ptr .= '<td>Average number of tries. [ Tries / #Stdnts ]';
1.1 stredwic 876: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 877: $Ptr .= '<b>#YES</b></td>';
1.1 stredwic 878: $Ptr .= '<td>Number of students solved the problem correctly.';
879: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 880: $Ptr .= '<b>#yes</b></td>';
1.1 stredwic 881: $Ptr .= '<td>Number of students solved the problem by override.';
882: $Ptr .= '</td></tr><tr><td>';
1.19 stredwic 883: $Ptr .= '<b>%Wrong</b></td>';
884: $Ptr .= '<td>Percentage of students who tried to solve the problem ';
885: $Ptr .= 'but is still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';
1.1 stredwic 886: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 887: $Ptr .= '<b>DoDiff</b></td>';
1.1 stredwic 888: $Ptr .= '<td>Degree of Difficulty of the problem. ';
889: $Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';
890: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 891: $Ptr .= '<b>S.D.</b></td>';
1.1 stredwic 892: $Ptr .= '<td>Standard Deviation of the tries. ';
893: $Ptr .= '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) ';
894: $Ptr .= 'where Xi denotes every student\'s tries ]';
895: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 896: $Ptr .= '<b>Skew.</b></td>';
1.1 stredwic 897: $Ptr .= '<td>Skewness of the students tries.';
898: $Ptr .= '[(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]';
899: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 900: $Ptr .= '<b>Dis.F.</b></td>';
1.1 stredwic 901: $Ptr .= '<td>Discrimination Factor: A Standard for evaluating the ';
902: $Ptr .= 'problem according to a Criterion<br>';
1.31 stredwic 903: $Ptr .= '<b>[Criterion to group students into %27 Upper Students - ';
904: $Ptr .= 'and %27 Lower Students]</b><br>';
1.1 stredwic 905: $Ptr .= '<b>1st Criterion</b> for Sorting the Students: ';
906: $Ptr .= '<b>Sum of Partial Credit Awarded / Total Number of Tries</b><br>';
907: $Ptr .= '<b>2nd Criterion</b> for Sorting the Students: ';
908: $Ptr .= '<b>Total number of Correct Answers / Total Number of Tries</b>';
909: $Ptr .= '</td></tr>';
910: $Ptr .= '<tr><td><b>Disc.</b></td>';
911: $Ptr .= '<td>Number of Students had at least one discussion.';
912: $Ptr .= '</td></tr></table>';
913: return $Ptr;
914: }
1.24 stredwic 915:
916: #---- END Problem Statistics Web Page ----------------------------------------
1.4 minaeibi 917:
1.1 stredwic 918: 1;
919: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>