Annotation of loncom/interface/statistics/lonproblemstatistics.pm, revision 1.48
1.1 stredwic 1: # The LearningOnline Network with CAPA
2: #
1.48 ! matthew 3: # $Id: lonproblemstatistics.pm,v 1.47 2003/03/27 19:26:33 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,
1.48 ! matthew 343: $SKEW) = &get_statistics($resource->{'symb'},$part);
1.43 matthew 344: #
345: $part = ' ' if ($part == 0);
346: #
1.41 matthew 347: my $wrongpercent = 0;
348: if (defined($num) && $num > 0) {
349: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
350: }
1.45 matthew 351: my $option = '';
1.41 matthew 352: $r->print('<tr>'.&statistics_html_table_data
353: ($resource,$part,$num,$tries,$mod,$mean,$Solved,
1.43 matthew 354: $solved,$wrongpercent,$DegOfDiff,$STD,$SKEW,
1.45 matthew 355: $option).
1.41 matthew 356: "</tr>\n");
357: }
358: }
359: $r->print("</table>\n");
360: $r->print("</td></tr></table>\n");
361: $r->rflush();
1.21 stredwic 362: }
1.41 matthew 363: #
364: return;
365: }
1.25 stredwic 366:
1.41 matthew 367: ###############################################
368: ###############################################
1.26 stredwic 369:
1.47 matthew 370: =pod
371:
372: =item &output_html_ungrouped()
373:
374: Presents the statistics data in a single html table which can be sorted by
375: different columns.
376:
377: =cut
378:
1.41 matthew 379: ###############################################
380: ###############################################
381: sub output_html_ungrouped {
1.45 matthew 382: my ($r,$option) = @_;
1.41 matthew 383: #
1.45 matthew 384: my $problem_num = 0;
1.41 matthew 385: my $show_container = 0;
1.43 matthew 386: my $show_part = 0;
1.41 matthew 387: #$r->print(&ProblemStatisticsLegend());
388: my @Header = ("Title","Part","#Stdnts","Tries","Mod",
1.26 stredwic 389: "Mean","#YES","#yes","%Wrng","DoDiff",
1.43 matthew 390: "S.D.","Skew");#,"D.F.1st","D.F.2nd");
1.42 matthew 391: #
392: my $sortby = undef;
393: foreach (@Header) {
394: if ($ENV{'form.sortby'} eq $_) {
395: $sortby = $_;
396: }
397: }
398: if (! defined($sortby) || $sortby eq '') {
399: $sortby = 'Container';
400: }
1.45 matthew 401: # If there is more than one sequence, list their titles
1.41 matthew 402: my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
403: if (@Sequences > 1) {
404: unshift(@Header,"Container");
405: $show_container = 1;
406: }
1.45 matthew 407: #
408: # If the option for showing the problem number is needed, push that
409: # on the list too
410: if (defined($option) && $option =~ /show probnum/) {
411: unshift(@Header,"P#");
412: }
1.41 matthew 413: #
414: $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
415: $r->rflush();
416: #
1.42 matthew 417: # Compile the data
418: my @Statsarray;
1.41 matthew 419: foreach my $sequence (@Sequences) {
420: next if ($sequence->{'num_assess'}<1);
421: foreach my $resource (@{$sequence->{'contents'}}) {
422: next if ($resource->{'type'} ne 'assessment');
423: foreach my $part (@{$resource->{'parts'}}) {
1.45 matthew 424: $problem_num++;
1.41 matthew 425: my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
1.48 ! matthew 426: $SKEW) = &get_statistics($resource->{'symb'},$part);
1.43 matthew 427: #
428: $show_part = 1 if ($part ne '0');
429: $part = ' ' if ($part == 0);
430: #
1.41 matthew 431: my $wrongpercent = 0;
432: if (defined($num) && $num > 0) {
433: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
434: }
1.43 matthew 435: push (@Statsarray,
436: { 'sequence' => $sequence,
1.42 matthew 437: 'resource' => $resource,
438: 'Title' => $resource->{'title'},
439: 'Part' => $part,
440: '#Stdnts' => $num,
441: 'Tries' => $tries,
442: 'Mod' => $mod,
443: 'Mean' => $mean,
444: '#YES' => $Solved,
445: '#yes' => $solved,
446: '%Wrng' => $wrongpercent,
447: 'DoDiff' => $DegOfDiff,
448: 'S.D.' => $STD,
449: 'Skew' => $SKEW,
1.45 matthew 450: 'problem_num' => $problem_num,
1.43 matthew 451: });
1.42 matthew 452: }
453: }
454: }
455: #
1.43 matthew 456: # Table Headers
457: $r->print('<table border="0" cellpadding="3">'."\n");
458: my $Str = '';
459: foreach (@Header) {
460: next if ($_ eq 'Part' && !$show_part);
461: # Do not allow sorting on some fields
1.45 matthew 462: if ($_ eq $sortby || /^(Part|P\#)$/) {
1.43 matthew 463: $Str .= '<th>'.$_.'</th>';
464: } else {
465: $Str .= '<th>'.
466: '<a href="javascript:document.Statistics.sortby.value='."'$_'".
467: ';document.Statistics.submit();">'.
468: $_.'</a></th>';
469: }
470: }
471: $r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n");
472: #
1.42 matthew 473: # Sort the data
1.43 matthew 474: my @OutputOrder;
1.42 matthew 475: if ($sortby eq 'Container') {
1.43 matthew 476: @OutputOrder = @Statsarray;
1.42 matthew 477: } else {
478: # $sortby is already defined, so we can charge ahead
479: if ($sortby =~ /^(title|part)$/i) {
480: # Alpha comparison
481: @OutputOrder = sort {
1.43 matthew 482: lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
483: lc($a->{'Title'}) cmp lc($b->{'Title'}) ||
484: lc($a->{'Part'}) cmp lc($b->{'Part'});
1.42 matthew 485: } @Statsarray;
486: } else {
487: # Numerical comparison
488: @OutputOrder = sort {
489: my $retvalue = 0;
490: if ($b->{$sortby} eq 'nan') {
491: if ($a->{$sortby} ne 'nan') {
492: $retvalue = -1;
493: } else {
494: $retvalue = 0;
495: }
496: }
497: if ($a->{$sortby} eq 'nan') {
498: if ($b->{$sortby} ne 'nan') {
499: $retvalue = 1;
500: }
501: }
502: if ($retvalue eq '0') {
503: $retvalue = $b->{$sortby} <=> $a->{$sortby} ||
1.43 matthew 504: lc($a->{'Title'}) <=> lc($b->{'Title'}) ||
505: lc($a->{'Part'}) <=> lc($b->{'Part'});
1.41 matthew 506: }
1.42 matthew 507: $retvalue;
508: } @Statsarray;
509: }
1.43 matthew 510: }
1.45 matthew 511: $option .= ',no part' if (! $show_part);
1.43 matthew 512: foreach my $row (@OutputOrder) {
513: $r->print('<tr>');
1.45 matthew 514: if (defined($option) && $option =~ /show probnum/) {
515: $r->print('<td bgcolor="#FFFFE6">'.$row->{'problem_num'}.'</td>');
516: }
1.43 matthew 517: if ($show_container) {
518: $r->print('<td bgcolor="#FFFFE6">'
519: .$row->{'sequence'}->{'title'}.'</td>');
1.41 matthew 520: }
1.45 matthew 521: $r->print(&statistics_html_table_data
522: ($row->{'resource'},$row->{'Part'},$row->{'#Stdnts'},
523: $row->{'Tries'},$row->{'Mod'},$row->{'Mean'},
524: $row->{'#YES'},$row->{'#yes'},$row->{"\%Wrng"},
525: $row->{'DoDiff'},$row->{'S.D.'},$row->{'Skew'},
526: $option));
1.43 matthew 527: $r->print("</tr>\n");
1.26 stredwic 528: }
1.41 matthew 529: $r->print("</table>\n");
530: $r->print("</td></tr></table>\n");
1.26 stredwic 531: $r->rflush();
1.41 matthew 532: #
533: return;
1.42 matthew 534: }
535:
1.41 matthew 536: ###############################################
537: ###############################################
1.26 stredwic 538:
1.47 matthew 539: =pod
540:
541: =item &output_excel()
542:
543: Presents the statistical data in an Excel 95 compatable spreadsheet file.
544:
545: =cut
546:
1.41 matthew 547: ###############################################
548: ###############################################
1.44 matthew 549: sub output_excel {
550: my ($r) = @_;
551: my $filename = '/prtspool/'.
552: $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
553: time.'_'.rand(1000000000).'.xls';
554: #
555: my $excel_workbook = undef;
556: my $excel_sheet = undef;
557: #
558: my $rows_output = 0;
559: my $cols_output = 0;
560: #
561: # Create sheet
562: $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
563: #
564: # Check for errors
565: if (! defined($excel_workbook)) {
566: $r->log_error("Error creating excel spreadsheet $filename: $!");
567: $r->print("Problems creating new Excel file. ".
568: "This error has been logged. ".
569: "Please alert your LON-CAPA administrator");
570: return ;
571: }
572: #
573: # The excel spreadsheet stores temporary data in files, then put them
574: # together. If needed we should be able to disable this (memory only).
575: # The temporary directory must be specified before calling 'addworksheet'.
576: # File::Temp is used to determine the temporary directory.
577: $excel_workbook->set_tempdir($Apache::lonnet::tmpdir);
578: #
579: # Add a worksheet
580: my $sheetname = $ENV{'course.'.$ENV{'request.course.id'}.'.description'};
581: if (length($sheetname) > 31) {
582: $sheetname = substr($sheetname,0,31);
583: }
584: $excel_sheet = $excel_workbook->addworksheet($sheetname);
585: #
586: # Put the course description in the header
587: $excel_sheet->write($rows_output,$cols_output++,
588: $ENV{'course.'.$ENV{'request.course.id'}.'.description'});
589: $cols_output += 3;
590: #
591: # Put a description of the sections listed
592: my $sectionstring = '';
593: my @Sections = @Apache::lonstatistics::SelectedSections;
594: if (scalar(@Sections) > 1) {
595: if (scalar(@Sections) > 2) {
596: my $last = pop(@Sections);
597: $sectionstring = "Sections ".join(', ',@Sections).', and '.$last;
598: } else {
599: $sectionstring = "Sections ".join(' and ',@Sections);
600: }
601: } else {
602: if ($Sections[0] eq 'all') {
603: $sectionstring = "All sections";
604: } else {
605: $sectionstring = "Section ".$Sections[0];
606: }
607: }
608: $excel_sheet->write($rows_output,$cols_output++,$sectionstring);
609: $cols_output += scalar(@Sections);
610: #
611: # Put the date in there too
612: $excel_sheet->write($rows_output,$cols_output++,
613: 'Compiled on '.localtime(time));
614: #
615: $rows_output++;
616: $cols_output=0;
617: #
618: # Add the headers
619: my @Header = ("Container","Title","Part","#Stdnts","Tries","Mod",
620: "Mean","#YES","#yes","%Wrng","DoDiff",
621: "S.D.","Skew.");#,"D.F.1st","D.F.2nd");
622: foreach (@Header) {
623: $excel_sheet->write($rows_output,$cols_output++,$_);
624: }
625: $rows_output++;
626: #
627: # Write the data
628: foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
629: next if ($sequence->{'num_assess'}<1);
630: foreach my $resource (@{$sequence->{'contents'}}) {
631: next if ($resource->{'type'} ne 'assessment');
632: foreach my $part (@{$resource->{'parts'}}) {
633: $cols_output=0;
634: my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
1.48 ! matthew 635: $SKEW) = &get_statistics($resource->{'symb'},$part);
1.44 matthew 636: #
637: if (!defined($part) || $part eq '') {
638: $part = ' ';
639: }
640: my $wrongpercent = 0;
641: if (defined($num) && $num > 0) {
642: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
643: }
644: foreach ($sequence->{'title'},$resource->{'title'},$part,
645: $num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent,
646: $DegOfDiff,$STD,$SKEW) {
647: $excel_sheet->write($rows_output,$cols_output++,$_);
648: }
649: $rows_output++;
650: }
651: }
652: }
653: #
654: # Write the excel file
655: $excel_workbook->close();
656: # Tell the user where to get their excel file
657: $r->print('<br />'.
658: '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
659: $r->rflush();
660: return;
661: }
662:
1.45 matthew 663: ###############################################
664: ###############################################
1.44 matthew 665:
1.47 matthew 666: =pod
667:
668: =item &statistics_html_table_data()
669:
670: Help function used to format the rows for HTML table output.
671:
672: =cut
673:
1.45 matthew 674: ###############################################
675: ###############################################
1.41 matthew 676: sub statistics_html_table_data {
677: my ($resource,$part,$num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent,
1.45 matthew 678: $DegOfDiff,$STD,$SKEW,$options) = @_;
1.41 matthew 679: my $row = '';
680: $row .= '<td bgcolor="#FFFFE6">'.
681: '<a href="'.$resource->{'src'}.'" target="_blank" >'.
682: $resource->{'title'}.'</a>'.
683: '</td>';
1.45 matthew 684: $row .= '<td bgcolor="#FFFFE6">'.$part.'</td>' if ($options !~ /no part/);
1.41 matthew 685: foreach ($num,$tries) {
686: $row .= '<td bgcolor="#EEFFCC" align="right">'.$_.'</td>';
687: }
1.47 matthew 688: foreach ($mod) {
689: $row .= '<td bgcolor="#DDFFFF" align="right">'.$_.'</td>';
690: }
691: foreach ($mean) {
1.41 matthew 692: $row .= '<td bgcolor="#DDFFFF" align="right">'.
693: sprintf("%5.2f",$_).'</td>';
694: }
695: foreach ($Solved,$solved) {
696: $row .= '<td bgcolor="#DDFFFF" align="right">'.$_.'</td>';
697: }
698: foreach ($wrongpercent) {
699: $row .= '<td bgcolor="#DDFFFF" align="right">'.
700: sprintf("%5.1f",$_).'</td>';
701: }
702: foreach ($DegOfDiff,$STD,$SKEW) {
703: $row .= '<td bgcolor="#FFDDDD" align="right">'.
704: sprintf("%5.2f",$_).'</td>';
1.26 stredwic 705: }
1.41 matthew 706: return $row;
707: }
1.26 stredwic 708:
1.41 matthew 709: ###############################################
710: ###############################################
1.47 matthew 711:
712: =pod
713:
714: =item &plot_statistics()
715:
716: =cut
717:
718: ###############################################
719: ###############################################
1.45 matthew 720: sub plot_statistics {
721: my ($r,$datafield) = @_;
722: my @Data;
723: #
724: my %Fields = ('#Stdnts'=> 0,
725: 'Tries' => 1,
726: 'Mod' => 2,
727: 'Mean' => 3,
728: '#YES' => 4,
729: '#yes' => 5,
730: '%Wrng' => 9,
731: 'DoDiff' => 6,
732: 'S.D.' => 7,
733: 'Skew' => 8,);
734: #
735: my $field = '%Wrng';
736: foreach (keys(%Fields)) {
737: $field = $_ if ($datafield eq $_);
1.34 minaeibi 738: }
1.45 matthew 739: my $fieldindex = $Fields{$field};
740: #
741: my $Max = 0;
742: foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
743: next if ($sequence->{'num_assess'}<1);
744: foreach my $resource (@{$sequence->{'contents'}}) {
745: next if ($resource->{'type'} ne 'assessment');
746: foreach my $part (@{$resource->{'parts'}}) {
1.48 ! matthew 747: my @Results= &get_statistics($resource->{'symb'},$part);
1.45 matthew 748: my ($num,$Solved,$solved) = @Results[0,4,5];
749: my $wrongpercent = 0;
750: if (defined($num) && $num > 0) {
751: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
752: }
753: push (@Results,$wrongpercent);
754: my $data = $Results[$fieldindex];
1.46 matthew 755: $data = 0 if ($data eq 'nan');
1.45 matthew 756: $Max = $data if ($Max<$data);
757: push (@Data,$data);
758: }
759: }
1.26 stredwic 760: }
1.45 matthew 761: #
762: # Print out plot request
763: my $title = 'Percent Wrong';
764: if ($field eq 'DoDiff') {
765: $title = 'Degree of Difficulty';
766: }
767: my $yaxis = 'Percent';
768: if ($field eq 'DoDiff') {
769: $yaxis = '';
770: } elsif ($field ne '%Wrng') {
771: $yaxis = '';
772: }
773: #
774: # Determine appropriate value for $Max
775: if ($field eq 'DoDiff') {
776: if ($Max > 0.5) {
777: $Max = 1;
778: } elsif ($Max > 0.2) {
779: $Max = 0.5;
780: } elsif ($Max > 0.1) {
781: $Max = 0.2;
782: }
783: } elsif ($field eq '%Wrng') {
784: if ($Max > 50) {
785: $Max = 100;
786: } elsif ($Max > 25) {
787: $Max = 50;
788: } elsif ($Max > 20) {
789: $Max = 25;
790: } elsif ($Max > 10) {
791: $Max = 20;
792: } elsif ($Max > 5) {
793: $Max = 10;
1.24 stredwic 794: } else {
1.45 matthew 795: $Max = 5;
1.24 stredwic 796: }
797: }
1.45 matthew 798:
799: $r->print("<p>".&DrawGraph(\@Data,$title,'Problem Number',$yaxis,
800: $Max)."</p>\n");
801: #
802: # Print out the data
803: $ENV{'form.sortby'} = 'Contents';
804: &output_html_ungrouped($r,'show probnum');
1.24 stredwic 805: return;
806: }
1.1 stredwic 807:
1.45 matthew 808: ###############################################
809: ###############################################
810:
1.47 matthew 811: =pod
812:
813: =item &DrawGraph()
814:
815: =cut
816:
1.45 matthew 817: ###############################################
818: ###############################################
1.35 minaeibi 819: sub DrawGraph {
1.45 matthew 820: my ($values,$title,$xaxis,$yaxis,$Max)=@_;
821: $title = '' if (! defined($title));
822: $xaxis = '' if (! defined($xaxis));
823: $yaxis = '' if (! defined($yaxis));
824: #
1.35 minaeibi 825: my $sendValues = join(',', @$values);
826: my $sendCount = scalar(@$values);
827: if ( $Max > 1 ) {
828: if ($Max % 10) {
1.36 minaeibi 829: if ( int($Max) < $Max ) {
830: $Max++;
831: $Max = int($Max);
832: }
1.35 minaeibi 833: }
1.45 matthew 834: } else {
835: $Max = 1;
1.1 stredwic 836: }
1.45 matthew 837: my @GData = ($title,$xaxis,$yaxis,$Max,$sendCount,$sendValues);
838: return '<IMG src="/cgi-bin/graph.png?'.
839: (join('&', @GData)).'" border="1" />';
1.48 ! matthew 840: }
! 841:
! 842: sub get_statistics {
! 843: my ($symb,$part) = @_;
! 844: #
! 845: my $courseid = $ENV{'request.course.id'};
! 846: #
! 847: my $students = \@Apache::lonstatistics::Students;
! 848: if ($Apache::lonstatistics::SelectedSections[0] eq 'all') {
! 849: $students = undef;
! 850: }
! 851: my @Results = &Apache::loncoursedata::get_problem_statistics
! 852: ($students,$symb,$part,$courseid);
! 853: return @Results;
1.1 stredwic 854: }
1.12 minaeibi 855:
1.45 matthew 856: ###############################################
857: ###############################################
1.47 matthew 858:
859: =pod
860:
861: =item &ProblemStatisticsLegend()
862:
863: =cut
1.1 stredwic 864:
1.45 matthew 865: ###############################################
866: ###############################################
1.1 stredwic 867: sub ProblemStatisticsLegend {
868: my $Ptr = '';
869: $Ptr = '<table border="0">';
870: $Ptr .= '<tr><td>';
1.6 minaeibi 871: $Ptr .= '<b>#Stdnts</b></td>';
1.19 stredwic 872: $Ptr .= '<td>Total number of students attempted the problem.';
1.1 stredwic 873: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 874: $Ptr .= '<b>Tries</b></td>';
1.19 stredwic 875: $Ptr .= '<td>Total number of tries for solving the problem.';
1.1 stredwic 876: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 877: $Ptr .= '<b>Mod</b></td>';
1.19 stredwic 878: $Ptr .= '<td>Largest number of tries for solving the problem by a student.';
1.1 stredwic 879: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 880: $Ptr .= '<b>Mean</b></td>';
1.19 stredwic 881: $Ptr .= '<td>Average number of tries. [ Tries / #Stdnts ]';
1.1 stredwic 882: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 883: $Ptr .= '<b>#YES</b></td>';
1.1 stredwic 884: $Ptr .= '<td>Number of students solved the problem correctly.';
885: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 886: $Ptr .= '<b>#yes</b></td>';
1.1 stredwic 887: $Ptr .= '<td>Number of students solved the problem by override.';
888: $Ptr .= '</td></tr><tr><td>';
1.19 stredwic 889: $Ptr .= '<b>%Wrong</b></td>';
890: $Ptr .= '<td>Percentage of students who tried to solve the problem ';
891: $Ptr .= 'but is still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';
1.1 stredwic 892: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 893: $Ptr .= '<b>DoDiff</b></td>';
1.1 stredwic 894: $Ptr .= '<td>Degree of Difficulty of the problem. ';
895: $Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';
896: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 897: $Ptr .= '<b>S.D.</b></td>';
1.1 stredwic 898: $Ptr .= '<td>Standard Deviation of the tries. ';
899: $Ptr .= '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) ';
900: $Ptr .= 'where Xi denotes every student\'s tries ]';
901: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 902: $Ptr .= '<b>Skew.</b></td>';
1.1 stredwic 903: $Ptr .= '<td>Skewness of the students tries.';
904: $Ptr .= '[(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]';
905: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 906: $Ptr .= '<b>Dis.F.</b></td>';
1.1 stredwic 907: $Ptr .= '<td>Discrimination Factor: A Standard for evaluating the ';
908: $Ptr .= 'problem according to a Criterion<br>';
1.31 stredwic 909: $Ptr .= '<b>[Criterion to group students into %27 Upper Students - ';
910: $Ptr .= 'and %27 Lower Students]</b><br>';
1.1 stredwic 911: $Ptr .= '<b>1st Criterion</b> for Sorting the Students: ';
912: $Ptr .= '<b>Sum of Partial Credit Awarded / Total Number of Tries</b><br>';
913: $Ptr .= '<b>2nd Criterion</b> for Sorting the Students: ';
914: $Ptr .= '<b>Total number of Correct Answers / Total Number of Tries</b>';
915: $Ptr .= '</td></tr>';
916: $Ptr .= '<tr><td><b>Disc.</b></td>';
917: $Ptr .= '<td>Number of Students had at least one discussion.';
918: $Ptr .= '</td></tr></table>';
919: return $Ptr;
920: }
1.24 stredwic 921:
922: #---- END Problem Statistics Web Page ----------------------------------------
1.4 minaeibi 923:
1.1 stredwic 924: 1;
925: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>