File:
[LON-CAPA] /
loncom /
interface /
statistics /
lonproblemstatistics.pm
Revision
1.45:
download - view:
text,
annotated -
select for diffs
Wed Mar 26 21:47:47 2003 UTC (21 years, 5 months ago) by
matthew
Branches:
MAIN
CVS tags:
HEAD
Added graph output.
Added problem number accounting to &output_html* subroutines.
Added parameter $option to &output_html_ungrouped to deal with the display
of problem numbers.
Removed &stats_row_from_hash because it was dumb.
Modified &statistics_html_table_data to take an $options string instead
of a flag.
Removed &BuildGraphicChart, &CreateProblemStatisticsTableHeading
&BuildStatisticsTable, &TableRow, &setbgcolor, and
&ProblemStatisticsButtons.
Added &plot_statistics
Rewrote &DrawGraph entirely. Requires version 1.21 of graph.png.
1: # The LearningOnline Network with CAPA
2: #
3: # $Id: lonproblemstatistics.pm,v 1.45 2003/03/26 21:47:47 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: package Apache::lonproblemstatistics;
32:
33: use strict;
34: use Apache::lonnet();
35: use Apache::lonhtmlcommon;
36: use Apache::loncoursedata;
37: use Apache::lonstatistics;
38: use Spreadsheet::WriteExcel;
39:
40: #######################################################
41: #######################################################
42:
43: sub CreateInterface {
44: my $Str = '';
45: $Str .= '<table cellspacing="5">'."\n";
46: $Str .= '<tr>';
47: $Str .= '<td align="center"><b>Sections</b></td>';
48: $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
49: $Str .= '<td align="center"><b>Output</b></td>';
50: $Str .= '</tr>'."\n";
51: #
52: $Str .= '<tr><td align="center">'."\n";
53: $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
54: $Str .= '</td><td align="center">';
55: #
56: my $only_seq_with_assessments = sub {
57: my $s=shift;
58: if ($s->{'num_assess'} < 1) {
59: return 0;
60: } else {
61: return 1;
62: }
63: };
64: $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
65: $only_seq_with_assessments);
66: $Str .= '</td><td>'."\n";
67: $Str .= &CreateAndParseOutputSelector();
68: $Str .= '</td></tr>'."\n";
69: $Str .= '</table>'."\n";
70: return $Str;
71: }
72:
73: #######################################################
74: #######################################################
75:
76: =pod
77:
78: =item &CreateAndParseOutputSelector()
79:
80: =cut
81:
82: #######################################################
83: #######################################################
84: my $output_mode;
85: my $show;
86:
87: my @OutputOptions =
88: (
89: { name => 'problem statistics grouped by sequence',
90: value => 'HTML problem statistics grouped',
91: description => 'Output statistics for the problem parts.',
92: mode => 'html',
93: show => 'grouped',
94: },
95: { name => 'problem statistics ungrouped',
96: value => 'HTML problem statistics ungrouped',
97: description => 'Output statistics for the problem parts.',
98: mode => 'html',
99: show => 'ungrouped',
100: },
101: { name => 'problem statistics, Excel',
102: value => 'Excel problem statistics',
103: description => 'Output statistics for the problem parts '.
104: 'in an Excel workbook',
105: mode => 'excel',
106: show => 'all',
107: },
108: { name => 'Degree of Difficulty Plot',
109: value => 'plot deg diff',
110: description => 'Generate a plot of the degree of difficulty of each '.
111: 'problem part.',
112: mode => 'plot',
113: show => 'deg of diff',
114: },
115: { name => 'Percent Wrong Plot',
116: value => 'plot per wrong',
117: description => 'Generate a plot showing the percent of students who '.
118: 'were unable to complete each problem part',
119: mode => 'plot',
120: show => 'per wrong',
121: },
122: );
123:
124: sub OutputDescriptions {
125: my $Str = '';
126: $Str .= "<h2>Output Modes</h2>\n";
127: $Str .= "<dl>\n";
128: foreach my $outputmode (@OutputOptions) {
129: $Str .=" <dt>".$outputmode->{'name'}."</dt>\n";
130: $Str .=" <dd>".$outputmode->{'description'}."</dd>\n";
131: }
132: $Str .= "</dl>\n";
133: return $Str;
134: }
135:
136: sub CreateAndParseOutputSelector {
137: my $Str = '';
138: my $elementname = 'outputmode';
139: #
140: # Format for output options is 'mode, restrictions';
141: my $selected = 'html, with links';
142: if (exists($ENV{'form.'.$elementname})) {
143: if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
144: $selected = $ENV{'form.'.$elementname}->[0];
145: } else {
146: $selected = $ENV{'form.'.$elementname};
147: }
148: }
149: #
150: # Set package variables describing output mode
151: $output_mode = 'html';
152: $show = 'all';
153: foreach my $option (@OutputOptions) {
154: next if ($option->{'value'} ne $selected);
155: $output_mode = $option->{'mode'};
156: $show = $option->{'show'};
157: }
158: #
159: # Build the form element
160: $Str = qq/<select size="5" name="$elementname">/;
161: foreach my $option (@OutputOptions) {
162: $Str .= "\n".' <option value="'.$option->{'value'}.'"';
163: $Str .= " selected " if ($option->{'value'} eq $selected);
164: $Str .= ">".$option->{'name'}."<\/option>";
165: }
166: $Str .= "\n</select>";
167: return $Str;
168: }
169:
170: ###############################################
171: ###############################################
172:
173: ###############################################
174: ###############################################
175: sub Gather_Student_Data {
176: my ($r) = @_;
177: my $c = $r->connection();
178: #
179: my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
180: #
181: my @Students = @Apache::lonstatistics::Students;
182: #
183: # Open the progress window
184: my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
185: ($r,'Statistics Compilation Status',
186: 'Statistics Compilation Progress', scalar(@Students));
187: #
188: while (my $student = shift @Students) {
189: return if ($c->aborted());
190: my ($status,undef) = &Apache::loncoursedata::ensure_current_data
191: ($student->{'username'},$student->{'domain'},
192: $ENV{'request.course.id'});
193: &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
194: 'last student');
195: }
196: &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
197: $r->rflush();
198: }
199:
200: ###############################################
201: ###############################################
202:
203: ###############################################
204: ###############################################
205: sub BuildProblemStatisticsPage {
206: my ($r,$c)=@_;
207: #
208: $output_mode = 'html';
209: $show = 'grouped';
210: #
211: $r->print(&CreateInterface());
212: $r->print('<input type="hidden" name="statsfirstcall" value="no" />');
213: $r->print('<input type="hidden" name="sortby" value="'.$ENV{'form.sortby'}.
214: '" />');
215: if (! exists($ENV{'form.statsfirstcall'})) {
216: return;
217: }
218: #
219: &Gather_Student_Data($r);
220: #
221: #
222: if ($output_mode eq 'html') {
223: $r->print("<h2>".
224: $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.
225: "</h2>\n");
226: $r->print("<h3>".localtime(time)."</h3>");
227: $r->rflush();
228: if ($show eq 'grouped') {
229: &output_html_grouped_by_sequence($r);
230: } elsif ($show eq 'ungrouped') {
231: &output_html_ungrouped($r);
232: }
233: } elsif ($output_mode eq 'excel') {
234: $r->print("<h2>Preparing Excel Spreadsheet</h2>");
235: &output_excel($r);
236: } elsif ($output_mode eq 'plot') {
237: if ($show eq 'deg of diff') {
238: &plot_statistics($r,'DoDiff');
239: } elsif ($show eq 'per wrong') {
240: &plot_statistics($r,'%Wrng');
241: }
242: } else {
243: $r->print("<h1>Not implemented</h1>");
244: }
245: return;
246: }
247:
248: ###############################################
249: ###############################################
250:
251: ###############################################
252: ###############################################
253: sub output_html_grouped_by_sequence {
254: my ($r) = @_;
255: my $problem_num = 0;
256: #$r->print(&ProblemStatisticsLegend());
257: my @Header = ("Title","Part","#Stdnts","Tries","Mod",
258: "Mean","#YES","#yes","%Wrng","DoDiff",
259: "S.D.","Skew.");#,"D.F.1st","D.F.2nd");
260: # #FFFFE6 #EEFFCC #DDFFFF FFDDDD #DDFFDD #FFDDFF
261: foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
262: my $show_part = 0;
263: next if ($sequence->{'num_assess'}<1);
264: $r->print("<h3>".$sequence->{'title'}."</h3>");
265: $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
266: $r->print('<table border="0" cellpadding="3">'."\n");
267: $r->print('<tr bgcolor="#FFFFE6"><th>'.
268: join("</th><th>",@Header)."</th></tr>\n");
269: foreach my $resource (@{$sequence->{'contents'}}) {
270: next if ($resource->{'type'} ne 'assessment');
271: foreach my $part (@{$resource->{'parts'}}) {
272: $problem_num++;
273: my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
274: $SKEW) = &Apache::loncoursedata::get_problem_statistics
275: (undef,$resource->{'symb'},$part,
276: $ENV{'request.course.id'});
277: #
278: $show_part = 1 if ($part ne '0');
279: $part = ' ' if ($part == 0);
280: #
281: my $wrongpercent = 0;
282: if (defined($num) && $num > 0) {
283: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
284: }
285: my $option = '';
286: $option .= 'no part' if (! $show_part);
287: $r->print('<tr>'.&statistics_html_table_data
288: ($resource,$part,$num,$tries,$mod,$mean,$Solved,
289: $solved,$wrongpercent,$DegOfDiff,$STD,$SKEW,
290: $option).
291: "</tr>\n");
292: }
293: }
294: $r->print("</table>\n");
295: $r->print("</td></tr></table>\n");
296: $r->rflush();
297: }
298: #
299: return;
300: }
301:
302: ###############################################
303: ###############################################
304:
305: ###############################################
306: ###############################################
307: sub output_html_ungrouped {
308: my ($r,$option) = @_;
309: #
310: my $problem_num = 0;
311: my $show_container = 0;
312: my $show_part = 0;
313: #$r->print(&ProblemStatisticsLegend());
314: my @Header = ("Title","Part","#Stdnts","Tries","Mod",
315: "Mean","#YES","#yes","%Wrng","DoDiff",
316: "S.D.","Skew");#,"D.F.1st","D.F.2nd");
317: #
318: my $sortby = undef;
319: foreach (@Header) {
320: if ($ENV{'form.sortby'} eq $_) {
321: $sortby = $_;
322: }
323: }
324: if (! defined($sortby) || $sortby eq '') {
325: $sortby = 'Container';
326: }
327: # If there is more than one sequence, list their titles
328: my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
329: if (@Sequences > 1) {
330: unshift(@Header,"Container");
331: $show_container = 1;
332: }
333: #
334: # If the option for showing the problem number is needed, push that
335: # on the list too
336: if (defined($option) && $option =~ /show probnum/) {
337: unshift(@Header,"P#");
338: }
339: #
340: $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
341: $r->rflush();
342: #
343: # Compile the data
344: my @Statsarray;
345: foreach my $sequence (@Sequences) {
346: next if ($sequence->{'num_assess'}<1);
347: foreach my $resource (@{$sequence->{'contents'}}) {
348: next if ($resource->{'type'} ne 'assessment');
349: foreach my $part (@{$resource->{'parts'}}) {
350: $problem_num++;
351: my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
352: $SKEW) = &Apache::loncoursedata::get_problem_statistics
353: (undef,$resource->{'symb'},$part,
354: $ENV{'request.course.id'});
355: #
356: $show_part = 1 if ($part ne '0');
357: $part = ' ' if ($part == 0);
358: #
359: my $wrongpercent = 0;
360: if (defined($num) && $num > 0) {
361: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
362: }
363: push (@Statsarray,
364: { 'sequence' => $sequence,
365: 'resource' => $resource,
366: 'Title' => $resource->{'title'},
367: 'Part' => $part,
368: '#Stdnts' => $num,
369: 'Tries' => $tries,
370: 'Mod' => $mod,
371: 'Mean' => $mean,
372: '#YES' => $Solved,
373: '#yes' => $solved,
374: '%Wrng' => $wrongpercent,
375: 'DoDiff' => $DegOfDiff,
376: 'S.D.' => $STD,
377: 'Skew' => $SKEW,
378: 'problem_num' => $problem_num,
379: });
380: }
381: }
382: }
383: #
384: # Table Headers
385: $r->print('<table border="0" cellpadding="3">'."\n");
386: my $Str = '';
387: foreach (@Header) {
388: next if ($_ eq 'Part' && !$show_part);
389: # Do not allow sorting on some fields
390: if ($_ eq $sortby || /^(Part|P\#)$/) {
391: $Str .= '<th>'.$_.'</th>';
392: } else {
393: $Str .= '<th>'.
394: '<a href="javascript:document.Statistics.sortby.value='."'$_'".
395: ';document.Statistics.submit();">'.
396: $_.'</a></th>';
397: }
398: }
399: $r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n");
400: #
401: # Sort the data
402: my @OutputOrder;
403: if ($sortby eq 'Container') {
404: @OutputOrder = @Statsarray;
405: } else {
406: # $sortby is already defined, so we can charge ahead
407: if ($sortby =~ /^(title|part)$/i) {
408: # Alpha comparison
409: @OutputOrder = sort {
410: lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
411: lc($a->{'Title'}) cmp lc($b->{'Title'}) ||
412: lc($a->{'Part'}) cmp lc($b->{'Part'});
413: } @Statsarray;
414: } else {
415: # Numerical comparison
416: @OutputOrder = sort {
417: my $retvalue = 0;
418: if ($b->{$sortby} eq 'nan') {
419: if ($a->{$sortby} ne 'nan') {
420: $retvalue = -1;
421: } else {
422: $retvalue = 0;
423: }
424: }
425: if ($a->{$sortby} eq 'nan') {
426: if ($b->{$sortby} ne 'nan') {
427: $retvalue = 1;
428: }
429: }
430: if ($retvalue eq '0') {
431: $retvalue = $b->{$sortby} <=> $a->{$sortby} ||
432: lc($a->{'Title'}) <=> lc($b->{'Title'}) ||
433: lc($a->{'Part'}) <=> lc($b->{'Part'});
434: }
435: $retvalue;
436: } @Statsarray;
437: }
438: }
439: $option .= ',no part' if (! $show_part);
440: foreach my $row (@OutputOrder) {
441: $r->print('<tr>');
442: if (defined($option) && $option =~ /show probnum/) {
443: $r->print('<td bgcolor="#FFFFE6">'.$row->{'problem_num'}.'</td>');
444: }
445: if ($show_container) {
446: $r->print('<td bgcolor="#FFFFE6">'
447: .$row->{'sequence'}->{'title'}.'</td>');
448: }
449: $r->print(&statistics_html_table_data
450: ($row->{'resource'},$row->{'Part'},$row->{'#Stdnts'},
451: $row->{'Tries'},$row->{'Mod'},$row->{'Mean'},
452: $row->{'#YES'},$row->{'#yes'},$row->{"\%Wrng"},
453: $row->{'DoDiff'},$row->{'S.D.'},$row->{'Skew'},
454: $option));
455:
456: $r->print("</tr>\n");
457: }
458: $r->print("</table>\n");
459: $r->print("</td></tr></table>\n");
460: $r->rflush();
461: #
462: return;
463: }
464:
465:
466: ###############################################
467: ###############################################
468:
469: ###############################################
470: ###############################################
471: sub output_excel {
472: my ($r) = @_;
473: my $filename = '/prtspool/'.
474: $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
475: time.'_'.rand(1000000000).'.xls';
476: #
477: my $excel_workbook = undef;
478: my $excel_sheet = undef;
479: #
480: my $rows_output = 0;
481: my $cols_output = 0;
482: #
483: # Create sheet
484: $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
485: #
486: # Check for errors
487: if (! defined($excel_workbook)) {
488: $r->log_error("Error creating excel spreadsheet $filename: $!");
489: $r->print("Problems creating new Excel file. ".
490: "This error has been logged. ".
491: "Please alert your LON-CAPA administrator");
492: return ;
493: }
494: #
495: # The excel spreadsheet stores temporary data in files, then put them
496: # together. If needed we should be able to disable this (memory only).
497: # The temporary directory must be specified before calling 'addworksheet'.
498: # File::Temp is used to determine the temporary directory.
499: $excel_workbook->set_tempdir($Apache::lonnet::tmpdir);
500: #
501: # Add a worksheet
502: my $sheetname = $ENV{'course.'.$ENV{'request.course.id'}.'.description'};
503: if (length($sheetname) > 31) {
504: $sheetname = substr($sheetname,0,31);
505: }
506: $excel_sheet = $excel_workbook->addworksheet($sheetname);
507: #
508: # Put the course description in the header
509: $excel_sheet->write($rows_output,$cols_output++,
510: $ENV{'course.'.$ENV{'request.course.id'}.'.description'});
511: $cols_output += 3;
512: #
513: # Put a description of the sections listed
514: my $sectionstring = '';
515: my @Sections = @Apache::lonstatistics::SelectedSections;
516: if (scalar(@Sections) > 1) {
517: if (scalar(@Sections) > 2) {
518: my $last = pop(@Sections);
519: $sectionstring = "Sections ".join(', ',@Sections).', and '.$last;
520: } else {
521: $sectionstring = "Sections ".join(' and ',@Sections);
522: }
523: } else {
524: if ($Sections[0] eq 'all') {
525: $sectionstring = "All sections";
526: } else {
527: $sectionstring = "Section ".$Sections[0];
528: }
529: }
530: $excel_sheet->write($rows_output,$cols_output++,$sectionstring);
531: $cols_output += scalar(@Sections);
532: #
533: # Put the date in there too
534: $excel_sheet->write($rows_output,$cols_output++,
535: 'Compiled on '.localtime(time));
536: #
537: $rows_output++;
538: $cols_output=0;
539: #
540: # Add the headers
541: my @Header = ("Container","Title","Part","#Stdnts","Tries","Mod",
542: "Mean","#YES","#yes","%Wrng","DoDiff",
543: "S.D.","Skew.");#,"D.F.1st","D.F.2nd");
544: foreach (@Header) {
545: $excel_sheet->write($rows_output,$cols_output++,$_);
546: }
547: $rows_output++;
548: #
549: # Write the data
550: foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
551: next if ($sequence->{'num_assess'}<1);
552: foreach my $resource (@{$sequence->{'contents'}}) {
553: next if ($resource->{'type'} ne 'assessment');
554: foreach my $part (@{$resource->{'parts'}}) {
555: $cols_output=0;
556: my ($num,$tries,$mod,$mean,$Solved,$solved,$DegOfDiff,$STD,
557: $SKEW) = &Apache::loncoursedata::get_problem_statistics
558: (undef,$resource->{'symb'},$part,
559: $ENV{'request.course.id'});
560: #
561: if (!defined($part) || $part eq '') {
562: $part = ' ';
563: }
564: my $wrongpercent = 0;
565: if (defined($num) && $num > 0) {
566: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
567: }
568: foreach ($sequence->{'title'},$resource->{'title'},$part,
569: $num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent,
570: $DegOfDiff,$STD,$SKEW) {
571: $excel_sheet->write($rows_output,$cols_output++,$_);
572: }
573: $rows_output++;
574: }
575: }
576: }
577: #
578: # Write the excel file
579: $excel_workbook->close();
580: # Tell the user where to get their excel file
581: $r->print('<br />'.
582: '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
583: $r->rflush();
584: return;
585: }
586:
587: ###############################################
588: ###############################################
589:
590: ###############################################
591: ###############################################
592: sub statistics_html_table_data {
593: my ($resource,$part,$num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent,
594: $DegOfDiff,$STD,$SKEW,$options) = @_;
595: my $row = '';
596: $row .= '<td bgcolor="#FFFFE6">'.
597: '<a href="'.$resource->{'src'}.'" target="_blank" >'.
598: $resource->{'title'}.'</a>'.
599: '</td>';
600: $row .= '<td bgcolor="#FFFFE6">'.$part.'</td>' if ($options !~ /no part/);
601: foreach ($num,$tries) {
602: $row .= '<td bgcolor="#EEFFCC" align="right">'.$_.'</td>';
603: }
604: foreach ($mod,$mean) {
605: $row .= '<td bgcolor="#DDFFFF" align="right">'.
606: sprintf("%5.2f",$_).'</td>';
607: }
608: foreach ($Solved,$solved) {
609: $row .= '<td bgcolor="#DDFFFF" align="right">'.$_.'</td>';
610: }
611: foreach ($wrongpercent) {
612: $row .= '<td bgcolor="#DDFFFF" align="right">'.
613: sprintf("%5.1f",$_).'</td>';
614: }
615: foreach ($DegOfDiff,$STD,$SKEW) {
616: $row .= '<td bgcolor="#FFDDDD" align="right">'.
617: sprintf("%5.2f",$_).'</td>';
618: }
619: return $row;
620: }
621:
622: ###############################################
623: ###############################################
624: sub plot_statistics {
625: my ($r,$datafield) = @_;
626: my @Data;
627: #
628: my %Fields = ('#Stdnts'=> 0,
629: 'Tries' => 1,
630: 'Mod' => 2,
631: 'Mean' => 3,
632: '#YES' => 4,
633: '#yes' => 5,
634: '%Wrng' => 9,
635: 'DoDiff' => 6,
636: 'S.D.' => 7,
637: 'Skew' => 8,);
638: #
639: my $field = '%Wrng';
640: foreach (keys(%Fields)) {
641: $field = $_ if ($datafield eq $_);
642: }
643: my $fieldindex = $Fields{$field};
644: #
645: my $Max = 0;
646: foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
647: next if ($sequence->{'num_assess'}<1);
648: foreach my $resource (@{$sequence->{'contents'}}) {
649: next if ($resource->{'type'} ne 'assessment');
650: foreach my $part (@{$resource->{'parts'}}) {
651: my @Results = &Apache::loncoursedata::get_problem_statistics
652: (undef,$resource->{'symb'},$part,
653: $ENV{'request.course.id'});
654: my ($num,$Solved,$solved) = @Results[0,4,5];
655: my $wrongpercent = 0;
656: if (defined($num) && $num > 0) {
657: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
658: }
659: push (@Results,$wrongpercent);
660: my $data = $Results[$fieldindex];
661: $Max = $data if ($Max<$data);
662: push (@Data,$data);
663: }
664: }
665: }
666: #
667: # Print out plot request
668: my $title = 'Percent Wrong';
669: if ($field eq 'DoDiff') {
670: $title = 'Degree of Difficulty';
671: }
672: my $yaxis = 'Percent';
673: if ($field eq 'DoDiff') {
674: $yaxis = '';
675: } elsif ($field ne '%Wrng') {
676: $yaxis = '';
677: }
678: #
679: # Determine appropriate value for $Max
680: if ($field eq 'DoDiff') {
681: if ($Max > 0.5) {
682: $Max = 1;
683: } elsif ($Max > 0.2) {
684: $Max = 0.5;
685: } elsif ($Max > 0.1) {
686: $Max = 0.2;
687: }
688: } elsif ($field eq '%Wrng') {
689: if ($Max > 50) {
690: $Max = 100;
691: } elsif ($Max > 25) {
692: $Max = 50;
693: } elsif ($Max > 20) {
694: $Max = 25;
695: } elsif ($Max > 10) {
696: $Max = 20;
697: } elsif ($Max > 5) {
698: $Max = 10;
699: } else {
700: $Max = 5;
701: }
702: }
703:
704: $r->print("<p>".&DrawGraph(\@Data,$title,'Problem Number',$yaxis,
705: $Max)."</p>\n");
706: #
707: # Print out the data
708: $ENV{'form.sortby'} = 'Contents';
709: &output_html_ungrouped($r,'show probnum');
710: return;
711: }
712:
713: ###############################################
714: ###############################################
715:
716: ###############################################
717: ###############################################
718: sub DrawGraph {
719: my ($values,$title,$xaxis,$yaxis,$Max)=@_;
720: $title = '' if (! defined($title));
721: $xaxis = '' if (! defined($xaxis));
722: $yaxis = '' if (! defined($yaxis));
723: #
724: my $sendValues = join(',', @$values);
725: my $sendCount = scalar(@$values);
726: if ( $Max > 1 ) {
727: if ($Max % 10) {
728: if ( int($Max) < $Max ) {
729: $Max++;
730: $Max = int($Max);
731: }
732: }
733: } else {
734: $Max = 1;
735: }
736: my @GData = ($title,$xaxis,$yaxis,$Max,$sendCount,$sendValues);
737: return '<IMG src="/cgi-bin/graph.png?'.
738: (join('&', @GData)).'" border="1" />';
739: }
740:
741: ###############################################
742: ###############################################
743:
744: ###############################################
745: ###############################################
746: sub ProblemStatisticsLegend {
747: my $Ptr = '';
748: $Ptr = '<table border="0">';
749: $Ptr .= '<tr><td>';
750: $Ptr .= '<b>#Stdnts</b></td>';
751: $Ptr .= '<td>Total number of students attempted the problem.';
752: $Ptr .= '</td></tr><tr><td>';
753: $Ptr .= '<b>Tries</b></td>';
754: $Ptr .= '<td>Total number of tries for solving the problem.';
755: $Ptr .= '</td></tr><tr><td>';
756: $Ptr .= '<b>Mod</b></td>';
757: $Ptr .= '<td>Largest number of tries for solving the problem by a student.';
758: $Ptr .= '</td></tr><tr><td>';
759: $Ptr .= '<b>Mean</b></td>';
760: $Ptr .= '<td>Average number of tries. [ Tries / #Stdnts ]';
761: $Ptr .= '</td></tr><tr><td>';
762: $Ptr .= '<b>#YES</b></td>';
763: $Ptr .= '<td>Number of students solved the problem correctly.';
764: $Ptr .= '</td></tr><tr><td>';
765: $Ptr .= '<b>#yes</b></td>';
766: $Ptr .= '<td>Number of students solved the problem by override.';
767: $Ptr .= '</td></tr><tr><td>';
768: $Ptr .= '<b>%Wrong</b></td>';
769: $Ptr .= '<td>Percentage of students who tried to solve the problem ';
770: $Ptr .= 'but is still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';
771: $Ptr .= '</td></tr><tr><td>';
772: $Ptr .= '<b>DoDiff</b></td>';
773: $Ptr .= '<td>Degree of Difficulty of the problem. ';
774: $Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';
775: $Ptr .= '</td></tr><tr><td>';
776: $Ptr .= '<b>S.D.</b></td>';
777: $Ptr .= '<td>Standard Deviation of the tries. ';
778: $Ptr .= '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) ';
779: $Ptr .= 'where Xi denotes every student\'s tries ]';
780: $Ptr .= '</td></tr><tr><td>';
781: $Ptr .= '<b>Skew.</b></td>';
782: $Ptr .= '<td>Skewness of the students tries.';
783: $Ptr .= '[(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]';
784: $Ptr .= '</td></tr><tr><td>';
785: $Ptr .= '<b>Dis.F.</b></td>';
786: $Ptr .= '<td>Discrimination Factor: A Standard for evaluating the ';
787: $Ptr .= 'problem according to a Criterion<br>';
788: $Ptr .= '<b>[Criterion to group students into %27 Upper Students - ';
789: $Ptr .= 'and %27 Lower Students]</b><br>';
790: $Ptr .= '<b>1st Criterion</b> for Sorting the Students: ';
791: $Ptr .= '<b>Sum of Partial Credit Awarded / Total Number of Tries</b><br>';
792: $Ptr .= '<b>2nd Criterion</b> for Sorting the Students: ';
793: $Ptr .= '<b>Total number of Correct Answers / Total Number of Tries</b>';
794: $Ptr .= '</td></tr>';
795: $Ptr .= '<tr><td><b>Disc.</b></td>';
796: $Ptr .= '<td>Number of Students had at least one discussion.';
797: $Ptr .= '</td></tr></table>';
798: return $Ptr;
799: }
800:
801: #---- END Problem Statistics Web Page ----------------------------------------
802:
803: 1;
804: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>