Annotation of loncom/interface/statistics/lonproblemstatistics.pm, revision 1.49
1.1 stredwic 1: # The LearningOnline Network with CAPA
2: #
1.49 ! matthew 3: # $Id: lonproblemstatistics.pm,v 1.48 2003/03/28 15:20:59 matthew Exp $
1.1 stredwic 4: #
5: # Copyright Michigan State University Board of Trustees
6: #
7: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
8: #
9: # LON-CAPA is free software; you can redistribute it and/or modify
10: # it under the terms of the GNU General Public License as published by
11: # the Free Software Foundation; either version 2 of the License, or
12: # (at your option) any later version.
13: #
14: # LON-CAPA is distributed in the hope that it will be useful,
15: # but WITHOUT ANY WARRANTY; without even the implied warranty of
16: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17: # GNU General Public License for more details.
18: #
19: # You should have received a copy of the GNU General Public License
20: # along with LON-CAPA; if not, write to the Free Software
21: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22: #
23: # /home/httpd/html/adm/gpl.txt
24: #
25: # http://www.lon-capa.org/
26: #
27: # (Navigate problems for statistical reports
28: #
1.47 matthew 29: ###############################################
30: ###############################################
31:
32: =pod
33:
34: =head1 NAME
35:
36: lonproblemstatistics
37:
38: =head1 SYNOPSIS
39:
40: Routines to present problem statistics to instructors via tables,
41: Excel files, and plots.
42:
43: =over 4
44:
45: =cut
46:
47: ###############################################
48: ###############################################
1.1 stredwic 49:
1.36 minaeibi 50: package Apache::lonproblemstatistics;
1.1 stredwic 51:
52: use strict;
53: use Apache::lonnet();
54: use Apache::lonhtmlcommon;
55: use Apache::loncoursedata;
1.41 matthew 56: use Apache::lonstatistics;
1.44 matthew 57: use Spreadsheet::WriteExcel;
1.1 stredwic 58:
1.49 ! matthew 59: my @Fields = (
! 60: { name => 'problem_num',
! 61: title => 'P#',
! 62: align => 'right',
! 63: color => '#FFFFE6' },
! 64: { name => 'container',
! 65: title => 'Container',
! 66: align => 'left',
! 67: color => '#FFFFE6',
! 68: sortable => 'yes' },
! 69: { name => 'title',
! 70: title => 'Title',
! 71: align => 'left',
! 72: color => '#FFFFE6',
! 73: special => 'link',
! 74: sortable => 'yes', },
! 75: { name => 'part',
! 76: title => 'Part',
! 77: align => 'left',
! 78: color => '#FFFFE6' },
! 79: { name => 'num_students',
! 80: title => '#Stdnts',
! 81: align => 'right',
! 82: color => '#EEFFCC',
! 83: format => '%d',
! 84: sortable => 'yes',
! 85: graphable => 'yes',
! 86: long_title => 'Number of Students Attempting Problem' },
! 87: { name => 'tries',
! 88: title => 'Tries',
! 89: align => 'right',
! 90: color => '#EEFFCC',
! 91: format => '%d',
! 92: sortable => 'yes',
! 93: graphable => 'yes',
! 94: long_title => 'Total Number of Tries' },
! 95: { name => 'max_tries',
! 96: title => 'Max Tries',
! 97: align => 'right',
! 98: color => '#DDFFFF',
! 99: format => '%d',
! 100: sortable => 'yes',
! 101: graphable => 'yes',
! 102: long_title => 'Maximum Number of Tries' },
! 103: { name => 'mean_tries',
! 104: title => 'Mean Tries',
! 105: align => 'right',
! 106: color => '#DDFFFF',
! 107: format => '%5.2f',
! 108: sortable => 'yes',
! 109: graphable => 'yes',
! 110: long_title => 'Average Number of Tries' },
! 111: { name => 'std_tries',
! 112: title => 'S.D. tries',
! 113: align => 'right',
! 114: color => '#DDFFFF',
! 115: format => '%5.2f',
! 116: sortable => 'yes',
! 117: graphable => 'yes',
! 118: long_title => 'Standard Deviation of Number of Tries' },
! 119: { name => 'skew_tries',
! 120: title => 'Skew Tries',
! 121: align => 'right',
! 122: color => '#DDFFFF',
! 123: format => '%5.2f',
! 124: sortable => 'yes',
! 125: graphable => 'yes',
! 126: long_title => 'Skew of Number of Tries' },
! 127: { name => 'deg_of_diff',
! 128: title => 'DoDiff',
! 129: align => 'right',
! 130: color => '#DDFFFF',
! 131: format => '%5.2f',
! 132: sortable => 'yes',
! 133: graphable => 'yes',
! 134: long_title => 'Degree of Difficulty' },
! 135: { name => 'num_solved',
! 136: title => '#YES',
! 137: align => 'right',
! 138: color => '#FFDDDD',
! 139: format => '%d',
! 140: sortable => 'yes',
! 141: graphable => 'yes',
! 142: long_title => 'Number of Students able to Solve' },
! 143: { name => 'num_override',
! 144: title => '#yes',
! 145: align => 'right',
! 146: color => '#FFDDDD',
! 147: format => '%d',
! 148: sortable => 'yes',
! 149: graphable => 'yes',
! 150: long_title => 'Number of Students given Override' },
! 151: { name => 'per_wrong',
! 152: title => '%Wrng',
! 153: align => 'right',
! 154: color => '#FFFFE6',
! 155: format => '%4.1f',
! 156: sortable => 'yes',
! 157: graphable => 'yes',
! 158: long_title => 'Percent Wrong' },
! 159: );
! 160:
1.47 matthew 161: ###############################################
162: ###############################################
163:
164: =pod
165:
166: =item &CreateInterface()
167:
168: Create the main intereface for the statistics page. Allows the user to
169: select sections, maps, and output.
170:
171: =cut
1.1 stredwic 172:
1.47 matthew 173: ###############################################
174: ###############################################
1.41 matthew 175: sub CreateInterface {
176: my $Str = '';
177: $Str .= '<table cellspacing="5">'."\n";
178: $Str .= '<tr>';
179: $Str .= '<td align="center"><b>Sections</b></td>';
180: $Str .= '<td align="center"><b>Sequences and Folders</b></td>';
181: $Str .= '<td align="center"><b>Output</b></td>';
182: $Str .= '</tr>'."\n";
183: #
184: $Str .= '<tr><td align="center">'."\n";
185: $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
186: $Str .= '</td><td align="center">';
187: #
188: my $only_seq_with_assessments = sub {
189: my $s=shift;
190: if ($s->{'num_assess'} < 1) {
191: return 0;
192: } else {
193: return 1;
194: }
195: };
196: $Str .= &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
197: $only_seq_with_assessments);
198: $Str .= '</td><td>'."\n";
199: $Str .= &CreateAndParseOutputSelector();
200: $Str .= '</td></tr>'."\n";
201: $Str .= '</table>'."\n";
202: return $Str;
203: }
204:
205: #######################################################
206: #######################################################
207:
208: =pod
209:
210: =item &CreateAndParseOutputSelector()
211:
1.47 matthew 212: Construct a selection list of options for output and parse output selections.
213: The current output selected is indicated by the values of the two package
214: variables $output_mode and $show. @OutputOptions holds the descriptions of
215: the output options and the values for $output_mode and $show.
216:
1.49 ! matthew 217: Based on code from lonstudentassessment.pm.
1.47 matthew 218:
1.41 matthew 219: =cut
1.16 minaeibi 220:
1.41 matthew 221: #######################################################
222: #######################################################
223: my $output_mode;
224: my $show;
225:
226: my @OutputOptions =
227: (
228: { name => 'problem statistics grouped by sequence',
229: value => 'HTML problem statistics grouped',
230: description => 'Output statistics for the problem parts.',
231: mode => 'html',
232: show => 'grouped',
233: },
234: { name => 'problem statistics ungrouped',
235: value => 'HTML problem statistics ungrouped',
236: description => 'Output statistics for the problem parts.',
237: mode => 'html',
238: show => 'ungrouped',
239: },
240: { name => 'problem statistics, Excel',
241: value => 'Excel problem statistics',
242: description => 'Output statistics for the problem parts '.
243: 'in an Excel workbook',
244: mode => 'excel',
245: show => 'all',
246: },
247: );
248:
249: sub OutputDescriptions {
250: my $Str = '';
251: $Str .= "<h2>Output Modes</h2>\n";
252: $Str .= "<dl>\n";
253: foreach my $outputmode (@OutputOptions) {
254: $Str .=" <dt>".$outputmode->{'name'}."</dt>\n";
255: $Str .=" <dd>".$outputmode->{'description'}."</dd>\n";
1.1 stredwic 256: }
1.41 matthew 257: $Str .= "</dl>\n";
258: return $Str;
259: }
1.1 stredwic 260:
1.41 matthew 261: sub CreateAndParseOutputSelector {
262: my $Str = '';
1.47 matthew 263: my $elementname = 'statsoutputmode';
1.41 matthew 264: #
265: # Format for output options is 'mode, restrictions';
1.47 matthew 266: my $selected = 'HTML problem statistics grouped';
1.41 matthew 267: if (exists($ENV{'form.'.$elementname})) {
268: if (ref($ENV{'form.'.$elementname} eq 'ARRAY')) {
269: $selected = $ENV{'form.'.$elementname}->[0];
270: } else {
271: $selected = $ENV{'form.'.$elementname};
1.25 stredwic 272: }
273: }
1.41 matthew 274: #
275: # Set package variables describing output mode
276: $output_mode = 'html';
277: $show = 'all';
278: foreach my $option (@OutputOptions) {
279: next if ($option->{'value'} ne $selected);
280: $output_mode = $option->{'mode'};
281: $show = $option->{'show'};
282: }
283: #
284: # Build the form element
285: $Str = qq/<select size="5" name="$elementname">/;
286: foreach my $option (@OutputOptions) {
1.49 ! matthew 287: if (exists($option->{'special'}) &&
! 288: $option->{'special'} =~ /do not show/) {
! 289: next;
! 290: }
1.41 matthew 291: $Str .= "\n".' <option value="'.$option->{'value'}.'"';
292: $Str .= " selected " if ($option->{'value'} eq $selected);
293: $Str .= ">".$option->{'name'}."<\/option>";
294: }
295: $Str .= "\n</select>";
296: return $Str;
297: }
1.25 stredwic 298:
1.41 matthew 299: ###############################################
300: ###############################################
1.28 stredwic 301:
1.47 matthew 302: =pod
303:
304: =item &Gather_Student_Data()
305:
306: Ensures all student data is up to date.
307:
308: =cut
309:
1.41 matthew 310: ###############################################
311: ###############################################
312: sub Gather_Student_Data {
313: my ($r) = @_;
314: my $c = $r->connection();
315: #
316: my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
317: #
318: my @Students = @Apache::lonstatistics::Students;
319: #
320: # Open the progress window
321: my %prog_state=&Apache::lonhtmlcommon::Create_PrgWin
322: ($r,'Statistics Compilation Status',
323: 'Statistics Compilation Progress', scalar(@Students));
324: #
325: while (my $student = shift @Students) {
326: return if ($c->aborted());
327: my ($status,undef) = &Apache::loncoursedata::ensure_current_data
328: ($student->{'username'},$student->{'domain'},
329: $ENV{'request.course.id'});
330: &Apache::lonhtmlcommon::Increment_PrgWin($r,\%prog_state,
331: 'last student');
1.28 stredwic 332: }
1.41 matthew 333: &Apache::lonhtmlcommon::Close_PrgWin($r,\%prog_state);
334: $r->rflush();
335: }
1.28 stredwic 336:
1.41 matthew 337: ###############################################
338: ###############################################
1.21 stredwic 339:
1.47 matthew 340: =pod
341:
342: =item &BuildProblemStatisticsPage()
343:
344: Main interface to problem statistics.
345:
346: =cut
347:
1.41 matthew 348: ###############################################
349: ###############################################
350: sub BuildProblemStatisticsPage {
351: my ($r,$c)=@_;
352: #
353: $output_mode = 'html';
354: $show = 'grouped';
355: #
356: $r->print(&CreateInterface());
357: $r->print('<input type="hidden" name="statsfirstcall" value="no" />');
358: $r->print('<input type="hidden" name="sortby" value="'.$ENV{'form.sortby'}.
359: '" />');
1.49 ! matthew 360: $r->print('<input type="hidden" name="plot" value="" />');
1.41 matthew 361: if (! exists($ENV{'form.statsfirstcall'})) {
1.47 matthew 362: $r->print(<<ENDMSG);
363: <p>
364: <font size="+1">
365: Please make your selections in the boxes above and hit
366: the button marked "Update Display".
367: </font>
368: </p>
369: ENDMSG
1.41 matthew 370: return;
1.28 stredwic 371: }
1.41 matthew 372: #
373: &Gather_Student_Data($r);
374: #
375: #
376: if ($output_mode eq 'html') {
377: $r->print("<h2>".
378: $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.
379: "</h2>\n");
380: $r->print("<h3>".localtime(time)."</h3>");
381: $r->rflush();
382: if ($show eq 'grouped') {
383: &output_html_grouped_by_sequence($r);
384: } elsif ($show eq 'ungrouped') {
385: &output_html_ungrouped($r);
386: }
1.44 matthew 387: } elsif ($output_mode eq 'excel') {
388: $r->print("<h2>Preparing Excel Spreadsheet</h2>");
389: &output_excel($r);
1.41 matthew 390: } else {
391: $r->print("<h1>Not implemented</h1>");
1.28 stredwic 392: }
1.41 matthew 393: return;
394: }
1.21 stredwic 395:
1.44 matthew 396: ###############################################
397: ###############################################
398:
1.47 matthew 399: =pod
400:
401: =item &output_html_grouped_by_sequence()
402:
403: Presents the statistics data as an html table organized by the order
404: the assessments appear in the course.
405:
406: =cut
407:
1.44 matthew 408: ###############################################
409: ###############################################
1.41 matthew 410: sub output_html_grouped_by_sequence {
411: my ($r) = @_;
1.45 matthew 412: my $problem_num = 0;
1.41 matthew 413: #$r->print(&ProblemStatisticsLegend());
414: foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
415: next if ($sequence->{'num_assess'}<1);
416: $r->print("<h3>".$sequence->{'title'}."</h3>");
417: $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
418: $r->print('<table border="0" cellpadding="3">'."\n");
1.49 ! matthew 419: $r->print('<tr bgcolor="#FFFFE6">');
! 420: my $Str = &statistics_table_header('no container no plots');
! 421: $r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n");
1.41 matthew 422: foreach my $resource (@{$sequence->{'contents'}}) {
423: next if ($resource->{'type'} ne 'assessment');
424: foreach my $part (@{$resource->{'parts'}}) {
1.45 matthew 425: $problem_num++;
1.49 ! matthew 426: my $data = &get_statistics($sequence,$resource,$part,
! 427: $problem_num);
1.45 matthew 428: my $option = '';
1.49 ! matthew 429: $r->print('<tr>'.&statistics_html_table_data($data,
! 430: 'no container').
1.41 matthew 431: "</tr>\n");
432: }
433: }
434: $r->print("</table>\n");
435: $r->print("</td></tr></table>\n");
436: $r->rflush();
1.21 stredwic 437: }
1.41 matthew 438: #
439: return;
440: }
1.25 stredwic 441:
1.41 matthew 442: ###############################################
443: ###############################################
1.26 stredwic 444:
1.47 matthew 445: =pod
446:
447: =item &output_html_ungrouped()
448:
449: Presents the statistics data in a single html table which can be sorted by
450: different columns.
451:
452: =cut
453:
1.41 matthew 454: ###############################################
455: ###############################################
456: sub output_html_ungrouped {
1.45 matthew 457: my ($r,$option) = @_;
1.41 matthew 458: #
1.49 ! matthew 459: if (exists($ENV{'form.plot'}) && $ENV{'form.plot'} ne '') {
! 460: &plot_statistics($r,$ENV{'form.plot'});
! 461: }
! 462: #
1.45 matthew 463: my $problem_num = 0;
1.41 matthew 464: my $show_container = 0;
1.43 matthew 465: my $show_part = 0;
1.41 matthew 466: #$r->print(&ProblemStatisticsLegend());
1.42 matthew 467: my $sortby = undef;
1.49 ! matthew 468: foreach my $field (@Fields) {
! 469: if ($ENV{'form.sortby'} eq $field->{'name'}) {
! 470: $sortby = $field->{'name'};
1.42 matthew 471: }
472: }
1.49 ! matthew 473: if (! defined($sortby) || $sortby eq '' || $sortby eq 'problem_num') {
! 474: $sortby = 'container';
1.42 matthew 475: }
1.45 matthew 476: # If there is more than one sequence, list their titles
1.41 matthew 477: my @Sequences = &Apache::lonstatistics::Sequences_with_Assess();
1.49 ! matthew 478: if (@Sequences < 1) {
! 479: $option .= ' no container';
1.45 matthew 480: }
1.41 matthew 481: #
1.42 matthew 482: # Compile the data
483: my @Statsarray;
1.41 matthew 484: foreach my $sequence (@Sequences) {
485: next if ($sequence->{'num_assess'}<1);
486: foreach my $resource (@{$sequence->{'contents'}}) {
487: next if ($resource->{'type'} ne 'assessment');
488: foreach my $part (@{$resource->{'parts'}}) {
1.45 matthew 489: $problem_num++;
1.49 ! matthew 490: my $data = &get_statistics($sequence,$resource,$part,
! 491: $problem_num);
1.43 matthew 492: $show_part = 1 if ($part ne '0');
493: #
1.49 ! matthew 494: push (@Statsarray,$data);
1.42 matthew 495: }
496: }
497: }
498: #
499: # Sort the data
1.43 matthew 500: my @OutputOrder;
1.49 ! matthew 501: if ($sortby eq 'container') {
1.43 matthew 502: @OutputOrder = @Statsarray;
1.42 matthew 503: } else {
504: # $sortby is already defined, so we can charge ahead
505: if ($sortby =~ /^(title|part)$/i) {
506: # Alpha comparison
507: @OutputOrder = sort {
1.43 matthew 508: lc($a->{$sortby}) cmp lc($b->{$sortby}) ||
1.49 ! matthew 509: lc($a->{'title'}) cmp lc($b->{'title'}) ||
! 510: lc($a->{'part'}) cmp lc($b->{'part'});
1.42 matthew 511: } @Statsarray;
512: } else {
513: # Numerical comparison
514: @OutputOrder = sort {
515: my $retvalue = 0;
516: if ($b->{$sortby} eq 'nan') {
517: if ($a->{$sortby} ne 'nan') {
518: $retvalue = -1;
519: } else {
520: $retvalue = 0;
521: }
522: }
523: if ($a->{$sortby} eq 'nan') {
524: if ($b->{$sortby} ne 'nan') {
525: $retvalue = 1;
526: }
527: }
528: if ($retvalue eq '0') {
529: $retvalue = $b->{$sortby} <=> $a->{$sortby} ||
1.49 ! matthew 530: lc($a->{'title'}) <=> lc($b->{'title'}) ||
! 531: lc($a->{'part'}) <=> lc($b->{'part'});
1.41 matthew 532: }
1.42 matthew 533: $retvalue;
534: } @Statsarray;
535: }
1.43 matthew 536: }
1.49 ! matthew 537: $option .= 'no part' if (! $show_part);
! 538: my $num_output = 0;
! 539: #
! 540: # output the headers
! 541: $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
! 542: $r->print('<table border="0" cellpadding="3">'."\n");
! 543: my $Str = &statistics_table_header($option.' sortable');
! 544: $r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n");
! 545: #
! 546: foreach my $rowdata (@OutputOrder) {
! 547: $num_output++;
! 548: if ($num_output % 25 == 0) {
! 549: $r->print("</table>\n</td></tr></table>\n");
! 550: #
! 551: $r->print('<table border="0"><tr><td bgcolor="#777777">'."\n");
! 552: $r->print('<table border="0" cellpadding="3">'."\n");
! 553: my $Str = &statistics_table_header($option.' sortable');
! 554: $r->print('<tr bgcolor="#FFFFE6">'.$Str."</tr>\n");
! 555: $r->rflush();
! 556: }
! 557: $r->print('<tr>'.&statistics_html_table_data($rowdata,$option).
! 558: "</tr>\n");
1.26 stredwic 559: }
1.41 matthew 560: $r->print("</table>\n");
561: $r->print("</td></tr></table>\n");
1.26 stredwic 562: $r->rflush();
1.41 matthew 563: #
564: return;
1.42 matthew 565: }
566:
1.41 matthew 567: ###############################################
568: ###############################################
1.26 stredwic 569:
1.47 matthew 570: =pod
571:
572: =item &output_excel()
573:
574: Presents the statistical data in an Excel 95 compatable spreadsheet file.
575:
576: =cut
577:
1.41 matthew 578: ###############################################
579: ###############################################
1.44 matthew 580: sub output_excel {
581: my ($r) = @_;
582: my $filename = '/prtspool/'.
583: $ENV{'user.name'}.'_'.$ENV{'user.domain'}.'_'.
584: time.'_'.rand(1000000000).'.xls';
585: #
586: my $excel_workbook = undef;
587: my $excel_sheet = undef;
588: #
589: my $rows_output = 0;
590: my $cols_output = 0;
591: #
592: # Create sheet
593: $excel_workbook = Spreadsheet::WriteExcel->new('/home/httpd'.$filename);
594: #
595: # Check for errors
596: if (! defined($excel_workbook)) {
597: $r->log_error("Error creating excel spreadsheet $filename: $!");
598: $r->print("Problems creating new Excel file. ".
599: "This error has been logged. ".
600: "Please alert your LON-CAPA administrator");
601: return ;
602: }
603: #
604: # The excel spreadsheet stores temporary data in files, then put them
605: # together. If needed we should be able to disable this (memory only).
606: # The temporary directory must be specified before calling 'addworksheet'.
607: # File::Temp is used to determine the temporary directory.
608: $excel_workbook->set_tempdir($Apache::lonnet::tmpdir);
609: #
610: # Add a worksheet
611: my $sheetname = $ENV{'course.'.$ENV{'request.course.id'}.'.description'};
612: if (length($sheetname) > 31) {
613: $sheetname = substr($sheetname,0,31);
614: }
615: $excel_sheet = $excel_workbook->addworksheet($sheetname);
616: #
617: # Put the course description in the header
618: $excel_sheet->write($rows_output,$cols_output++,
619: $ENV{'course.'.$ENV{'request.course.id'}.'.description'});
620: $cols_output += 3;
621: #
622: # Put a description of the sections listed
623: my $sectionstring = '';
624: my @Sections = @Apache::lonstatistics::SelectedSections;
625: if (scalar(@Sections) > 1) {
626: if (scalar(@Sections) > 2) {
627: my $last = pop(@Sections);
628: $sectionstring = "Sections ".join(', ',@Sections).', and '.$last;
629: } else {
630: $sectionstring = "Sections ".join(' and ',@Sections);
631: }
632: } else {
633: if ($Sections[0] eq 'all') {
634: $sectionstring = "All sections";
635: } else {
636: $sectionstring = "Section ".$Sections[0];
637: }
638: }
639: $excel_sheet->write($rows_output,$cols_output++,$sectionstring);
640: $cols_output += scalar(@Sections);
641: #
642: # Put the date in there too
643: $excel_sheet->write($rows_output,$cols_output++,
644: 'Compiled on '.localtime(time));
645: #
646: $rows_output++;
647: $cols_output=0;
648: #
649: # Add the headers
1.49 ! matthew 650: foreach my $field (@Fields) {
! 651: next if ($field->{'name'} eq 'problem_num');
! 652: $excel_sheet->write($rows_output,$cols_output++,$field->{'title'});
1.44 matthew 653: }
654: $rows_output++;
655: #
656: # Write the data
1.49 ! matthew 657: my $problem_num=0;
1.44 matthew 658: foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
659: next if ($sequence->{'num_assess'}<1);
660: foreach my $resource (@{$sequence->{'contents'}}) {
661: next if ($resource->{'type'} ne 'assessment');
662: foreach my $part (@{$resource->{'parts'}}) {
663: $cols_output=0;
1.49 ! matthew 664: $problem_num++;
! 665: my $data = &get_statistics($sequence,$resource,$part,
! 666: $problem_num);
1.44 matthew 667: #
668: if (!defined($part) || $part eq '') {
669: $part = ' ';
670: }
1.49 ! matthew 671: foreach my $field (@Fields) {
! 672: next if ($field->{'name'} eq 'problem_num');
! 673: $excel_sheet->write($rows_output,$cols_output++,
! 674: $data->{$field->{'name'}});
1.44 matthew 675: }
676: $rows_output++;
677: }
678: }
679: }
680: #
681: # Write the excel file
682: $excel_workbook->close();
683: # Tell the user where to get their excel file
684: $r->print('<br />'.
685: '<a href="'.$filename.'">Your Excel spreadsheet.</a>'."\n");
686: $r->rflush();
687: return;
688: }
689:
1.45 matthew 690: ###############################################
691: ###############################################
1.44 matthew 692:
1.47 matthew 693: =pod
694:
695: =item &statistics_html_table_data()
696:
697: Help function used to format the rows for HTML table output.
698:
699: =cut
700:
1.45 matthew 701: ###############################################
702: ###############################################
1.41 matthew 703: sub statistics_html_table_data {
1.49 ! matthew 704: my ($data,$options) = @_;
1.41 matthew 705: my $row = '';
1.49 ! matthew 706: foreach my $field (@Fields) {
! 707: next if ($options =~ /no $field->{'name'}/);
! 708: $row .= '<td bgcolor="'.$field->{'color'}.'"';
! 709: if (exists($field->{'align'})) {
! 710: $row .= ' align="'.$field->{'align'}.'"';
! 711: }
! 712: $row .= '>';
! 713: if (exists($field->{'special'}) && $field->{'special'} eq 'link') {
! 714: $row .= '<a href="'.$data->{$field->{'name'}.'.link'}.
! 715: ' target="_blank">';
! 716: }
! 717: if (exists($field->{'format'})) {
! 718: $row .= sprintf($field->{'format'},$data->{$field->{'name'}});
! 719: } else {
! 720: $row .= $data->{$field->{'name'}};
! 721: }
! 722: if (exists($field->{'special'}) && $field->{'special'} eq 'link') {
! 723: $row.= '</a>';
! 724: }
! 725: $row .= '</td>';
1.26 stredwic 726: }
1.41 matthew 727: return $row;
728: }
1.26 stredwic 729:
1.49 ! matthew 730: sub statistics_table_header {
! 731: my ($options) = @_;
! 732: my $header_row;
! 733: foreach my $field (@Fields) {
! 734: next if ($options =~ /no $field->{'name'}/);
! 735: $header_row .= '<th>';
! 736: if ($options =~ /sortable/ &&
! 737: exists($field->{'sortable'}) && $field->{'sortable'} eq 'yes') {
! 738: $header_row .= '<a href="javascript:'.
! 739: 'document.Statistics.sortby.value='."'".$field->{'name'}."'".
! 740: ';document.Statistics.submit();">';
! 741: }
! 742: $header_row .= $field->{'title'};
! 743: if ($options =~ /sortable/) {
! 744: $header_row.= '</a>';
! 745: }
! 746: if ($options !~ /no plots/ &&
! 747: exists($field->{'graphable'}) &&
! 748: $field->{'graphable'} eq 'yes') {
! 749: $header_row.=' (';
! 750: $header_row .= '<a href="javascript:'.
! 751: "document.Statistics.plot.value='$field->{'name'}'".
! 752: ';document.Statistics.submit();">';
! 753: $header_row .= 'plot</a>)';
! 754: }
! 755: $header_row .= '</th>';
! 756: }
! 757: return $header_row;
! 758: }
! 759:
1.41 matthew 760: ###############################################
761: ###############################################
1.47 matthew 762:
763: =pod
764:
765: =item &plot_statistics()
766:
767: =cut
768:
769: ###############################################
770: ###############################################
1.45 matthew 771: sub plot_statistics {
772: my ($r,$datafield) = @_;
773: my @Data;
774: #
1.49 ! matthew 775: #
! 776: my $sortfield = undef;
! 777: my $title = undef;
! 778: foreach my $field (@Fields) {
! 779: if ($datafield eq $field->{'name'} &&
! 780: exists($field->{'graphable'}) && $field->{'graphable'} eq 'yes') {
! 781: $sortfield = $field->{'name'};
! 782: $title = $field->{'long_title'};
! 783: }
1.34 minaeibi 784: }
1.49 ! matthew 785: return if (! defined($sortfield) || $sortfield eq '');
! 786: &Apache::lonnet::logthis('data field = '.$datafield);
1.45 matthew 787: #
788: my $Max = 0;
1.49 ! matthew 789: my $problem_num = 0;
1.45 matthew 790: foreach my $sequence (&Apache::lonstatistics::Sequences_with_Assess()) {
791: next if ($sequence->{'num_assess'}<1);
792: foreach my $resource (@{$sequence->{'contents'}}) {
793: next if ($resource->{'type'} ne 'assessment');
794: foreach my $part (@{$resource->{'parts'}}) {
1.49 ! matthew 795: my $problem_number++;
! 796: my $data = &get_statistics($sequence,$resource,$part,
! 797: $problem_num);
! 798: my $value = $data->{$sortfield};
! 799: $Max = $value if ($Max < $value);
! 800: push (@Data,$value);
1.45 matthew 801: }
802: }
1.26 stredwic 803: }
1.45 matthew 804: #
805: # Print out plot request
1.49 ! matthew 806: my $yaxis = '';
! 807: if ($sortfield eq 'per_wrong') {
! 808: $yaxis = 'Percent';
1.45 matthew 809: }
810: #
811: # Determine appropriate value for $Max
1.49 ! matthew 812: if ($sortfield eq 'deg_of_diff') {
1.45 matthew 813: if ($Max > 0.5) {
814: $Max = 1;
815: } elsif ($Max > 0.2) {
816: $Max = 0.5;
817: } elsif ($Max > 0.1) {
818: $Max = 0.2;
819: }
1.49 ! matthew 820: } elsif ($sortfield eq 'per_wrong') {
1.45 matthew 821: if ($Max > 50) {
822: $Max = 100;
823: } elsif ($Max > 25) {
824: $Max = 50;
825: } elsif ($Max > 20) {
826: $Max = 25;
827: } elsif ($Max > 10) {
828: $Max = 20;
829: } elsif ($Max > 5) {
830: $Max = 10;
1.24 stredwic 831: } else {
1.45 matthew 832: $Max = 5;
1.24 stredwic 833: }
834: }
1.45 matthew 835:
836: $r->print("<p>".&DrawGraph(\@Data,$title,'Problem Number',$yaxis,
837: $Max)."</p>\n");
838: #
839: # Print out the data
840: $ENV{'form.sortby'} = 'Contents';
1.49 ! matthew 841: # &output_html_ungrouped($r);
1.24 stredwic 842: return;
843: }
1.1 stredwic 844:
1.45 matthew 845: ###############################################
846: ###############################################
847:
1.47 matthew 848: =pod
849:
850: =item &DrawGraph()
851:
852: =cut
853:
1.45 matthew 854: ###############################################
855: ###############################################
1.35 minaeibi 856: sub DrawGraph {
1.45 matthew 857: my ($values,$title,$xaxis,$yaxis,$Max)=@_;
1.49 ! matthew 858: $title = '' if (! defined($title));
1.45 matthew 859: $xaxis = '' if (! defined($xaxis));
860: $yaxis = '' if (! defined($yaxis));
861: #
1.35 minaeibi 862: my $sendValues = join(',', @$values);
863: my $sendCount = scalar(@$values);
1.49 ! matthew 864: $Max =1 if ($Max < 1);
! 865: if ( int($Max) < $Max ) {
! 866: $Max++;
! 867: $Max = int($Max);
1.1 stredwic 868: }
1.45 matthew 869: my @GData = ($title,$xaxis,$yaxis,$Max,$sendCount,$sendValues);
870: return '<IMG src="/cgi-bin/graph.png?'.
871: (join('&', @GData)).'" border="1" />';
1.48 matthew 872: }
873:
874: sub get_statistics {
1.49 ! matthew 875: my ($sequence,$resource,$part,$problem_num) = @_;
1.48 matthew 876: #
1.49 ! matthew 877: my $symb = $resource->{'symb'};
1.48 matthew 878: my $courseid = $ENV{'request.course.id'};
879: #
880: my $students = \@Apache::lonstatistics::Students;
881: if ($Apache::lonstatistics::SelectedSections[0] eq 'all') {
882: $students = undef;
883: }
1.49 ! matthew 884: my $data = &Apache::loncoursedata::get_problem_statistics
1.48 matthew 885: ($students,$symb,$part,$courseid);
1.49 ! matthew 886: $data->{'part'} = $part;
! 887: $data->{'problem_num'} = $problem_num;
! 888: $data->{'container'} = $sequence->{'title'};
! 889: $data->{'title'} = $resource->{'title'};
! 890: $data->{'title.link'} = $resource->{'src'};
! 891: #
! 892: return $data;
1.1 stredwic 893: }
1.12 minaeibi 894:
1.45 matthew 895: ###############################################
896: ###############################################
1.47 matthew 897:
898: =pod
899:
900: =item &ProblemStatisticsLegend()
901:
902: =cut
1.1 stredwic 903:
1.45 matthew 904: ###############################################
905: ###############################################
1.1 stredwic 906: sub ProblemStatisticsLegend {
907: my $Ptr = '';
908: $Ptr = '<table border="0">';
909: $Ptr .= '<tr><td>';
1.6 minaeibi 910: $Ptr .= '<b>#Stdnts</b></td>';
1.19 stredwic 911: $Ptr .= '<td>Total number of students attempted the problem.';
1.1 stredwic 912: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 913: $Ptr .= '<b>Tries</b></td>';
1.19 stredwic 914: $Ptr .= '<td>Total number of tries for solving the problem.';
1.1 stredwic 915: $Ptr .= '</td></tr><tr><td>';
1.49 ! matthew 916: $Ptr .= '<b>Max Tries</b></td>';
1.19 stredwic 917: $Ptr .= '<td>Largest number of tries for solving the problem by a student.';
1.1 stredwic 918: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 919: $Ptr .= '<b>Mean</b></td>';
1.19 stredwic 920: $Ptr .= '<td>Average number of tries. [ Tries / #Stdnts ]';
1.1 stredwic 921: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 922: $Ptr .= '<b>#YES</b></td>';
1.1 stredwic 923: $Ptr .= '<td>Number of students solved the problem correctly.';
924: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 925: $Ptr .= '<b>#yes</b></td>';
1.1 stredwic 926: $Ptr .= '<td>Number of students solved the problem by override.';
927: $Ptr .= '</td></tr><tr><td>';
1.19 stredwic 928: $Ptr .= '<b>%Wrong</b></td>';
929: $Ptr .= '<td>Percentage of students who tried to solve the problem ';
930: $Ptr .= 'but is still incorrect. [ 100*((#Stdnts-(#YES+#yes))/#Stdnts) ]';
1.1 stredwic 931: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 932: $Ptr .= '<b>DoDiff</b></td>';
1.1 stredwic 933: $Ptr .= '<td>Degree of Difficulty of the problem. ';
934: $Ptr .= '[ 1 - ((#YES+#yes) / Tries) ]';
935: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 936: $Ptr .= '<b>S.D.</b></td>';
1.1 stredwic 937: $Ptr .= '<td>Standard Deviation of the tries. ';
938: $Ptr .= '[ sqrt(sum((Xi - Mean)^2)) / (#Stdnts-1) ';
939: $Ptr .= 'where Xi denotes every student\'s tries ]';
940: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 941: $Ptr .= '<b>Skew.</b></td>';
1.1 stredwic 942: $Ptr .= '<td>Skewness of the students tries.';
943: $Ptr .= '[(sqrt( sum((Xi - Mean)^3) / #Stdnts)) / (S.D.^3)]';
944: $Ptr .= '</td></tr><tr><td>';
1.6 minaeibi 945: $Ptr .= '<b>Dis.F.</b></td>';
1.1 stredwic 946: $Ptr .= '<td>Discrimination Factor: A Standard for evaluating the ';
947: $Ptr .= 'problem according to a Criterion<br>';
1.31 stredwic 948: $Ptr .= '<b>[Criterion to group students into %27 Upper Students - ';
949: $Ptr .= 'and %27 Lower Students]</b><br>';
1.1 stredwic 950: $Ptr .= '<b>1st Criterion</b> for Sorting the Students: ';
951: $Ptr .= '<b>Sum of Partial Credit Awarded / Total Number of Tries</b><br>';
952: $Ptr .= '<b>2nd Criterion</b> for Sorting the Students: ';
953: $Ptr .= '<b>Total number of Correct Answers / Total Number of Tries</b>';
954: $Ptr .= '</td></tr>';
955: $Ptr .= '<tr><td><b>Disc.</b></td>';
956: $Ptr .= '<td>Number of Students had at least one discussion.';
957: $Ptr .= '</td></tr></table>';
958: return $Ptr;
959: }
1.24 stredwic 960:
961: #---- END Problem Statistics Web Page ----------------------------------------
1.4 minaeibi 962:
1.1 stredwic 963: 1;
964: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>