Annotation of loncom/interface/statistics/lonproblemanalysis.pm, revision 1.41
1.1 stredwic 1: # The LearningOnline Network with CAPA
2: #
1.33 matthew 3:
1.41 ! matthew 4: # $Id: lonproblemanalysis.pm,v 1.40 2003/10/16 13:23:13 matthew 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:
1.11 minaeibi 29: package Apache::lonproblemanalysis;
1.1 stredwic 30:
31: use strict;
32: use Apache::lonnet();
1.25 matthew 33: use Apache::loncommon();
1.7 stredwic 34: use Apache::lonhtmlcommon();
1.23 matthew 35: use Apache::loncoursedata();
36: use Apache::lonstatistics;
37: use Apache::lonlocal;
1.37 matthew 38: use HTML::Entities();
1.2 stredwic 39:
1.40 matthew 40: my $plotcolors = ['#33ff00',
41: '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
42: '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
43: ];
1.39 matthew 44:
1.1 stredwic 45: sub BuildProblemAnalysisPage {
1.23 matthew 46: my ($r,$c)=@_;
1.24 matthew 47: $r->print('<h2>'.&mt('Option Response Problem Analysis').'</h2>');
1.25 matthew 48: $r->print(&CreateInterface());
1.28 matthew 49: #
50: my @Students = @Apache::lonstatistics::Students;
51: #
1.41 ! matthew 52: if (exists($ENV{'form.ClearCache'}) ||
! 53: exists($ENV{'form.updatecaches'}) ||
1.33 matthew 54: (exists($ENV{'form.firstanalysis'}) &&
55: $ENV{'form.firstanalysis'} ne 'no')) {
56: &Apache::lonstatistics::Gather_Full_Student_Data($r);
57: }
58: if (! exists($ENV{'form.firstanalysis'})) {
59: $r->print('<input type="hidden" name="firstanalysis" value="yes" />');
60: } else {
61: $r->print('<input type="hidden" name="firstanalysis" value="no" />');
62: }
1.39 matthew 63: $r->rflush();
1.33 matthew 64: if (exists($ENV{'form.problemchoice'}) &&
65: ! exists($ENV{'form.SelectAnother'})) {
1.31 matthew 66: $r->print('<input type="submit" name="ProblemAnalysis" value="'.
67: &mt('Analyze Problem Again').'" />');
68: $r->print(' 'x5);
69: $r->print('<input type="submit" name="ClearCache" value="'.
70: &mt('Clear Caches').'" />');
71: $r->print(' 'x5);
1.33 matthew 72: $r->print('<input type="submit" name="updatecaches" value="'.
73: &mt('Update Student Data').'" />');
74: $r->print(' 'x5);
1.31 matthew 75: $r->print('<input type="hidden" name="problemchoice" value="'.
76: $ENV{'form.problemchoice'}.'" />');
77: $r->print('<input type="submit" name="SelectAnother" value="'.
78: &mt('Choose a different resource').'" />');
79: $r->print(' 'x5);
80: #
1.25 matthew 81: $r->print('<hr />');
1.23 matthew 82: #
1.25 matthew 83: my ($symb,$part,$resid) = &get_problem_symb(
1.23 matthew 84: &Apache::lonnet::unescape($ENV{'form.problemchoice'})
85: );
1.28 matthew 86: #
1.23 matthew 87: my $resource = &get_resource_from_symb($symb);
88: if (defined($resource)) {
1.41 ! matthew 89: $r->print('<h3>'.$resource->{'src'}.'</h3>');
1.25 matthew 90: my %Data = &get_problem_data($resource->{'src'});
91: my $ORdata = $Data{$part.'.'.$resid};
92: ##
1.26 matthew 93: ## Render the problem
1.25 matthew 94: my $base;
95: ($base,undef) = ($resource->{'src'} =~ m|(.*/)[^/]*$|);
96: $base = "http://".$ENV{'SERVER_NAME'}.$base;
1.26 matthew 97: my $rendered_problem =
98: &Apache::lonnet::ssi_body($resource->{'src'});
1.30 matthew 99: $rendered_problem =~ s/<\s*form\s*/<nop /g;
100: $rendered_problem =~ s|(<\s*/form\s*>)|<\/nop>|g;
1.26 matthew 101: $r->print('<table bgcolor="ffffff"><tr><td>'.
1.25 matthew 102: '<base href="'.$base.'" />'.
1.26 matthew 103: $rendered_problem.
104: '</td></tr></table>');
1.25 matthew 105: ##
106: ## Analyze the problem
1.26 matthew 107: my $PerformanceData =
108: &Apache::loncoursedata::get_optionresponse_data
1.28 matthew 109: (\@Students,$symb,$resid);
1.26 matthew 110: if (defined($PerformanceData) &&
111: ref($PerformanceData) eq 'ARRAY') {
1.36 matthew 112: if ($ENV{'form.AnalyzeOver'} eq 'Tries') {
1.33 matthew 113: my $analysis_html = &tries_analysis($PerformanceData,
114: $ORdata);
115: $r->print($analysis_html);
1.36 matthew 116: } elsif ($ENV{'form.AnalyzeOver'} eq 'Time') {
1.33 matthew 117: my $analysis_html = &time_analysis($PerformanceData,
1.28 matthew 118: $ORdata);
1.26 matthew 119: $r->print($analysis_html);
1.28 matthew 120: } else {
121: $r->print('<h2>'.
122: &mt('The analysis you have selected is '.
123: 'not supported at this time').
124: '</h2>');
125: }
1.26 matthew 126: } else {
127: $r->print('<h2>'.
128: &mt('There is no student data for this problem.').
129: '</h2>');
130: }
1.23 matthew 131: } else {
132: $r->print('resource is undefined');
1.7 stredwic 133: }
1.23 matthew 134: $r->print('<hr />');
1.25 matthew 135: } else {
1.31 matthew 136: $r->print('<input type="submit" name="ProblemAnalysis" value="'.
137: &mt('Analyze Problem').'" />');
138: $r->print(' 'x5);
1.27 matthew 139: $r->print('<h3>'.&mt('Please select a problem to analyze').'</h3>');
1.31 matthew 140: $r->print(&OptionResponseProblemSelector());
1.1 stredwic 141: }
142: }
143:
1.25 matthew 144:
1.33 matthew 145: #########################################################
146: #########################################################
147: ##
148: ## Misc interface routines use by analysis code
149: ##
150: #########################################################
151: #########################################################
152: sub build_foil_index {
153: my ($ORdata) = @_;
1.41 ! matthew 154: return if (! exists($ORdata->{'Foils'}));
1.36 matthew 155: my %Foildata = %{$ORdata->{'Foils'}};
156: my @Foils = sort(keys(%Foildata));
157: my %Concepts;
1.25 matthew 158: foreach my $foilid (@Foils) {
1.36 matthew 159: push(@{$Concepts{$Foildata{$foilid}->{'Concept'}}},
160: $foilid);
161: }
162: undef(@Foils);
163: # Having gathered the concept information in a hash, we now translate it
164: # into an array because we need to be consistent about order.
165: # Also put the foils in order, too.
166: my $sortfunction = sub {
167: my %Numbers = (one => 1,
168: two => 2,
169: three => 3,
170: four => 4,
171: five => 5,
172: six => 6,
173: seven => 7,
174: eight => 8,
175: nine => 9,
176: ten => 10,);
177: my $a1 = $a;
178: my $b1 = $b;
1.39 matthew 179: if (exists($Numbers{lc($a)})) {
180: $a1 = $Numbers{lc($a)};
1.36 matthew 181: }
1.39 matthew 182: if (exists($Numbers{lc($b)})) {
183: $b1 = $Numbers{lc($b)};
1.36 matthew 184: }
185: $a1 cmp $b1;
186: };
187: my @Concepts;
188: foreach my $concept (sort $sortfunction (keys(%Concepts))) {
1.39 matthew 189: push(@Concepts,{ name => $concept,
1.36 matthew 190: foils => [@{$Concepts{$concept}}]});
191: push(@Foils,(@{$Concepts{$concept}}));
1.25 matthew 192: }
1.31 matthew 193: #
194: # Build up the table of row labels.
195: my $table = '<table border="1" >'."\n";
1.39 matthew 196: if (@Concepts > 1) {
197: $table .= '<tr>'.
198: '<th>'.&mt('Concept Number').'</th>'.
199: '<th>'.&mt('Concept').'</th>'.
200: '<th>'.&mt('Foil Number').'</th>'.
201: '<th>'.&mt('Foil Name').'</th>'.
202: '<th>'.&mt('Foil Text').'</th>'.
203: '<th>'.&mt('Correct Value').'</th>'.
204: "</tr>\n";
205: } else {
206: $table .= '<tr>'.
207: '<th>'.&mt('Foil Number').'</th>'.
208: '<th>'.&mt('Foil Name').'</th>'.
209: '<th>'.&mt('Foil Text').'</th>'.
210: '<th>'.&mt('Correct Value').'</th>'.
211: "</tr>\n";
212: }
1.36 matthew 213: my $conceptindex = 1;
214: my $foilindex = 1;
215: foreach my $concept (@Concepts) {
216: my @FoilsInConcept = @{$concept->{'foils'}};
217: my $firstfoil = shift(@FoilsInConcept);
1.39 matthew 218: if (@Concepts > 1) {
219: $table .= '<tr>'.
220: '<td>'.$conceptindex.'</td>'.
221: '<td>'.$concept->{'name'}.'</td>'.
222: '<td>'.$foilindex++.'</td>'.
223: '<td>'.$Foildata{$firstfoil}->{'name'}.'</td>'.
224: '<td>'.$Foildata{$firstfoil}->{'text'}.'</td>'.
225: '<td>'.$Foildata{$firstfoil}->{'value'}.'</td>'.
226: "</tr>\n";
227: } else {
1.36 matthew 228: $table .= '<tr>'.
1.39 matthew 229: '<td>'.$foilindex++.'</td>'.
230: '<td>'.$Foildata{$firstfoil}->{'name'}.'</td>'.
231: '<td>'.$Foildata{$firstfoil}->{'text'}.'</td>'.
232: '<td>'.$Foildata{$firstfoil}->{'value'}.'</td>'.
1.36 matthew 233: "</tr>\n";
1.39 matthew 234: }
235: foreach my $foilid (@FoilsInConcept) {
236: if (@Concepts > 1) {
237: $table .= '<tr>'.
238: '<td></td>'.
239: '<td></td>'.
240: '<td>'.$foilindex.'</td>'.
241: '<td>'.$Foildata{$foilid}->{'name'}.'</td>'.
242: '<td>'.$Foildata{$foilid}->{'text'}.'</td>'.
243: '<td>'.$Foildata{$foilid}->{'value'}.'</td>'.
244: "</tr>\n";
245: } else {
246: $table .= '<tr>'.
247: '<td>'.$foilindex.'</td>'.
248: '<td>'.$Foildata{$foilid}->{'name'}.'</td>'.
249: '<td>'.$Foildata{$foilid}->{'text'}.'</td>'.
250: '<td>'.$Foildata{$foilid}->{'value'}.'</td>'.
251: "</tr>\n";
252: }
1.36 matthew 253: } continue {
254: $foilindex++;
255: }
1.31 matthew 256: } continue {
1.36 matthew 257: $conceptindex++;
1.25 matthew 258: }
1.31 matthew 259: $table .= "</table>\n";
1.39 matthew 260: #
261: # Build option index with color stuff
1.36 matthew 262: return ($table,\@Foils,\@Concepts);
1.33 matthew 263: }
264:
1.39 matthew 265: sub build_option_index {
266: my ($ORdata)= @_;
267: my $table = "<table>\n";
268: my $optionindex = 0;
269: my @Rows;
1.41 ! matthew 270: foreach my $option (&mt('correct option chosen'),@{$ORdata->{'Options'}}) {
1.39 matthew 271: push (@Rows,
272: '<tr>'.
273: '<td bgcolor="'.$plotcolors->[$optionindex++].'">'.
274: (' 'x4).'</td>'.
275: '<td>'.$option.'</td>'.
276: "</tr>\n");
277: }
278: $table .= join('',reverse(@Rows));
279: $table .= "</table>\n";
280: }
281:
1.33 matthew 282: #########################################################
283: #########################################################
284: ##
285: ## Tries Analysis
286: ##
287: #########################################################
288: #########################################################
289: sub tries_analysis {
290: my ($PerformanceData,$ORdata) = @_;
291: my $mintries = 1;
292: my $maxtries = $ENV{'form.NumPlots'};
1.39 matthew 293: my ($table,$Foils,$Concepts) = &build_foil_index($ORdata);
294: if ((@$Concepts < 2) && ($ENV{'form.AnalyzeAs'} ne 'Foils')) {
295: $table = '<h3>'.
296: &mt('Not enough data for concept analysis. '.
297: 'Performing Foil Analysis').
298: '</h3>'.$table;
299: $ENV{'form.AnalyzeAs'} = 'Foils';
300: }
1.33 matthew 301: my %ResponseData = &analyze_option_data_by_tries($PerformanceData,
1.36 matthew 302: $mintries,$maxtries);
1.31 matthew 303: #
304: # Compute the data neccessary to make the plots
1.39 matthew 305: my @PlotData; # Array which holds the data for each plot
306: # @{$PlotData[$try]->{'datasetname'}} holds the data for
307: # try $try with respect to 'datasetname'. The array is
308: # filled either with per-foil or per-concept data.
309: my ($extrakey,$xlabel,$ylabel);
1.36 matthew 310: if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
1.39 matthew 311: $extrakey = &build_option_index($ORdata);
1.36 matthew 312: $xlabel = 'Foil Number';
1.41 ! matthew 313: $ylabel = 'Percent Choosing Option';
1.36 matthew 314: foreach my $foilid (@$Foils) {
315: for (my $i=$mintries;$i<=$maxtries;$i++) {
1.39 matthew 316: foreach my $option ('_correct',@{$ORdata->{'Options'}}) {
317: push(@{$PlotData[$i]->{'_total'}},
318: $ResponseData{$foilid}->[$i]->{'_total'});
319: if ($ResponseData{$foilid}->[$i]->{'_total'} == 0) {
320: push (@{$PlotData[$i]->{$option}},0);
321: } else {
322: push (@{$PlotData[$i]->{$option}},
323: 100 * $ResponseData{$foilid}->[$i]->{$option} /
324: $ResponseData{$foilid}->[$i]->{'_total'});
325: }
1.36 matthew 326: }
327: }
328: }
329: } else {
330: # Concept analysis
1.39 matthew 331: #
332: # Note: we do not bother with characterizing the students incorrect
333: # answers at the concept level because an incorrect answer for one foil
334: # may be a correct answer for another foil.
335: $extrakey = '';
1.36 matthew 336: $xlabel = 'Concept Number';
1.39 matthew 337: $ylabel = 'Percent Correct';
338: my %ConceptData;
339: foreach my $concept (@{$Concepts}) {
1.36 matthew 340: for (my $i=$mintries;$i<=$maxtries;$i++) {
341: #
342: # Gather the per-attempt data
1.39 matthew 343: my $cdata = $ConceptData{$concept}->[$i];
344: foreach my $foilid (@{$concept->{'foils'}}) {
345: $cdata->{'_correct'} +=
346: $ResponseData{$foilid}->[$i]->{'_correct'};
347: $cdata->{'_total'} +=
348: $ResponseData{$foilid}->[$i]->{'_total'};
1.36 matthew 349: }
1.39 matthew 350: push (@{$PlotData[$i]->{'_total'}},$cdata->{'_total'});
351: if ($cdata->{'_total'} == 0) {
352: push (@{$PlotData[$i]->{'_correct'}},0);
1.36 matthew 353: } else {
1.39 matthew 354: push (@{$PlotData[$i]->{'_correct'}},
355: 100*$cdata->{'_correct'}/$cdata->{'_total'});
1.36 matthew 356: }
357: }
1.25 matthew 358: }
1.39 matthew 359: } # End of work to fill @PlotData
1.31 matthew 360: #
361: # Build a table for the plots
362: $table .= "<table>\n";
363: my @Plots;
1.25 matthew 364: for (my $i=$mintries;$i<=$maxtries;$i++) {
1.39 matthew 365: my $minstu = $PlotData[$i]->{'_total'}->[0];
366: my $maxstu = $PlotData[$i]->{'_total'}->[0];
367: foreach my $count (@{$PlotData[$i]->{'_total'}}) {
1.36 matthew 368: if ($minstu > $count) {
369: $minstu = $count;
1.27 matthew 370: }
1.36 matthew 371: if ($maxstu < $count) {
372: $maxstu = $count;
1.27 matthew 373: }
374: }
1.39 matthew 375: $maxstu = 0 if (! defined($maxstu));
376: $minstu = 0 if (! defined($minstu));
1.35 matthew 377: my $title;
1.27 matthew 378: if ($maxstu == $minstu) {
1.35 matthew 379: $title = 'Attempt '.$i.', '.$maxstu.' students';
1.27 matthew 380: } else {
1.35 matthew 381: $title = 'Attempt '.$i.', '.$minstu.'-'.$maxstu.' students';
1.27 matthew 382: }
1.39 matthew 383: my @Datasets;
384: foreach my $option ('_correct',@{$ORdata->{'Options'}}) {
385: next if (! exists($PlotData[$i]->{$option}));
386: push(@Datasets,$PlotData[$i]->{$option});
387: }
1.35 matthew 388: my $graphlink = &Apache::loncommon::DrawGraph($title,
1.36 matthew 389: $xlabel,
1.39 matthew 390: $ylabel,
1.35 matthew 391: 100,
1.39 matthew 392: $plotcolors,
393: @Datasets);
1.31 matthew 394: push(@Plots,$graphlink);
1.25 matthew 395: }
1.31 matthew 396: #
397: # Should this be something the user can set? Too many dialogs!
398: while (my $plotlink = shift(@Plots)) {
1.39 matthew 399: $table .= '<tr><td>'.$plotlink.'</td><td>'.$extrakey."</td></tr>\n";
1.25 matthew 400: }
1.31 matthew 401: $table .= "</table>\n";
1.25 matthew 402: return ($table);
403: }
404:
405: sub analyze_option_data_by_tries {
1.26 matthew 406: my ($PerformanceData,$mintries,$maxtries) = @_;
1.25 matthew 407: my %Trydata;
408: $mintries = 1 if (! defined($mintries) || $mintries < 1);
409: $maxtries = $mintries if (! defined($maxtries) || $maxtries < $mintries);
1.26 matthew 410: foreach my $row (@$PerformanceData) {
411: next if (! defined($row));
1.25 matthew 412: my ($grading,$submission,$time,$tries) = @$row;
1.39 matthew 413: next if ($grading eq 'MISSING_ANSWER');
1.25 matthew 414: my @Foilgrades = split('&',$grading);
415: my @Foilsubs = split('&',$submission);
416: for (my $numtries = 1; $numtries <= $maxtries; $numtries++) {
417: if ($tries == $numtries) {
1.39 matthew 418: for (my $i=0;$i<=$#Foilgrades;$i++) {
419: my ($foilid,$correct) = split('=',$Foilgrades[$i]);
420: my (undef,$submission) = split('=',$Foilsubs[$i]);
1.41 ! matthew 421: $submission = &Apache::lonnet::unescape($submission);
1.25 matthew 422: if ($correct) {
1.39 matthew 423: $Trydata{$foilid}->[$numtries]->{'_correct'}++;
1.25 matthew 424: } else {
1.39 matthew 425: $Trydata{$foilid}->[$numtries]->{$submission}++;
1.25 matthew 426: }
1.39 matthew 427: $Trydata{$foilid}->[$numtries]->{'_total'}++;
1.25 matthew 428: }
429: }
430: }
431: }
432: return %Trydata;
433: }
434:
1.33 matthew 435: #########################################################
436: #########################################################
437: ##
438: ## Time Analysis
439: ##
440: #########################################################
441: #########################################################
442: sub time_analysis {
443: my ($PerformanceData,$ORdata) = @_;
444: my $num_plots = $ENV{'form.NumPlots'};
1.36 matthew 445: my ($table,$Foils,$Concepts) = &build_foil_index($ORdata);
1.33 matthew 446: my $num_data = scalar(@$PerformanceData)-1;
447: my $percent = sprintf('%2f',100/$num_plots);
1.39 matthew 448: my $extratable = '';
449: if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
450: $extratable = &build_option_index($ORdata);
451: }
1.37 matthew 452: $table .= "<table>\n";
1.33 matthew 453: for (my $i=0;$i<$num_plots;$i++) {
1.34 matthew 454: my $starttime = &Apache::lonhtmlcommon::get_date_from_form
455: ('startdate_'.$i);
456: my $endtime = &Apache::lonhtmlcommon::get_date_from_form
457: ('enddate_'.$i);
458: my ($begin_index,$end_index,$plottitle,$plothtml,$data);
459: if (! defined($starttime) || ! defined($endtime)) {
460: $begin_index = $i*int($num_data/$num_plots);
461: $end_index = ($i+1)*int($num_data/$num_plots);
462: my $lownum = sprintf('%2.1f',$i*$percent);
463: $lownum =~ s/(\.0)$//;
464: my $highnum = sprintf('%2.1f',($i+1)*$percent);
465: $highnum =~ s/(\.0)$//;
466: $plottitle = $lownum.'% to '.$highnum.'% of submissions';
467: } else {
468: my $j;
469: while (++$j < scalar(@$PerformanceData)) {
470: last if ($PerformanceData->[$j]->[2] > $starttime);
471: }
472: $begin_index = $j;
473: while (++$j < scalar(@$PerformanceData)) {
474: last if ($PerformanceData->[$j]->[2] > $endtime);
475: }
476: $end_index = $j;
1.37 matthew 477: $plottitle = $ENV{'form.plottitle_'.$i};
1.34 matthew 478: }
479: ($plothtml,$starttime,$endtime,$data) =
1.33 matthew 480: &analyze_option_data_by_time($PerformanceData,
481: $begin_index,$end_index,
1.39 matthew 482: $plottitle,$Foils,
483: $Concepts,$ORdata);
1.34 matthew 484: my $startdateform = &Apache::lonhtmlcommon::date_setter
485: ('Statistics','startdate_'.$i,$starttime);
486: my $enddateform = &Apache::lonhtmlcommon::date_setter
487: ('Statistics','enddate_'.$i,$endtime);
1.37 matthew 488: $table.="<tr><td>".$plothtml.'</td><td align="left" valign="top">'.
489: "<b>Start Time</b>: ".$startdateform."<br />".
490: "<b>End Time</b> : "." ".$enddateform."<br />".
1.38 matthew 491: '<b>Plot Title</b> :'.(" "x3).
1.37 matthew 492: '<input type="text" size="30" name="plottitle_'.$i.'" value="'.
1.39 matthew 493: &HTML::Entities::encode($plottitle).'" /><br />'.$extratable.
1.37 matthew 494: "</td></tr>\n";
1.33 matthew 495: }
1.37 matthew 496: $table .="</table>\n";
1.33 matthew 497: return $table;
498: }
499:
500: sub analyze_option_data_by_time {
1.39 matthew 501: my ($PerformanceData,$begin_index,
502: $end_index,$description,$Foils,$Concepts,$ORdata) = @_;
1.33 matthew 503: my %TimeData;
504: #
505: # Get the start and end times for this segment of the plot
506: my $starttime = $PerformanceData->[$begin_index]->[2];
507: my $endtime = $PerformanceData->[$end_index ]->[2];
508: #
509: # Compute the number getting the foils correct or incorrects
510: for (my $i=$begin_index;$i<=$end_index;$i++) {
511: my $row = $PerformanceData->[$i];
512: next if (! defined($row));
513: my ($grading,$submission,$time,$tries) = @$row;
1.39 matthew 514: next if ($grading eq 'MISSING_ANSWER');
1.33 matthew 515: my @Foilgrades = split('&',$grading);
516: my @Foilsubs = split('&',$submission);
1.39 matthew 517: for (my $j=0;$j<=$#Foilgrades;$j++) {
518: my ($foilid,$correct) = split('=',$Foilgrades[$j]);
519: my (undef,$submission) = split('=',$Foilsubs[$j]);
1.33 matthew 520: if ($correct) {
1.39 matthew 521: $TimeData{$foilid}->{'_correct'}++;
1.33 matthew 522: } else {
1.39 matthew 523: $submission = &HTML::Entities::decode($submission);
524: $submission =~ s/\%20/ /g;
525: $TimeData{$foilid}->{$submission}++;
1.33 matthew 526: }
1.39 matthew 527: $TimeData{$foilid}->{'_total'}++;
1.33 matthew 528: }
529: }
530: #
531: # Compute the total and percent correct
1.39 matthew 532: my @Plotdata;
533: my ($xlabel,$ylabel);
534: if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
535: $xlabel = 'Foil Number';
536: $ylabel = 'Option Chosen';
537: foreach my $foil (@$Foils) {
538: my $total = $TimeData{$foil}->{'_total'};
539: my $optionidx = 0;
540: foreach my $option ('_correct',@{$ORdata->{'Options'}}) {
541: if ($total > 0) {
542: push(@{$Plotdata[$optionidx]},
543: 100 * $TimeData{$foil}->{$option} / $total);
544: } else {
545: push(@{$Plotdata[$optionidx]},0);
546: }
547: } continue {
548: $optionidx++;
1.37 matthew 549: }
1.39 matthew 550: }
551: } else {
552: $xlabel = 'Concept Number';
553: $ylabel = 'Percent Correct';
554: foreach my $concept (@$Concepts) {
555: my $correct;
556: my $total;
557: foreach my $foil (@{$concept->{'foils'}}) {
558: $correct+=$TimeData{$foil}->{'_correct'};
559: $total +=$TimeData{$foil}->{'_total'};
1.37 matthew 560: }
1.39 matthew 561: if ($total > 0) {
562: push(@{$Plotdata[0]},100 * $correct / $total);
1.37 matthew 563: } else {
1.39 matthew 564: push(@{$Plotdata[0]},0);
1.37 matthew 565: }
1.33 matthew 566: }
567: }
568: #
569: # Create the plot
570: my $graphlink = &Apache::loncommon::DrawGraph
571: ($description,#'Time Interval Analysis',
1.37 matthew 572: $xlabel,
1.39 matthew 573: $ylabel,
1.33 matthew 574: 100,
1.39 matthew 575: $plotcolors,
576: @Plotdata);
1.33 matthew 577: #
578: return ($graphlink,$starttime,$endtime,\%TimeData);
1.1 stredwic 579: }
580:
1.33 matthew 581: #########################################################
582: #########################################################
583: ##
584: ## Interface
585: ##
586: #########################################################
587: #########################################################
1.23 matthew 588: sub CreateInterface {
1.28 matthew 589: ##
590: ## Environment variable initialization
1.36 matthew 591: if (! exists$ENV{'form.AnalyzeOver'}) {
592: $ENV{'form.AnalyzeOver'} = 'Tries';
1.28 matthew 593: }
594: ##
595: ## Build the menu
1.7 stredwic 596: my $Str = '';
1.23 matthew 597: $Str .= '<table cellspacing="5">'."\n";
598: $Str .= '<tr>';
599: $Str .= '<td align="center"><b>'.&mt('Sections').'</b></td>';
600: $Str .= '<td align="center"><b>'.&mt('Enrollment Status').'</b></td>';
1.31 matthew 601: # $Str .= '<td align="center"><b>'.&mt('Sequences and Folders').'</b></td>';
602: $Str .= '<td align="center"> </td>';
1.23 matthew 603: $Str .= '</tr>'."\n";
1.31 matthew 604: ##
605: ##
1.23 matthew 606: $Str .= '<tr><td align="center">'."\n";
607: $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
1.28 matthew 608: $Str .= '</td>';
609: #
610: $Str .= '<td align="center">';
1.23 matthew 611: $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5);
1.28 matthew 612: $Str .= '</td>';
613: #
1.31 matthew 614: # $Str .= '<td align="center">';
1.23 matthew 615: my $only_seq_with_assessments = sub {
616: my $s=shift;
617: if ($s->{'num_assess'} < 1) {
618: return 0;
619: } else {
620: return 1;
621: }
622: };
1.31 matthew 623: &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
1.23 matthew 624: $only_seq_with_assessments);
1.36 matthew 625: ##
626: ##
1.28 matthew 627: $Str .= '<td>';
1.36 matthew 628: { # These braces are here to organize the code, not scope it.
629: {
630: $Str .= '<nobr>'.&mt('Analyze Over ');
631: $Str .='<select name="AnalyzeOver" >';
632: $Str .= '<option value="Tries" ';
633: if (! exists($ENV{'form.AnalyzeOver'}) ||
634: $ENV{'form.AnalyzeOver'} eq 'Tries'){
635: # Default to Tries
636: $Str .= ' selected ';
637: }
638: $Str .= '>'.&mt('Tries').'</option>';
639: $Str .= '<option value="Time" ';
640: $Str .= ' selected ' if ($ENV{'form.AnalyzeOver'} eq 'Time');
641: $Str .= '>'.&mt('Time').'</option>';
642: $Str .= '</select></nobr><br />';
643: }
644: {
645: $Str .= '<nobr>'.&mt('Analyze as ');
646: $Str .='<select name="AnalyzeAs" >';
647: $Str .= '<option value="Concepts" ';
648: if (! exists($ENV{'form.AnalyzeAs'}) ||
649: $ENV{'form.AnalyzeAs'} eq 'Concepts'){
650: # Default to Concepts
651: $Str .= ' selected ';
652: }
653: $Str .= '>'.&mt('Concepts').'</option>';
654: $Str .= '<option value="Foils" ';
655: $Str .= ' selected ' if ($ENV{'form.AnalyzeAs'} eq 'Foils');
656: $Str .= '>'.&mt('Foils').'</option>';
657: $Str .= '</select></nobr><br />';
658: }
659: {
660: $Str .= '<br /><nobr>'.&mt('Number of Plots:');
661: $Str .= '<select name="NumPlots">';
662: if (! exists($ENV{'form.NumPlots'})
663: || $ENV{'form.NumPlots'} < 1
664: || $ENV{'form.NumPlots'} > 20) {
665: $ENV{'form.NumPlots'} = 5;
666: }
667: foreach my $i (1,2,3,4,5,6,7,8,10,15,20) {
668: $Str .= '<option value="'.$i.'" ';
669: if ($ENV{'form.NumPlots'} == $i) { $Str.=' selected '; }
670: $Str .= '>'.$i.'</option>';
671: }
672: $Str .= '</select></nobr>';
673: }
1.28 matthew 674: }
675: $Str .= '</td>';
1.36 matthew 676: ##
677: ##
1.28 matthew 678: $Str .= '</tr>'."\n";
1.23 matthew 679: $Str .= '</table>'."\n";
680: return ($Str);
681: }
682:
683: sub OptionResponseProblemSelector {
684: my $Str;
685: $Str = "\n<table>\n";
686: foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
687: next if ($seq->{'num_assess'}<1);
688: my $seq_str = '';
689: foreach my $res (@{$seq->{'contents'}}) {
1.26 matthew 690: next if ($res->{'type'} ne 'assessment');
1.23 matthew 691: foreach my $part (@{$res->{'parts'}}) {
692: my $partdata = $res->{'partdata'}->{$part};
693: if (! exists($partdata->{'option'}) ||
694: $partdata->{'option'} == 0) {
695: next;
696: }
697: for (my $i=0;$i<scalar(@{$partdata->{'ResponseTypes'}});$i++){
698: my $respid = $partdata->{'ResponseIds'}->[$i];
699: my $resptype = $partdata->{'ResponseTypes'}->[$i];
700: if ($resptype eq 'option') {
1.25 matthew 701: my $value = &Apache::lonnet::escape($res->{'symb'}.':'.$part.':'.$respid);
1.23 matthew 702: my $checked = '';
703: if ($ENV{'form.problemchoice'} eq $value) {
704: $checked = 'checked ';
705: }
706: $seq_str .= '<tr><td>'.
707: '<input type="radio" name="problemchoice" value="'.$value.'" '.$checked.'/>'.
708: '</td><td>'.
709: '<a href="'.$res->{'src'}.'">'.$res->{'title'}.'</a> ';
710: if ($partdata->{'option'} > 1) {
711: $seq_str .= &mt('response').' '.$respid;
712: }
713: $seq_str .= "</td></tr>\n";
1.11 minaeibi 714: }
715: }
716: }
717: }
1.23 matthew 718: if ($seq_str ne '') {
719: $Str .= '<tr><td> </td><td><b>'.$seq->{'title'}.'</b></td>'.
720: "</tr>\n".$seq_str;
721: }
1.11 minaeibi 722: }
1.23 matthew 723: $Str .= "</table>\n";
724: return $Str;
1.33 matthew 725: }
726:
727: #########################################################
728: #########################################################
729: ##
730: ## Misc functions
731: ##
732: #########################################################
733: #########################################################
734: sub get_problem_symb {
735: my $problemstring = shift();
736: my ($symb,$partid,$resid) = ($problemstring=~ /^(.*):([^:]*):([^:]*)$/);
737: return ($symb,$partid,$resid);
1.11 minaeibi 738: }
739:
1.23 matthew 740: sub get_resource_from_symb {
741: my ($symb) = @_;
742: foreach my $seq (&Apache::lonstatistics::Sequences_with_Assess()) {
743: foreach my $res (@{$seq->{'contents'}}) {
744: if ($res->{'symb'} eq $symb) {
745: return $res;
1.2 stredwic 746: }
1.1 stredwic 747: }
748: }
1.23 matthew 749: return undef;
1.1 stredwic 750: }
751:
1.39 matthew 752: ##
753: ## get problem data and put it into a useful data structure.
754: ## note: we must force each foil and option to not begin or end with
755: ## spaces as they are stored without such data.
756: ##
1.25 matthew 757: sub get_problem_data {
758: my ($url) = @_;
759: my $Answ=&Apache::lonnet::ssi($url,('grade_target' => 'analyze'));
1.23 matthew 760: (my $garbage,$Answ)=split(/_HASH_REF__/,$Answ,2);
1.25 matthew 761: my %Answer;
1.23 matthew 762: %Answer=&Apache::lonnet::str2hash($Answ);
1.25 matthew 763: my %Partdata;
764: foreach my $part (@{$Answer{'parts'}}) {
765: while (my($key,$value) = each(%Answer)) {
766: next if ($key !~ /^$part/);
767: $key =~ s/^$part\.//;
768: if (ref($value) eq 'ARRAY') {
769: if ($key eq 'options') {
770: $Partdata{$part}->{'Options'}=$value;
771: } elsif ($key eq 'concepts') {
772: $Partdata{$part}->{'Concepts'}=$value;
773: } elsif ($key =~ /^concept\.(.*)$/) {
774: my $concept = $1;
775: foreach my $foil (@$value) {
1.36 matthew 776: $Partdata{$part}->{'Foils'}->{$foil}->{'Concept'}=
777: $concept;
1.25 matthew 778: }
779: }
780: } else {
781: if ($key=~ /^foil\.text\.(.*)$/) {
782: my $foil = $1;
1.36 matthew 783: $Partdata{$part}->{'Foils'}->{$foil}->{'name'}=$foil;
784: $Partdata{$part}->{'Foils'}->{$foil}->{'text'}=$value;
1.25 matthew 785: } elsif ($key =~ /^foil\.value\.(.*)$/) {
786: my $foil = $1;
1.36 matthew 787: $Partdata{$part}->{'Foils'}->{$foil}->{'value'}=$value;
1.25 matthew 788: }
789: }
790: }
1.23 matthew 791: }
1.25 matthew 792: return %Partdata;
1.1 stredwic 793: }
794:
1.23 matthew 795: 1;
1.1 stredwic 796:
797: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>