Annotation of loncom/interface/statistics/lonproblemstatistics.pm, revision 1.46
1.1 stredwic 1: # The LearningOnline Network with CAPA
2: #
1.46 ! matthew 3: # $Id: lonproblemstatistics.pm,v 1.45 2003/03/26 21:47:47 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: #
29: ###
30:
1.36 minaeibi 31: package Apache::lonproblemstatistics;
1.1 stredwic 32:
33: use strict;
34: use Apache::lonnet();
35: use Apache::lonhtmlcommon;
36: use Apache::loncoursedata;
1.41 matthew 37: use Apache::lonstatistics;
1.44 matthew 38: use Spreadsheet::WriteExcel;
1.1 stredwic 39:
1.41 matthew 40: #######################################################
41: #######################################################
1.1 stredwic 42:
1.41 matthew 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
1.16 minaeibi 81:
1.41 matthew 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";
1.1 stredwic 131: }
1.41 matthew 132: $Str .= "</dl>\n";
133: return $Str;
134: }
1.1 stredwic 135:
1.41 matthew 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};
1.25 stredwic 147: }
148: }
1.41 matthew 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: }
1.25 stredwic 169:
1.41 matthew 170: ###############################################
171: ###############################################
1.28 stredwic 172:
1.41 matthew 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');
1.28 stredwic 195: }
1.41 matthew 196: &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
197: $r->rflush();
198: }
1.28 stredwic 199:
1.41 matthew 200: ###############################################
201: ###############################################
1.21 stredwic 202:
1.41 matthew 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;
1.28 stredwic 217: }
1.41 matthew 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: }
1.44 matthew 233: } elsif ($output_mode eq 'excel') {
234: $r->print("<h2>Preparing Excel Spreadsheet</h2>");
235: &output_excel($r);
1.45 matthew 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: }
1.41 matthew 242: } else {
243: $r->print("<h1>Not implemented</h1>");
1.28 stredwic 244: }
1.41 matthew 245: return;
246: }
1.21 stredwic 247:
1.44 matthew 248: ###############################################
249: ###############################################
250:
251: ###############################################
252: ###############################################
1.41 matthew 253: sub output_html_grouped_by_sequence {
254: my ($r) = @_;
1.45 matthew 255: my $problem_num = 0;
1.41 matthew 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()) {
1.43 matthew 262: my $show_part = 0;
1.41 matthew 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'}}) {
1.45 matthew 272: $problem_num++;
1.41 matthew 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'});
1.43 matthew 277: #
278: $show_part = 1 if ($part ne '0');
279: $part = ' ' if ($part == 0);
280: #
1.41 matthew 281: my $wrongpercent = 0;
282: if (defined($num) && $num > 0) {
283: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
284: }
1.45 matthew 285: my $option = '';
286: $option .= 'no part' if (! $show_part);
1.41 matthew 287: $r->print('<tr>'.&statistics_html_table_data
288: ($resource,$part,$num,$tries,$mod,$mean,$Solved,
1.43 matthew 289: $solved,$wrongpercent,$DegOfDiff,$STD,$SKEW,
1.45 matthew 290: $option).
1.41 matthew 291: "</tr>\n");
292: }
293: }
294: $r->print("</table>\n");
295: $r->print("</td></tr></table>\n");
296: $r->rflush();
1.21 stredwic 297: }
1.41 matthew 298: #
299: return;
300: }
1.25 stredwic 301:
1.41 matthew 302: ###############################################
303: ###############################################
1.26 stredwic 304:
1.41 matthew 305: ###############################################
306: ###############################################
307: sub output_html_ungrouped {
1.45 matthew 308: my ($r,$option) = @_;
1.41 matthew 309: #
1.45 matthew 310: my $problem_num = 0;
1.41 matthew 311: my $show_container = 0;
1.43 matthew 312: my $show_part = 0;
1.41 matthew 313: #$r->print(&ProblemStatisticsLegend());
314: my @Header = ("Title","Part","#Stdnts","Tries","Mod",
1.26 stredwic 315: "Mean","#YES","#yes","%Wrng","DoDiff",
1.43 matthew 316: "S.D.","Skew");#,"D.F.1st","D.F.2nd");
1.42 matthew 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: }
1.45 matthew 327: # If there is more than one sequence, list their titles
1.41 matthew 328: my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
329: if (@Sequences > 1) {
330: unshift(@Header,"Container");
331: $show_container = 1;
332: }
1.45 matthew 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: }
1.41 matthew 339: #
340: $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
341: $r->rflush();
342: #
1.42 matthew 343: # Compile the data
344: my @Statsarray;
1.41 matthew 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'}}) {
1.45 matthew 350: $problem_num++;
1.41 matthew 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'});
1.43 matthew 355: #
356: $show_part = 1 if ($part ne '0');
357: $part = ' ' if ($part == 0);
358: #
1.41 matthew 359: my $wrongpercent = 0;
360: if (defined($num) && $num > 0) {
361: $wrongpercent=int(10*100*($num-$Solved+$solved)/$num)/10;
362: }
1.43 matthew 363: push (@Statsarray,
364: { 'sequence' => $sequence,
1.42 matthew 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,
1.45 matthew 378: 'problem_num' => $problem_num,
1.43 matthew 379: });
1.42 matthew 380: }
381: }
382: }
383: #
1.43 matthew 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
1.45 matthew 390: if ($_ eq $sortby || /^(Part|P\#)$/) {
1.43 matthew 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: #
1.42 matthew 401: # Sort the data
1.43 matthew 402: my @OutputOrder;
1.42 matthew 403: if ($sortby eq 'Container') {
1.43 matthew 404: @OutputOrder = @Statsarray;
1.42 matthew 405: } else {
406: # $sortby is already defined, so we can charge ahead
407: if ($sortby =~ /^(title|part)$/i) {
408: # Alpha comparison
409: @OutputOrder = sort {
1.43 matthew 410: lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
411: lc($a->{'Title'}) cmp lc($b->{'Title'}) ||
412: lc($a->{'Part'}) cmp lc($b->{'Part'});
1.42 matthew 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} ||
1.43 matthew 432: lc($a->{'Title'}) <=> lc($b->{'Title'}) ||
433: lc($a->{'Part'}) <=> lc($b->{'Part'});
1.41 matthew 434: }
1.42 matthew 435: $retvalue;
436: } @Statsarray;
437: }
1.43 matthew 438: }
1.45 matthew 439: $option .= ',no part' if (! $show_part);
1.43 matthew 440: foreach my $row (@OutputOrder) {
441: $r->print('<tr>');
1.45 matthew 442: if (defined($option) && $option =~ /show probnum/) {
443: $r->print('<td bgcolor="#FFFFE6">'.$row->{'problem_num'}.'</td>');
444: }
1.43 matthew 445: if ($show_container) {
446: $r->print('<td bgcolor="#FFFFE6">'
447: .$row->{'sequence'}->{'title'}.'</td>');
1.41 matthew 448: }
1.45 matthew 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:
1.43 matthew 456: $r->print("</tr>\n");
1.26 stredwic 457: }
1.41 matthew 458: $r->print("</table>\n");
459: $r->print("</td></tr></table>\n");
1.26 stredwic 460: $r->rflush();
1.41 matthew 461: #
462: return;
1.42 matthew 463: }
464:
1.41 matthew 465:
466: ###############################################
467: ###############################################
1.26 stredwic 468:
1.41 matthew 469: ###############################################
470: ###############################################
1.44 matthew 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:
1.45 matthew 587: ###############################################
588: ###############################################
1.44 matthew 589:
1.45 matthew 590: ###############################################
591: ###############################################
1.41 matthew 592: sub statistics_html_table_data {
593: my ($resource,$part,$num,$tries,$mod,$mean,$Solved,$solved,$wrongpercent,
1.45 matthew 594: $DegOfDiff,$STD,$SKEW,$options) = @_;
1.41 matthew 595: my $row = '';
596: $row .= '<td bgcolor="#FFFFE6">'.
597: '<a href="'.$resource->{'src'}.'" target="_blank" >'.
598: $resource->{'title'}.'</a>'.
599: '</td>';
1.45 matthew 600: $row .= '<td bgcolor="#FFFFE6">'.$part.'</td>' if ($options !~ /no part/);
1.41 matthew 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>';
1.26 stredwic 618: }
1.41 matthew 619: return $row;
620: }
1.26 stredwic 621:
1.41 matthew 622: ###############################################
623: ###############################################
1.45 matthew 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 $_);
1.34 minaeibi 642: }
1.45 matthew 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];
1.46 ! matthew 661: $data = 0 if ($data eq 'nan');
1.45 matthew 662: $Max = $data if ($Max<$data);
663: push (@Data,$data);
664: }
665: }
1.26 stredwic 666: }
1.45 matthew 667: #
668: # Print out plot request
669: my $title = 'Percent Wrong';
670: if ($field eq 'DoDiff') {
671: $title = 'Degree of Difficulty';
672: }
673: my $yaxis = 'Percent';
674: if ($field eq 'DoDiff') {
675: $yaxis = '';
676: } elsif ($field ne '%Wrng') {
677: $yaxis = '';
678: }
679: #
680: # Determine appropriate value for $Max
681: if ($field eq 'DoDiff') {
682: if ($Max > 0.5) {
683: $Max = 1;
684: } elsif ($Max > 0.2) {
685: $Max = 0.5;
686: } elsif ($Max > 0.1) {
687: $Max = 0.2;
688: }
689: } elsif ($field eq '%Wrng') {
690: if ($Max > 50) {
691: $Max = 100;
692: } elsif ($Max > 25) {
693: $Max = 50;
694: } elsif ($Max > 20) {
695: $Max = 25;
696: } elsif ($Max > 10) {
697: $Max = 20;
698: } elsif ($Max > 5) {
699: $Max = 10;
1.24 stredwic 700: } else {
1.45 matthew 701: $Max = 5;
1.24 stredwic 702: }
703: }
1.45 matthew 704:
705: $r->print("<p>".&DrawGraph(\@Data,$title,'Problem Number',$yaxis,
706: $Max)."</p>\n");
707: #
708: # Print out the data
709: $ENV{'form.sortby'} = 'Contents';
710: &output_html_ungrouped($r,'show probnum');
1.24 stredwic 711: return;
712: }
1.1 stredwic 713:
1.45 matthew 714: ###############################################
715: ###############################################
716:
717: ###############################################
718: ###############################################
1.35 minaeibi 719: sub DrawGraph {
1.45 matthew 720: my ($values,$title,$xaxis,$yaxis,$Max)=@_;
721: $title = '' if (! defined($title));
722: $xaxis = '' if (! defined($xaxis));
723: $yaxis = '' if (! defined($yaxis));
724: #
1.35 minaeibi 725: my $sendValues = join(',', @$values);
726: my $sendCount = scalar(@$values);
727: if ( $Max > 1 ) {
728: if ($Max % 10) {
1.36 minaeibi 729: if ( int($Max) < $Max ) {
730: $Max++;
731: $Max = int($Max);
732: }
1.35 minaeibi 733: }
1.45 matthew 734: } else {
735: $Max = 1;
1.1 stredwic 736: }
1.45 matthew 737: my @GData = ($title,$xaxis,$yaxis,$Max,$sendCount,$sendValues);
738: return '<IMG src="/cgi-bin/graph.png?'.
739: (join('&', @GData)).'" border="1" />';
1.1 stredwic 740: }
1.12 minaeibi 741:
1.45 matthew 742: ###############################################
743: ###############################################
1.1 stredwic 744:
1.45 matthew 745: ###############################################
746: ###############################################
1.1 stredwic 747: sub ProblemStatisticsLegend {
748: my $Ptr = '';
749: $Ptr = '<table border="0">';
750: $Ptr .= '<tr><td>';
1.6 minaeibi 751: $Ptr .= '<b>#Stdnts</b></td>';
1.19 stredwic 752: $Ptr .= '<td>Total number of students attempted the problem.';
1.1 stredwic 753: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 754: $Ptr .= '<b>Tries</b></td>';
1.19 stredwic 755: $Ptr .= '<td>Total number of tries for solving the problem.';
1.1 stredwic 756: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 757: $Ptr .= '<b>Mod</b></td>';
1.19 stredwic 758: $Ptr .= '<td>Largest number of tries for solving the problem by a student.';
1.1 stredwic 759: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 760: $Ptr .= '<b>Mean</b></td>';
1.19 stredwic 761: $Ptr .= '<td>Average number of tries. [ Tries / #Stdnts ]';
1.1 stredwic 762: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 763: $Ptr .= '<b>#YES</b></td>';
1.1 stredwic 764: $Ptr .= '<td>Number of students solved the problem correctly.';
765: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 766: $Ptr .= '<b>#yes</b></td>';
1.1 stredwic 767: $Ptr .= '<td>Number of students solved the problem by override.';
768: $Ptr .= '</td></tr><tr><td>';
1.19 stredwic 769: $Ptr .= '<b>%Wrong</b></td>';
770: $Ptr .= '<td>Percentage of students who tried to solve the problem ';
771: $Ptr .= 'but is still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';
1.1 stredwic 772: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 773: $Ptr .= '<b>DoDiff</b></td>';
1.1 stredwic 774: $Ptr .= '<td>Degree of Difficulty of the problem. ';
775: $Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';
776: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 777: $Ptr .= '<b>S.D.</b></td>';
1.1 stredwic 778: $Ptr .= '<td>Standard Deviation of the tries. ';
779: $Ptr .= '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) ';
780: $Ptr .= 'where Xi denotes every student\'s tries ]';
781: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 782: $Ptr .= '<b>Skew.</b></td>';
1.1 stredwic 783: $Ptr .= '<td>Skewness of the students tries.';
784: $Ptr .= '[(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]';
785: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 786: $Ptr .= '<b>Dis.F.</b></td>';
1.1 stredwic 787: $Ptr .= '<td>Discrimination Factor: A Standard for evaluating the ';
788: $Ptr .= 'problem according to a Criterion<br>';
1.31 stredwic 789: $Ptr .= '<b>[Criterion to group students into %27 Upper Students - ';
790: $Ptr .= 'and %27 Lower Students]</b><br>';
1.1 stredwic 791: $Ptr .= '<b>1st Criterion</b> for Sorting the Students: ';
792: $Ptr .= '<b>Sum of Partial Credit Awarded / Total Number of Tries</b><br>';
793: $Ptr .= '<b>2nd Criterion</b> for Sorting the Students: ';
794: $Ptr .= '<b>Total number of Correct Answers / Total Number of Tries</b>';
795: $Ptr .= '</td></tr>';
796: $Ptr .= '<tr><td><b>Disc.</b></td>';
797: $Ptr .= '<td>Number of Students had at least one discussion.';
798: $Ptr .= '</td></tr></table>';
799: return $Ptr;
800: }
1.24 stredwic 801:
802: #---- END Problem Statistics Web Page ----------------------------------------
1.4 minaeibi 803:
1.1 stredwic 804: 1;
805: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>