Annotation of loncom/interface/statistics/lonproblemstatistics.pm, revision 1.26
1.1 stredwic 1: # The LearningOnline Network with CAPA
2: # (Publication Handler
3: #
1.26 ! stredwic 4: # $Id: lonproblemstatistics.pm,v 1.25 2002/08/14 16:18:55 stredwic Exp $
1.1 stredwic 5: #
6: # Copyright Michigan State University Board of Trustees
7: #
8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
9: #
10: # LON-CAPA is free software; you can redistribute it and/or modify
11: # it under the terms of the GNU General Public License as published by
12: # the Free Software Foundation; either version 2 of the License, or
13: # (at your option) any later version.
14: #
15: # LON-CAPA is distributed in the hope that it will be useful,
16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18: # GNU General Public License for more details.
19: #
20: # You should have received a copy of the GNU General Public License
21: # along with LON-CAPA; if not, write to the Free Software
22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23: #
24: # /home/httpd/html/adm/gpl.txt
25: #
26: # http://www.lon-capa.org/
27: #
28: # (Navigate problems for statistical reports
29: # YEAR=2001
30: # 5/5,7/9,7/25/1,8/11,9/13,9/26,10/5,10/9,10/22,10/26 Behrouz Minaei
31: # 11/1,11/4,11/16,12/14,12/16,12/18,12/20,12/31 Behrouz Minaei
32: # YEAR=2002
33: # 1/22,2/1,2/6,2/25,3/2,3/6,3/17,3/21,3/22,3/26,4/7,5/6 Behrouz Minaei
1.12 minaeibi 34: # 5/12,5/14,5/15,5/19,5/26,7/16,7/25,7/29,8/5 Behrouz Minaei
1.1 stredwic 35: #
36: ###
37:
38: package Apache::lonproblemstatistics;
39:
40: use strict;
41: use Apache::lonnet();
42: use Apache::lonhtmlcommon;
43: use Apache::loncoursedata;
44: use GDBM_File;
45:
1.19 stredwic 46: my $jr;
1.1 stredwic 47:
1.26 ! stredwic 48: sub InitializeProblemStatistics {
1.5 minaeibi 49: my ($cacheDB, $students, $courseID, $c, $r)=@_;
1.1 stredwic 50: my %cache;
1.16 minaeibi 51:
1.19 stredwic 52: $jr = $r;
53:
1.18 albertel 54: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
1.21 stredwic 55: $r->print('Unable to tie database.');
1.26 ! stredwic 56: return ('ERROR', undef);
1.1 stredwic 57: }
58:
1.25 stredwic 59: # Remove students who don't have the proper section.
60: my @sectionsSelected = split(':',$cache{'sectionsSelected'});
61: for(my $studentIndex=((scalar @$students)-1); $studentIndex>=0;
62: $studentIndex--) {
63: my $value = $cache{$students->[$studentIndex].':section'};
64: my $found = 0;
65: foreach (@sectionsSelected) {
66: if($_ eq 'none') {
67: if($value eq '' || !defined($value) || $value eq ' ') {
68: $found = 1;
69: last;
70: }
71: } else {
72: if($value eq $_) {
73: $found = 1;
74: last;
75: }
76: }
77: }
78: if($found == 0) {
79: splice(@$students, $studentIndex, 1);
80: }
81: }
82:
1.19 stredwic 83: # my %Discuss=&Apache::loncoursedata::LoadDiscussion($courseID);
1.25 stredwic 84: my $lastStatus = (defined($cache{'StatisticsLastStatus'})) ?
85: $cache{'StatisticsLastStatus'} : 'Nothing';
86: my $whichStudents = join(':::',sort(@$students));
87: if(!defined($cache{'StatisticsCached'}) ||
88: $lastStatus ne $cache{'Status'} ||
89: $whichStudents ne $cache{'StatisticsWhichStudents'}) {
1.24 stredwic 90: if(defined($cache{'StatisticsCached'})) {
91: untie(%cache);
92: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT(),0640)) {
93: $r->print('Unable to tie database.');
1.26 ! stredwic 94: return ('ERROR', undef);
1.24 stredwic 95: }
96: my @statkeys = split(':::', $cache{'StatisticsKeys'});
97: delete $cache{'StatisticsKeys'};
98: delete $cache{'StatisticsCached'};
99: foreach(@statkeys) {
100: delete $cache{$_};
101: }
102: }
1.21 stredwic 103: untie(%cache);
104: &Apache::loncoursedata::DownloadStudentCourseDataSeparate($students,
105: 'true',
106: $cacheDB,
107: 'true',
108: 'true',
109: $courseID,
110: $r, $c);
1.26 ! stredwic 111: if($c->aborted()) { return ('ERROR', undef); }
1.21 stredwic 112:
113: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
114: $r->print('Unable to tie database.');
1.26 ! stredwic 115: return ('ERROR', undef);
1.21 stredwic 116: }
117: my ($problemData) = &ExtractStudentData(\%cache, $students);
1.24 stredwic 118: &CalculateStatistics($problemData, \%cache);
1.21 stredwic 119: untie(%cache);
120:
121: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_WRCREAT(),0640)) {
122: $r->print('Unable to tie database.');
1.26 ! stredwic 123: return ('ERROR', undef);
1.21 stredwic 124: }
125: foreach(keys(%$problemData)) {
126: $cache{$_} = $problemData->{$_};
127: }
1.24 stredwic 128: $cache{'StatisticsKeys'} = join(':::', keys(%$problemData));
1.21 stredwic 129: $cache{'StatisticsCached'} = 'true';
1.25 stredwic 130: $cache{'StatisticsLastStatus'} = $cache{'Status'};
131: $cache{'StatisticsWhichStudents'} = $whichStudents;
1.21 stredwic 132: untie(%cache);
133:
134: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
135: $r->print('Unable to tie database.');
1.26 ! stredwic 136: return ('ERROR', undef);
1.21 stredwic 137: }
138: }
1.25 stredwic 139:
1.21 stredwic 140: my $orderedProblems = &SortProblems(\%cache,
141: $cache{'ProblemStatisticsSort'},
1.26 ! stredwic 142: $cache{'SortProblems'},
1.21 stredwic 143: $cache{'ProblemStatisticsAscend'});
1.26 ! stredwic 144: return ('OK', $orderedProblems);
! 145: }
! 146:
! 147: sub BuildProblemStatisticsPage {
! 148: my ($cacheDB, $students, $courseID, $c, $r)=@_;
! 149:
! 150: my @Header = ("Homework Sets Order","#Stdnts","Tries","Mod",
! 151: "Mean","#YES","#yes","%Wrng","DoDiff",
! 152: "S.D.","Skew.","D.F.1st","D.F.2nd","Disc.");
! 153: my $color=&setbgcolor(0);
! 154: my %cache;
! 155:
! 156: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
! 157: $r->print('Unable to tie database.');
! 158: return;
! 159: }
! 160: my $Ptr = '';
! 161: $Ptr .= '<table border="0"><tbody>';
! 162: $Ptr .= '<tr><td align="right"><b>Select Map</b></td>'."\n";
! 163: $Ptr .= '<td align="left">';
! 164: $Ptr .= &Apache::lonhtmlcommon::MapOptions(\%cache, 'ProblemStatistics',
! 165: 'Statistics');
! 166: $Ptr .= '</td></tr>'."\n";
! 167: $Ptr .= '<tr><td align="right"><b>Sorting Type:</b></td>'."\n";
! 168: $Ptr .= '<td align="left">'."\n";
! 169: $Ptr .= &Apache::lonhtmlcommon::AscendOrderOptions(
! 170: $cache{'ProblemStatisticsAscend'},
! 171: 'ProblemStatistics',
! 172: 'Statistics');
! 173: $Ptr .= '</td></tr>'."\n";
! 174: $Ptr .= '<tr><td align="right"><b>Select Sections</b>';
! 175: $Ptr .= '</td>'."\n";
! 176: $Ptr .= '<td align="left">'."\n";
! 177: my @sections = split(':',$cache{'sectionList'});
! 178: my @sectionsSelected = split(':',$cache{'sectionsSelected'});
! 179: $Ptr .= &Apache::lonhtmlcommon::MultipleSectionSelect(\@sections,
! 180: \@sectionsSelected,
! 181: 'Statistics');
! 182: $Ptr .= '</td></tr>'."\n";
! 183: $Ptr .= &ProblemStatisticsButtons($cache{'DisplayFormat'},
! 184: $cache{'DisplayLegend'},
! 185: $cache{'SortProblems'});
! 186: $Ptr .= '</table>';
! 187: if($cache{'DisplayLegend'} eq 'Show Legend') {
! 188: $Ptr .= &ProblemStatisticsLegend();
! 189: }
! 190: $r->print($Ptr);
! 191: $r->rflush();
! 192: untie(%cache);
! 193:
! 194: my ($result, $orderedProblems) =
! 195: &InitializeProblemStatistics($cacheDB, $students, $courseID, $c, $r);
! 196: if($result ne 'OK') {
! 197: return;
! 198: }
! 199:
! 200: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
! 201: $r->print('Unable to tie database.');
! 202: return;
! 203: }
! 204: &BuildStatisticsTable(\%cache, $cache{'DisplayFormat'},
! 205: $cache{'SortProblems'}, $orderedProblems,
1.21 stredwic 206: \@Header, $r, $color);
1.19 stredwic 207: untie(%cache);
1.12 minaeibi 208:
1.19 stredwic 209: return;
1.1 stredwic 210: }
211:
1.24 stredwic 212: sub BuildGraphicChart {
1.26 ! stredwic 213: my ($graph,$cacheDB,$courseDescription,$students,$courseID,$r,$c)=@_;
1.24 stredwic 214: my %cache;
215: my $max = 0;
216:
1.26 ! stredwic 217: my ($result, undef) =
! 218: &InitializeProblemStatistics($cacheDB, $students, $courseID, $c, $r);
! 219: if($result ne 'OK') {
! 220: return;
! 221: }
! 222:
1.24 stredwic 223: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
1.26 ! stredwic 224: return 'Unable to tie database.';
1.24 stredwic 225: }
226:
227: my @problems = split(':::', $cache{'problemList'});
228: my @values = ();
229: foreach (@problems) {
230: my $data = 0;
231: if($graph eq 'DoDiffGraph') {
232: $data = sprintf("%.2f", $cache{$_.':degreeOfDifficulty'}),
233: } else {
234: $data = sprintf("%.1f", $cache{$_.':percentWrong'}),
235: }
236: if($max < $data) {
237: $max = $data;
238: }
239: push(@values, $data);
240: }
241: untie(%cache);
242:
243: my $sendValues = join(',', @values);
244: my $sendCount = scalar(@values);
245:
246: my $title = '';
247: if($graph eq 'DoDiffGraph') {
248: $title = 'Degree-of-Difficulty';
249: } else {
250: $title = 'Wrong-Percentage';
251: }
252: my @GData = ($courseDescription, 'Problems', $title, $max, $sendCount,
253: $sendValues);
254:
255: $r->print('</form>'."\n");
1.26 ! stredwic 256: $r->print('<IMG src="/cgi-bin/graph.gif?'.(join('&', @GData)).
! 257: '" border="1" />');
1.24 stredwic 258: $r->print('<form>'."\n");
259:
260: return;
261: }
1.1 stredwic 262:
263: #---- Problem Statistics Web Page ---------------------------------------
264:
265: sub CreateProblemStatisticsTableHeading {
1.19 stredwic 266: my ($headings,$r)=@_;
1.3 minaeibi 267:
1.19 stredwic 268: my $Str='';
269: $Str .= '<tr>'."\n";
270: $Str .= '<th bgcolor="#ffffe6">P#</th>'."\n";
271: foreach(@$headings) {
272: $Str .= '<th bgcolor="#ffffe6">'.'<a href="/adm/statistics?reportSelected=';
273: $Str .= &Apache::lonnet::escape('Problem Statistics');
274: $Str .= '&ProblemStatisticsSort=';
275: $Str .= &Apache::lonnet::escape($_).'">'.$_.'</a> </th>'."\n";
1.1 stredwic 276: }
1.19 stredwic 277: $Str .= "\n".'</tr>'."\n";
1.1 stredwic 278:
1.19 stredwic 279: return $Str;
1.1 stredwic 280: }
1.12 minaeibi 281:
1.1 stredwic 282: sub BuildStatisticsTable {
1.26 ! stredwic 283: my ($cache,$displayFormat,$sortProblems,$orderedProblems,$headings,
! 284: $r,$color)=@_;
1.5 minaeibi 285:
1.1 stredwic 286: #6666666
287: # my $file="/home/httpd/perl/tmp/183d.txt";
288: # open(OUT, ">$file");
289: #6666666
1.2 minaeibi 290: ## &Apache::lonstatistics::Create_PrgWin($r);
1.1 stredwic 291: ##777777
292: ## my (%Activity) = &LoadActivityLog();
293: ## $r->print('<script>popwin.document.popremain.remaining.value="'.
294: ## 'Loading Discussion...";</script>');
295: ## my ($doDiffFile) = &LoadDoDiffFile();
296:
1.5 minaeibi 297: ##777777
298: ## $Str .= &Classify($discriminantFactor, $students);
299:
1.19 stredwic 300: my $count = 1;
1.26 ! stredwic 301: my $currentSequence = -1;
1.21 stredwic 302: foreach(@$orderedProblems) {
1.19 stredwic 303: my ($sequence,$problem,$part)=split(':', $_);
1.25 stredwic 304: if($cache->{'StatisticsMaps'} ne 'All Maps' &&
305: $cache->{'StatisticsMaps'} ne $cache->{$sequence.':title'}) {
1.23 stredwic 306: next;
307: }
1.19 stredwic 308:
1.26 ! stredwic 309: if($currentSequence == -1 ||
! 310: ($sortProblems eq 'Sort Within Sequence' &&
! 311: $currentSequence != $sequence)) {
! 312: if($displayFormat ne 'Display CSV Format') {
! 313: if($currentSequence ne -1) {
! 314: $r->print('</table>');
! 315: $r->print('</td></tr></table><br>');
! 316: }
! 317: if($sortProblems eq 'Sort Within Sequence') {
! 318: $r->print('<b>'.$cache->{$sequence.':title'}.'</b>');
! 319: }
! 320: $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
! 321: $r->print('<table border="0" cellpadding="3">'."\n");
! 322: $r->print(&CreateProblemStatisticsTableHeading($headings, $r));
! 323: } else {
! 324: if($sortProblems eq 'Sort Within Sequence') {
! 325: $r->print('"'.$cache->{$sequence.':title'}.'"');
! 326: }
! 327: $r->print('<br>');
! 328: }
! 329: $currentSequence = $sequence;
! 330: }
! 331:
1.21 stredwic 332: my $ref = '<a href="'.$cache->{$problem.':source'}.
333: '" target="_blank">'.$cache->{$problem.':title'}.'</a>';
1.19 stredwic 334: my $title = $cache->{$problem.':title'};
1.26 ! stredwic 335: my $source = $cache->{$problem.':source'};
1.19 stredwic 336: my $tableData = join('&', $ref, $title, $source,
1.21 stredwic 337: $cache->{$_.':studentCount'},
338: $cache->{$_.':totalTries'},
339: $cache->{$_.':maxTries'},
340: sprintf("%.2f", $cache->{$_.':mean'}),
341: $cache->{$_.':correct'},
342: $cache->{$_.':correctByOverride'},
343: sprintf("%.1f", $cache->{$_.':percentWrong'}),
344: sprintf("%.2f", $cache->{$_.':degreeOfDifficulty'}),
345: sprintf("%.1f", $cache->{$_.':standardDeviation'}),
346: sprintf("%.1f", $cache->{$_.':skewness'}),
347: sprintf("%.2f", $cache->{$_.':discriminationFactor1'}),
348: sprintf("%.2f", $cache->{$_.':discriminationFactor2'}),
349: 0); # 0 is for discussion, need to figure out
1.1 stredwic 350:
1.19 stredwic 351: &TableRow($displayFormat,$tableData,$count,$r,$color);
1.26 ! stredwic 352:
1.19 stredwic 353: $count++;
354: }
1.26 ! stredwic 355: if($displayFormat ne 'Display CSV Format') {
1.19 stredwic 356: $r->print('</table>'."\n");
1.26 ! stredwic 357: $r->print('</td></tr></table>');
! 358: } else {
! 359: $r->print('<br>');
1.1 stredwic 360: }
1.14 minaeibi 361: #6666666
1.25 stredwic 362: # $r->print('<br>'.$out.'&'.$DoD);
363: # print (OUT $out.'@'.$DoD.'&');
364: #6666666
365:
366: #6666666
1.1 stredwic 367: # close( OUT );
368: #666666
1.21 stredwic 369: return;
1.1 stredwic 370: }
371:
372: sub TableRow {
1.19 stredwic 373: my ($displayFormat,$Str,$RealIdx,$r,$color)=@_;
374: my($ref,$title,$source,$StdNo,$TotalTries,$MxTries,$Avg,$YES,$Override,
1.1 stredwic 375: $Wrng,$DoD,$SD,$Sk,$_D1,$_D2,$DiscNo,$Prob)=split(/\&/,$Str);
1.8 minaeibi 376: my $Ptr;
1.19 stredwic 377: if($displayFormat eq 'Display CSV Format') {
1.26 ! stredwic 378: $Ptr='"'.$RealIdx.'",'."\n".
! 379: '"'.$title.'",'."\n".
! 380: '"'.$source.'",'."\n".
! 381: '"'.$StdNo.'",'."\n".
! 382: '"'.$TotalTries.'",'."\n".
! 383: '"'.$MxTries.'",'."\n".
! 384: '"'.$Avg.'",'."\n".
! 385: '"'.$YES.'",'."\n".
! 386: '"'.$Override.'",'."\n".
! 387: '"'.$Wrng.'",'."\n".
! 388: '"'.$DoD.'",'."\n".
! 389: '"'.$SD.'",'."\n".
! 390: '"'.$Sk.'",'."\n".
! 391: '"'.$_D1.'",'."\n".
! 392: '"'.$_D2.'"'."\n".
! 393: '"'.$DiscNo.'"'."\n".
! 394: "<br>\n";
1.1 stredwic 395:
396: $r->print("\n".$Ptr);
1.8 minaeibi 397: } else {
1.26 ! stredwic 398: $Ptr='<tr>'."\n".
! 399: '<td bgcolor="#ffffe6">'.$RealIdx.'</td>'."\n".
! 400: '<td bgcolor="#ffffe6">'.$ref.'</td>'."\n".
! 401: '<td bgcolor='.$color->{"yellow"}.'> '.$StdNo.'</td>'."\n".
! 402: '<td bgcolor='.$color->{"yellow"}.'>'.$TotalTries.'</td>'."\n".
! 403: '<td bgcolor='.$color->{"yellow"}.'>'.$MxTries.'</td>'."\n".
! 404: '<td bgcolor='.$color->{"gb"}.'>'.$Avg.'</td>'."\n".
! 405: '<td bgcolor='.$color->{"gb"}.'> '.$YES.'</td>'."\n".
! 406: '<td bgcolor='.$color->{"gb"}.'> '.$Override.'</td>'."\n".
! 407: '<td bgcolor='.$color->{"red"}.'> '.$Wrng.'</td>'."\n".
! 408: '<td bgcolor='.$color->{"red"}.'> '.$DoD.'</td>'."\n".
! 409: '<td bgcolor='.$color->{"green"}.'> '.$SD.'</td>'."\n".
! 410: '<td bgcolor='.$color->{"green"}.'> '.$Sk.'</td>'."\n".
! 411: '<td bgcolor='.$color->{"purple"}.'> '.$_D1.'</td>'."\n".
! 412: '<td bgcolor='.$color->{"purple"}.'> '.$_D2.'</td>'."\n".
! 413: '<td bgcolor='.$color->{"yellow"}.'> '.$DiscNo.'</td>'."\n";
! 414: $r->print($Ptr.'</tr>'."\n");
1.1 stredwic 415: }
1.19 stredwic 416:
417: return;
1.1 stredwic 418: }
1.5 minaeibi 419:
420: # For loading the colored table for display or un-colored for print
421: sub setbgcolor {
422: my $PrintTable=shift;
423: my %color;
424: if ($PrintTable){
425: $color{"gb"}="#FFFFFF";
426: $color{"red"}="#FFFFFF";
427: $color{"yellow"}="#FFFFFF";
428: $color{"green"}="#FFFFFF";
429: $color{"purple"}="#FFFFFF";
430: } else {
431: $color{"gb"}="#DDFFFF";
432: $color{"red"}="#FFDDDD";
433: $color{"yellow"}="#EEFFCC";
434: $color{"green"}="#DDFFDD";
435: $color{"purple"}="#FFDDFF";
436: }
437:
438: return \%color;
439: }
440:
1.1 stredwic 441: sub ProblemStatisticsButtons {
1.26 ! stredwic 442: my ($displayFormat, $displayLegend, $sortProblems)=@_;
1.1 stredwic 443:
444: my $Ptr = '<tr><td></td><td align="left">';
445: $Ptr .= '<input type="submit" name="DoDiffGraph" ';
446: $Ptr .= 'value="DoDiff Graph" />'."\n";
447: $Ptr .= ' ';
448: $Ptr .= '<input type="submit" name="PercentWrongGraph" ';
449: $Ptr .= 'value="%Wrong Graph" />'."\n";
1.20 stredwic 450: $Ptr .= '</td></tr><tr><td></td><td>'."\n";
1.26 ! stredwic 451: $Ptr .= '<input type="submit" name="SortProblems" ';
! 452: if($sortProblems eq 'Sort All Problems') {
! 453: $Ptr .= 'value="Sort Within Sequence" />'."\n";
! 454: } else {
! 455: $Ptr .= 'value="Sort All Problems" />'."\n";
! 456: }
! 457: $Ptr .= ' ';
1.20 stredwic 458: $Ptr .= '<input type="submit" name="DisplayLegend" ';
459: if($displayLegend eq 'Show Legend') {
460: $Ptr .= 'value="Hide Legend" />'."\n";
461: } else {
462: $Ptr .= 'value="Show Legend" />'."\n";
463: }
1.1 stredwic 464: $Ptr .= ' ';
465: $Ptr .= '<input type="submit" name="DisplayCSVFormat" ';
466: if($displayFormat eq 'Display CSV Format') {
1.9 stredwic 467: $Ptr .= 'value="Display Table Format" />'."\n";
468: } else {
1.1 stredwic 469: $Ptr .= 'value="Display CSV Format" />'."\n";
470: }
471: $Ptr .= '</td></tr>';
472:
473: return $Ptr;
474: }
475:
476: sub ProblemStatisticsLegend {
477: my $Ptr = '';
478: $Ptr = '<table border="0">';
479: $Ptr .= '<tr><td>';
1.6 minaeibi 480: $Ptr .= '<b>#Stdnts</b></td>';
1.19 stredwic 481: $Ptr .= '<td>Total number of students attempted the problem.';
1.1 stredwic 482: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 483: $Ptr .= '<b>Tries</b></td>';
1.19 stredwic 484: $Ptr .= '<td>Total number of tries for solving the problem.';
1.1 stredwic 485: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 486: $Ptr .= '<b>Mod</b></td>';
1.19 stredwic 487: $Ptr .= '<td>Largest number of tries for solving the problem by a student.';
1.1 stredwic 488: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 489: $Ptr .= '<b>Mean</b></td>';
1.19 stredwic 490: $Ptr .= '<td>Average number of tries. [ Tries / #Stdnts ]';
1.1 stredwic 491: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 492: $Ptr .= '<b>#YES</b></td>';
1.1 stredwic 493: $Ptr .= '<td>Number of students solved the problem correctly.';
494: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 495: $Ptr .= '<b>#yes</b></td>';
1.1 stredwic 496: $Ptr .= '<td>Number of students solved the problem by override.';
497: $Ptr .= '</td></tr><tr><td>';
1.19 stredwic 498: $Ptr .= '<b>%Wrong</b></td>';
499: $Ptr .= '<td>Percentage of students who tried to solve the problem ';
500: $Ptr .= 'but is still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';
1.1 stredwic 501: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 502: $Ptr .= '<b>DoDiff</b></td>';
1.1 stredwic 503: $Ptr .= '<td>Degree of Difficulty of the problem. ';
504: $Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';
505: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 506: $Ptr .= '<b>S.D.</b></td>';
1.1 stredwic 507: $Ptr .= '<td>Standard Deviation of the tries. ';
508: $Ptr .= '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) ';
509: $Ptr .= 'where Xi denotes every student\'s tries ]';
510: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 511: $Ptr .= '<b>Skew.</b></td>';
1.1 stredwic 512: $Ptr .= '<td>Skewness of the students tries.';
513: $Ptr .= '[(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]';
514: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 515: $Ptr .= '<b>Dis.F.</b></td>';
1.1 stredwic 516: $Ptr .= '<td>Discrimination Factor: A Standard for evaluating the ';
517: $Ptr .= 'problem according to a Criterion<br>';
518: $Ptr .= '<b>[Applied Criterion in %27 Upper Students - ';
519: $Ptr .= 'Applied the same Criterion in %27 Lower Students]</b><br>';
520: $Ptr .= '<b>1st Criterion</b> for Sorting the Students: ';
521: $Ptr .= '<b>Sum of Partial Credit Awarded / Total Number of Tries</b><br>';
522: $Ptr .= '<b>2nd Criterion</b> for Sorting the Students: ';
523: $Ptr .= '<b>Total number of Correct Answers / Total Number of Tries</b>';
524: $Ptr .= '</td></tr>';
525: $Ptr .= '<tr><td><b>Disc.</b></td>';
526: $Ptr .= '<td>Number of Students had at least one discussion.';
527: $Ptr .= '</td></tr></table>';
528:
529: return $Ptr;
530: }
531:
1.4 minaeibi 532: #------- Processing upperlist and lowerlist according to each problem
1.19 stredwic 533:
534: sub ExtractStudentData {
535: my ($cache, $students)=@_;
536:
537: #$Apache::lonxml::debug=1;
538: #&Apache::lonhomework::showhash(%$cache);
539: #$Apache::lonxml::debug=0;
540:
541: my @problemList=();
542: my %problemData;
543: foreach my $sequence (split(':', $cache->{'orderedSequences'})) {
544: foreach my $problemID (split(':', $cache->{$sequence.':problems'})) {
545: foreach my $part (split(/\:/,$cache->{$sequence.':'.
546: $problemID.
547: ':parts'})) {
548: my $id = $sequence.':'.$problemID.':'.$part;
549: push(@problemList, $id);
550: my $totalTries = 0;
551: my $totalAwarded = 0;
552: my $correct = 0;
553: my $correctByOverride = 0;
554: my $studentCount = 0;
555: my $maxTries = 0;
556: my $totalFirst = 0;
557: my @studentTries=();
558: foreach(@$students) {
559: my $code = $cache->{"$_:$problemID:$part:code"};
560:
561: if(defined($cache->{$_.':error'}) || $code eq ' ' ||
562: $cache->{"$_:$problemID:NoVersion"} eq 'true') {
563: next;
564: }
565:
566: $studentCount++;
567: my $tries = $cache->{"$_:$problemID:$part:tries"};
568: if($maxTries < $tries) {
569: $maxTries = $tries;
570: }
571: $totalTries += $tries;
572: push(@studentTries, $tries);
573:
574: my $awarded = $cache->{"$_:$problemID:$part:awarded"};
575: $totalAwarded += $awarded;
576:
577: if($code eq '*') {
578: $correct++;
579: if($tries == 1) {
580: $totalFirst++;
581: }
582: } elsif($code eq '+') {
583: $correctByOverride++;
584: }
585: }
586:
587: $problemData{$id.':sequenceTitle'} =
588: $cache->{$sequence.':title'};
589: $problemData{$id.':studentCount'} = $studentCount;
590: $problemData{$id.':totalTries'} = $totalTries;
591: $problemData{$id.':studentTries'} = \@studentTries;
592: $problemData{$id.':totalAwarded'} = $totalAwarded;
593: $problemData{$id.':correct'} = $correct;
594: $problemData{$id.':correctByOverride'} = $correctByOverride;
595: $problemData{$id.':wrong'} = $studentCount -
596: ($correct + $correctByOverride);
597: $problemData{$id.':maxTries'} = $maxTries;
598: $problemData{$id.':totalFirst'} = $totalFirst;
599: }
600: }
601: }
602:
1.24 stredwic 603: my @upperStudents1=();
604: my @lowerStudents1=();
605: my @upperStudents2=();
606: my @lowerStudents2=();
607: my $upperCount = int(0.27*scalar(@$students));
608: # Discriminant Factor criterion 1
609: my $sortedStudents = &SortDivideByTries($students,$cache,':totalAwarded');
610:
611: for(my $i=0; $i<$upperCount; $i++) {
612: push(@lowerStudents1, $sortedStudents->[$i]);
613: push(@upperStudents1, $sortedStudents->[(scalar(@$students)-$i-1)]);
614: }
615:
616: $problemData{'studentsUpperListCriterion1'}=join(':::', @upperStudents1);
617: $problemData{'studentsLowerListCriterion1'}=join(':::', @lowerStudents1);
618:
619: # Discriminant Factor criterion 2
620: $sortedStudents = &SortDivideByTries($students, $cache, ':totalSolved');
621:
622: for(my $i=0; $i<$upperCount; $i++) {
623: push(@lowerStudents2, $sortedStudents->[$i]);
624: push(@upperStudents2, $sortedStudents->[(scalar(@$students)-$i-1)]);
625: }
626: $problemData{'studentsUpperListCriterion2'}=join(':::', @upperStudents2);
627: $problemData{'studentsLowerListCriterion2'}=join(':::', @lowerStudents2);
628:
1.21 stredwic 629: $problemData{'problemList'} = join(':::', @problemList);
1.19 stredwic 630: # $Discussed=0;
631: # if($Discuss->{"$name:$problem"}) {
632: # $TotDiscuss++;
633: # $Discussed=1;
634: # }
635:
636: return \%problemData;
637: }
638:
1.24 stredwic 639: sub SortDivideByTries {
640: my ($toSort, $data, $sortOn)=@_;
641: my @orderedData = sort { ($data->{$a.':totalTries'}) ?
642: ($data->{$a.$sortOn}/$data->{$a.':totalTries'}):0
643: <=>
644: ($data->{$b.':totalTries'}) ?
645: ($data->{$b.$sortOn}/$data->{$b.':totalTries'}):0
646: } @$toSort;
647:
648: return \@orderedData;
649: }
650:
1.19 stredwic 651: sub SortProblems {
1.26 ! stredwic 652: my ($problemData,$sortBy,$sortProblems,$ascend)=@_;
1.19 stredwic 653:
1.21 stredwic 654: my @problems = split(':::', $problemData->{'problemList'});
1.19 stredwic 655: if($sortBy eq "Homework Sets Order") {
1.21 stredwic 656: return \@problems;
1.19 stredwic 657: }
658:
659: my $data;
660:
661: if ($sortBy eq "#Stdnts") { $data = ':studentCount'; }
662: elsif($sortBy eq "Tries") { $data = ':totalTries'; }
663: elsif($sortBy eq "Mod") { $data = ':maxTries'; }
664: elsif($sortBy eq "Mean") { $data = ':mean'; }
665: elsif($sortBy eq "#YES") { $data = ':correct'; }
666: elsif($sortBy eq "#yes") { $data = ':correctByOverride'; }
667: elsif($sortBy eq "%Wrng") { $data = ':percentWrong'; }
668: elsif($sortBy eq "DoDiff") { $data = ':degreeOfDifficulty'; }
669: elsif($sortBy eq "S.D.") { $data = ':standardDeviation'; }
670: elsif($sortBy eq "Skew.") { $data = ':skewness'; }
671: elsif($sortBy eq "D.F.1st") { $data = ':discriminantFactor1'; }
672: elsif($sortBy eq "D.F.2nd") { $data = ':discriminantFactor2'; }
673: elsif($sortBy eq "Disc.") { $data = ''; }
1.21 stredwic 674: else { return \@problems; }
1.19 stredwic 675:
1.26 ! stredwic 676: my %temp;
! 677: foreach(@problems) {
! 678: my ($sequence) = split(':', $_);
! 679: $temp{$_} = $sequence;
! 680: }
! 681:
! 682: my @orderedProblems;
! 683: if($sortProblems eq "Sort Within Sequence") {
! 684: @orderedProblems =
! 685: sort { $temp{$a} cmp $temp{$b} ||
! 686: $problemData->{$a.$data} <=> $problemData->{$b.$data} }
! 687: @problems;
! 688: } else {
! 689: @orderedProblems =
! 690: sort { $problemData->{$a.$data} <=> $problemData->{$b.$data} }
! 691: @problems;
! 692: }
! 693:
1.19 stredwic 694: if($ascend eq 'Descending') {
695: @orderedProblems = reverse(@orderedProblems);
696: }
697:
1.21 stredwic 698: return \@orderedProblems;
1.19 stredwic 699: }
700:
701: sub CalculateStatistics {
1.24 stredwic 702: my ($data, $cache)=@_;
1.19 stredwic 703:
1.21 stredwic 704: my @problems = split(':::', $data->{'problemList'});
705: foreach(@problems) {
1.19 stredwic 706: # Mean
707: $data->{$_.':mean'} = ($data->{$_.':studentCount'}) ?
708: ($data->{$_.':totalTries'} / $data->{$_.':studentCount'}) : 0;
709:
710: # %Wrong
711: $data->{$_.':percentWrong'} = ($data->{$_.':studentCount'}) ?
712: (($data->{$_.':wrong'} / $data->{$_.':studentCount'}) * 100.0) :
713: 100.0;
714:
715: # Degree of Difficulty
716: $data->{$_.':degreeOfDifficulty'} = ($data->{$_.':totalTries'}) ?
717: (1 - (($data->{$_.':correct'} + $data->{$_.':correctByOverride'}) /
718: $data->{$_.':totalTries'})) : 0;
719:
720: # Factor in mean
721: my $studentTries = $data->{$_.':studentTries'};
722: foreach(my $index=0; $index < scalar(@$studentTries); $index++) {
723: $studentTries->[$index] -= $data->{$_.':mean'};
724: }
725: my $sumSquared = 0;
726: my $sumCubed = 0;
727: foreach(@$studentTries) {
728: my $squared = ($_ * $_);
729: my $cubed = ($squared * $_);
730: $sumSquared += $squared;
731: $sumCubed += $cubed;
732: }
733:
734: # Standard deviation
735: $data->{$_.':standardDeviation'} = ($data->{$_.':studentCount'} - 1) ?
736: ((sqrt($sumSquared)) / ($data->{$_.':studentCount'} - 1)) : 0;
737:
738: # Skewness
739: my $standardDeviation = $data->{$_.':standardDeviation'};
740: $data->{$_.':skewness'} = ($data->{$_.':standardDeviation'}) ?
741: (((sqrt($sumSquared)) / $data->{$_.':studentCount'}) /
742: ($standardDeviation * $standardDeviation * $standardDeviation)) :
743: 0;
744:
745: # Discrimination Factor 1
1.24 stredwic 746: my ($sequence, $problem, $part) = split(':', $_);
1.19 stredwic 747:
1.24 stredwic 748: my @upper1 = split(':::', $data->{'studentsUpperListCriterion1'});
749: my @lower1 = split(':::', $data->{'studentsLowerListCriterion1'});
1.19 stredwic 750:
1.24 stredwic 751: my $upper1Sum=0;
752: foreach my $name (@upper1) {
753: $upper1Sum += $cache->{"$name:$problem:$part:awarded"};
754: }
1.25 stredwic 755: $upper1Sum = (scalar(@upper1)) ? ($upper1Sum/(scalar(@upper1))) : 0;
1.19 stredwic 756:
1.24 stredwic 757: my $lower1Sum=0;
758: foreach my $name (@lower1) {
759: $lower1Sum += $cache->{"$name:$problem:$part:awarded"};
1.4 minaeibi 760: }
1.25 stredwic 761: $lower1Sum = (scalar(@lower1)) ? ($lower1Sum/(scalar(@lower1))) : 0;
1.4 minaeibi 762:
1.24 stredwic 763: $data->{$_.':discriminationFactor1'} = $upper1Sum - $lower1Sum;
1.4 minaeibi 764:
1.24 stredwic 765: # Discrimination Factor 2
766: my @upper2 = split(':::', $data->{'studentsUpperListCriterion2'});
767: my @lower2 = split(':::', $data->{'studentsLowerListCriterion2'});
1.1 stredwic 768:
1.24 stredwic 769: my $upper2Sum=0;
770: foreach my $name (@upper2) {
771: $upper2Sum += $cache->{"$name:$problem:$part:awarded"};
772: }
1.25 stredwic 773: $upper2Sum = (scalar(@upper2)) ? ($upper2Sum/(scalar(@upper2))) : 0;
1.14 minaeibi 774:
1.24 stredwic 775: my $lower2Sum=0;
776: foreach my $name (@lower2) {
777: $lower2Sum += $cache->{"$name:$problem:$part:awarded"};
1.22 stredwic 778: }
1.25 stredwic 779: $lower2Sum = (scalar(@lower2)) ? ($lower2Sum/(scalar(@lower2))) : 0;
1.22 stredwic 780:
1.24 stredwic 781: $data->{$_.':discriminationFactor2'} = $upper2Sum - $lower2Sum;
1.16 minaeibi 782: }
783:
784: return;
1.1 stredwic 785: }
1.24 stredwic 786:
787: #---- END Problem Statistics Web Page ----------------------------------------
1.4 minaeibi 788:
1.1 stredwic 789: 1;
790: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>