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