File:
[LON-CAPA] /
loncom /
interface /
statistics /
lonproblemstatistics.pm
Revision
1.47:
download - view:
text,
annotated -
select for diffs
Thu Mar 27 19:26:33 2003 UTC (21 years, 5 months ago) by
matthew
Branches:
MAIN
CVS tags:
HEAD
Added beginnings of POD.
Added prompt for the user to hit 'update display' on the first run.
Removed problem part supression from html grouped by sequence display
because it just did not work right.
Added default output mode choice.
'Mod' is now displayed as an integer (which it is) instead of %5.2f.
1: # The LearningOnline Network with CAPA
2: #
3: # $Id: lonproblemstatistics.pm,v 1.47 2003/03/27 19:26:33 matthew Exp $
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: #
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: ###############################################
49:
50: package Apache::lonproblemstatistics;
51:
52: use strict;
53: use Apache::lonnet();
54: use Apache::lonhtmlcommon;
55: use Apache::loncoursedata;
56: use Apache::lonstatistics;
57: use Spreadsheet::WriteExcel;
58:
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
70:
71: ###############################################
72: ###############################################
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:
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:
117: =cut
118:
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";
168: }
169: $Str .= "</dl>\n";
170: return $Str;
171: }
172:
173: sub CreateAndParseOutputSelector {
174: my $Str = '';
175: my $elementname = 'statsoutputmode';
176: #
177: # Format for output options is 'mode, restrictions';
178: my $selected = 'HTML problem statistics grouped';
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};
184: }
185: }
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: }
206:
207: ###############################################
208: ###############################################
209:
210: =pod
211:
212: =item &Gather_Student_Data()
213:
214: Ensures all student data is up to date.
215:
216: =cut
217:
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');
240: }
241: &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
242: $r->rflush();
243: }
244:
245: ###############################################
246: ###############################################
247:
248: =pod
249:
250: =item &BuildProblemStatisticsPage()
251:
252: Main interface to problem statistics.
253:
254: =cut
255:
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'})) {
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
277: return;
278: }
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: }
294: } elsif ($output_mode eq 'excel') {
295: $r->print("<h2>Preparing Excel Spreadsheet</h2>");
296: &output_excel($r);
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: }
303: } else {
304: $r->print("<h1>Not implemented</h1>");
305: }
306: return;
307: }
308:
309: ###############################################
310: ###############################################
311:
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:
321: ###############################################
322: ###############################################
323: sub output_html_grouped_by_sequence {
324: my ($r) = @_;
325: my $problem_num = 0;
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'}}) {
341: $problem_num++;
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'});
346: #
347: $part = ' ' if ($part == 0);
348: #
349: my $wrongpercent = 0;
350: if (defined($num) && $num > 0) {
351: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
352: }
353: my $option = '';
354: $r->print('<tr>'.&statistics_html_table_data
355: ($resource,$part,$num,$tries,$mod,$mean,$Solved,
356: $solved,$wrongpercent,$DegOfDiff,$STD,$SKEW,
357: $option).
358: "</tr>\n");
359: }
360: }
361: $r->print("</table>\n");
362: $r->print("</td></tr></table>\n");
363: $r->rflush();
364: }
365: #
366: return;
367: }
368:
369: ###############################################
370: ###############################################
371:
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:
381: ###############################################
382: ###############################################
383: sub output_html_ungrouped {
384: my ($r,$option) = @_;
385: #
386: my $problem_num = 0;
387: my $show_container = 0;
388: my $show_part = 0;
389: #$r->print(&ProblemStatisticsLegend());
390: my @Header = ("Title","Part","#Stdnts","Tries","Mod",
391: "Mean","#YES","#yes","%Wrng","DoDiff",
392: "S.D.","Skew");#,"D.F.1st","D.F.2nd");
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: }
403: # If there is more than one sequence, list their titles
404: my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
405: if (@Sequences > 1) {
406: unshift(@Header,"Container");
407: $show_container = 1;
408: }
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: }
415: #
416: $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
417: $r->rflush();
418: #
419: # Compile the data
420: my @Statsarray;
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'}}) {
426: $problem_num++;
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'});
431: #
432: $show_part = 1 if ($part ne '0');
433: $part = ' ' if ($part == 0);
434: #
435: my $wrongpercent = 0;
436: if (defined($num) && $num > 0) {
437: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
438: }
439: push (@Statsarray,
440: { 'sequence' => $sequence,
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,
454: 'problem_num' => $problem_num,
455: });
456: }
457: }
458: }
459: #
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
466: if ($_ eq $sortby || /^(Part|P\#)$/) {
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: #
477: # Sort the data
478: my @OutputOrder;
479: if ($sortby eq 'Container') {
480: @OutputOrder = @Statsarray;
481: } else {
482: # $sortby is already defined, so we can charge ahead
483: if ($sortby =~ /^(title|part)$/i) {
484: # Alpha comparison
485: @OutputOrder = sort {
486: lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
487: lc($a->{'Title'}) cmp lc($b->{'Title'}) ||
488: lc($a->{'Part'}) cmp lc($b->{'Part'});
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} ||
508: lc($a->{'Title'}) <=> lc($b->{'Title'}) ||
509: lc($a->{'Part'}) <=> lc($b->{'Part'});
510: }
511: $retvalue;
512: } @Statsarray;
513: }
514: }
515: $option .= ',no part' if (! $show_part);
516: foreach my $row (@OutputOrder) {
517: $r->print('<tr>');
518: if (defined($option) && $option =~ /show probnum/) {
519: $r->print('<td bgcolor="#FFFFE6">'.$row->{'problem_num'}.'</td>');
520: }
521: if ($show_container) {
522: $r->print('<td bgcolor="#FFFFE6">'
523: .$row->{'sequence'}->{'title'}.'</td>');
524: }
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));
531: $r->print("</tr>\n");
532: }
533: $r->print("</table>\n");
534: $r->print("</td></tr></table>\n");
535: $r->rflush();
536: #
537: return;
538: }
539:
540: ###############################################
541: ###############################################
542:
543: =pod
544:
545: =item &output_excel()
546:
547: Presents the statistical data in an Excel 95 compatable spreadsheet file.
548:
549: =cut
550:
551: ###############################################
552: ###############################################
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:
669: ###############################################
670: ###############################################
671:
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:
680: ###############################################
681: ###############################################
682: sub statistics_html_table_data {
683: my ($resource,$part,$num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent,
684: $DegOfDiff,$STD,$SKEW,$options) = @_;
685: my $row = '';
686: $row .= '<td bgcolor="#FFFFE6">'.
687: '<a href="'.$resource->{'src'}.'" target="_blank" >'.
688: $resource->{'title'}.'</a>'.
689: '</td>';
690: $row .= '<td bgcolor="#FFFFE6">'.$part.'</td>' if ($options !~ /no part/);
691: foreach ($num,$tries) {
692: $row .= '<td bgcolor="#EEFFCC" align="right">'.$_.'</td>';
693: }
694: foreach ($mod) {
695: $row .= '<td bgcolor="#DDFFFF" align="right">'.$_.'</td>';
696: }
697: foreach ($mean) {
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>';
711: }
712: return $row;
713: }
714:
715: ###############################################
716: ###############################################
717:
718: =pod
719:
720: =item &plot_statistics()
721:
722: =cut
723:
724: ###############################################
725: ###############################################
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 $_);
744: }
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];
763: $data = 0 if ($data eq 'nan');
764: $Max = $data if ($Max<$data);
765: push (@Data,$data);
766: }
767: }
768: }
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;
802: } else {
803: $Max = 5;
804: }
805: }
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');
813: return;
814: }
815:
816: ###############################################
817: ###############################################
818:
819: =pod
820:
821: =item &DrawGraph()
822:
823: =cut
824:
825: ###############################################
826: ###############################################
827: sub DrawGraph {
828: my ($values,$title,$xaxis,$yaxis,$Max)=@_;
829: $title = '' if (! defined($title));
830: $xaxis = '' if (! defined($xaxis));
831: $yaxis = '' if (! defined($yaxis));
832: #
833: my $sendValues = join(',', @$values);
834: my $sendCount = scalar(@$values);
835: if ( $Max > 1 ) {
836: if ($Max % 10) {
837: if ( int($Max) < $Max ) {
838: $Max++;
839: $Max = int($Max);
840: }
841: }
842: } else {
843: $Max = 1;
844: }
845: my @GData = ($title,$xaxis,$yaxis,$Max,$sendCount,$sendValues);
846: return '<IMG src="/cgi-bin/graph.png?'.
847: (join('&', @GData)).'" border="1" />';
848: }
849:
850: ###############################################
851: ###############################################
852:
853: =pod
854:
855: =item &ProblemStatisticsLegend()
856:
857: =cut
858:
859: ###############################################
860: ###############################################
861: sub ProblemStatisticsLegend {
862: my $Ptr = '';
863: $Ptr = '<table border="0">';
864: $Ptr .= '<tr><td>';
865: $Ptr .= '<b>#Stdnts</b></td>';
866: $Ptr .= '<td>Total number of students attempted the problem.';
867: $Ptr .= '</td></tr><tr><td>';
868: $Ptr .= '<b>Tries</b></td>';
869: $Ptr .= '<td>Total number of tries for solving the problem.';
870: $Ptr .= '</td></tr><tr><td>';
871: $Ptr .= '<b>Mod</b></td>';
872: $Ptr .= '<td>Largest number of tries for solving the problem by a student.';
873: $Ptr .= '</td></tr><tr><td>';
874: $Ptr .= '<b>Mean</b></td>';
875: $Ptr .= '<td>Average number of tries. [ Tries / #Stdnts ]';
876: $Ptr .= '</td></tr><tr><td>';
877: $Ptr .= '<b>#YES</b></td>';
878: $Ptr .= '<td>Number of students solved the problem correctly.';
879: $Ptr .= '</td></tr><tr><td>';
880: $Ptr .= '<b>#yes</b></td>';
881: $Ptr .= '<td>Number of students solved the problem by override.';
882: $Ptr .= '</td></tr><tr><td>';
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) ]';
886: $Ptr .= '</td></tr><tr><td>';
887: $Ptr .= '<b>DoDiff</b></td>';
888: $Ptr .= '<td>Degree of Difficulty of the problem. ';
889: $Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';
890: $Ptr .= '</td></tr><tr><td>';
891: $Ptr .= '<b>S.D.</b></td>';
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>';
896: $Ptr .= '<b>Skew.</b></td>';
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>';
900: $Ptr .= '<b>Dis.F.</b></td>';
901: $Ptr .= '<td>Discrimination Factor: A Standard for evaluating the ';
902: $Ptr .= 'problem according to a Criterion<br>';
903: $Ptr .= '<b>[Criterion to group students into %27 Upper Students - ';
904: $Ptr .= 'and %27 Lower Students]</b><br>';
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: }
915:
916: #---- END Problem Statistics Web Page ----------------------------------------
917:
918: 1;
919: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>