1: # The LearningOnline Network with CAPA
2: #
3:
4: # $Id: lonproblemanalysis.pm,v 1.40 2003/10/16 13:23:13 matthew Exp $
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:
29: package Apache::lonproblemanalysis;
30:
31: use strict;
32: use Apache::lonnet();
33: use Apache::loncommon();
34: use Apache::lonhtmlcommon();
35: use Apache::loncoursedata();
36: use Apache::lonstatistics;
37: use Apache::lonlocal;
38: use HTML::Entities();
39:
40: my $plotcolors = ['#33ff00',
41: '#0033cc', '#990000', '#aaaa66', '#663399', '#ff9933',
42: '#66ccff', '#ff9999', '#cccc33', '#660000', '#33cc66',
43: ];
44:
45: sub BuildProblemAnalysisPage {
46: my ($r,$c)=@_;
47: $r->print('<h2>'.&mt('Option Response Problem Analysis').'</h2>');
48: $r->print(&CreateInterface());
49: #
50: my @Students = @Apache::lonstatistics::Students;
51: #
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: }
62: $r->rflush();
63: if (exists($ENV{'form.problemchoice'}) &&
64: ! exists($ENV{'form.SelectAnother'})) {
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);
71: $r->print('<input type="submit" name="updatecaches" value="'.
72: &mt('Update Student Data').'" />');
73: $r->print(' 'x5);
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: #
80: $r->print('<hr />');
81: #
82: my ($symb,$part,$resid) = &get_problem_symb(
83: &Apache::lonnet::unescape($ENV{'form.problemchoice'})
84: );
85: #
86: my $resource = &get_resource_from_symb($symb);
87: if (defined($resource)) {
88: my %Data = &get_problem_data($resource->{'src'});
89: my $ORdata = $Data{$part.'.'.$resid};
90: ##
91: ## Render the problem
92: my $base;
93: ($base,undef) = ($resource->{'src'} =~ m|(.*/)[^/]*$|);
94: $base = "http://".$ENV{'SERVER_NAME'}.$base;
95: my $rendered_problem =
96: &Apache::lonnet::ssi_body($resource->{'src'});
97: $rendered_problem =~ s/<\s*form\s*/<nop /g;
98: $rendered_problem =~ s|(<\s*/form\s*>)|<\/nop>|g;
99: $r->print('<table bgcolor="ffffff"><tr><td>'.
100: '<base href="'.$base.'" />'.
101: $rendered_problem.
102: '</td></tr></table>');
103: ##
104: ## Analyze the problem
105: my $PerformanceData =
106: &Apache::loncoursedata::get_optionresponse_data
107: (\@Students,$symb,$resid);
108: if (defined($PerformanceData) &&
109: ref($PerformanceData) eq 'ARRAY') {
110: if ($ENV{'form.AnalyzeOver'} eq 'Tries') {
111: my $analysis_html = &tries_analysis($PerformanceData,
112: $ORdata);
113: $r->print($analysis_html);
114: } elsif ($ENV{'form.AnalyzeOver'} eq 'Time') {
115: my $analysis_html = &time_analysis($PerformanceData,
116: $ORdata);
117: $r->print($analysis_html);
118: } else {
119: $r->print('<h2>'.
120: &mt('The analysis you have selected is '.
121: 'not supported at this time').
122: '</h2>');
123: }
124: } else {
125: $r->print('<h2>'.
126: &mt('There is no student data for this problem.').
127: '</h2>');
128: }
129: } else {
130: $r->print('resource is undefined');
131: }
132: $r->print('<hr />');
133: } else {
134: $r->print('<input type="submit" name="ProblemAnalysis" value="'.
135: &mt('Analyze Problem').'" />');
136: $r->print(' 'x5);
137: $r->print('<h3>'.&mt('Please select a problem to analyze').'</h3>');
138: $r->print(&OptionResponseProblemSelector());
139: }
140: }
141:
142:
143: #########################################################
144: #########################################################
145: ##
146: ## Misc interface routines use by analysis code
147: ##
148: #########################################################
149: #########################################################
150: sub build_foil_index {
151: my ($ORdata) = @_;
152: my %Foildata = %{$ORdata->{'Foils'}};
153: my @Foils = sort(keys(%Foildata));
154: my %Concepts;
155: foreach my $foilid (@Foils) {
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;
176: if (exists($Numbers{lc($a)})) {
177: $a1 = $Numbers{lc($a)};
178: }
179: if (exists($Numbers{lc($b)})) {
180: $b1 = $Numbers{lc($b)};
181: }
182: $a1 cmp $b1;
183: };
184: my @Concepts;
185: foreach my $concept (sort $sortfunction (keys(%Concepts))) {
186: push(@Concepts,{ name => $concept,
187: foils => [@{$Concepts{$concept}}]});
188: push(@Foils,(@{$Concepts{$concept}}));
189: }
190: #
191: # Build up the table of row labels.
192: my $table = '<table border="1" >'."\n";
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: }
210: my $conceptindex = 1;
211: my $foilindex = 1;
212: foreach my $concept (@Concepts) {
213: my @FoilsInConcept = @{$concept->{'foils'}};
214: my $firstfoil = shift(@FoilsInConcept);
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 {
225: $table .= '<tr>'.
226: '<td>'.$foilindex++.'</td>'.
227: '<td>'.$Foildata{$firstfoil}->{'name'}.'</td>'.
228: '<td>'.$Foildata{$firstfoil}->{'text'}.'</td>'.
229: '<td>'.$Foildata{$firstfoil}->{'value'}.'</td>'.
230: "</tr>\n";
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: }
250: } continue {
251: $foilindex++;
252: }
253: } continue {
254: $conceptindex++;
255: }
256: $table .= "</table>\n";
257: #
258: # Build option index with color stuff
259: return ($table,\@Foils,\@Concepts);
260: }
261:
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:
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'};
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: }
298: my %ResponseData = &analyze_option_data_by_tries($PerformanceData,
299: $mintries,$maxtries);
300: #
301: # Compute the data neccessary to make the plots
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);
307: if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
308: $extrakey = &build_option_index($ORdata);
309: $xlabel = 'Foil Number';
310: $ylabel = 'Option Chosen';
311: foreach my $foilid (@$Foils) {
312: for (my $i=$mintries;$i<=$maxtries;$i++) {
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: }
323: }
324: }
325: }
326: } else {
327: # Concept analysis
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 = '';
333: $xlabel = 'Concept Number';
334: $ylabel = 'Percent Correct';
335: my %ConceptData;
336: foreach my $concept (@{$Concepts}) {
337: for (my $i=$mintries;$i<=$maxtries;$i++) {
338: #
339: # Gather the per-attempt data
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'};
346: }
347: push (@{$PlotData[$i]->{'_total'}},$cdata->{'_total'});
348: if ($cdata->{'_total'} == 0) {
349: push (@{$PlotData[$i]->{'_correct'}},0);
350: } else {
351: push (@{$PlotData[$i]->{'_correct'}},
352: 100*$cdata->{'_correct'}/$cdata->{'_total'});
353: }
354: }
355: }
356: } # End of work to fill @PlotData
357: #
358: # Build a table for the plots
359: $table .= "<table>\n";
360: my @Plots;
361: for (my $i=$mintries;$i<=$maxtries;$i++) {
362: my $minstu = $PlotData[$i]->{'_total'}->[0];
363: my $maxstu = $PlotData[$i]->{'_total'}->[0];
364: foreach my $count (@{$PlotData[$i]->{'_total'}}) {
365: if ($minstu > $count) {
366: $minstu = $count;
367: }
368: if ($maxstu < $count) {
369: $maxstu = $count;
370: }
371: }
372: $maxstu = 0 if (! defined($maxstu));
373: $minstu = 0 if (! defined($minstu));
374: my $title;
375: if ($maxstu == $minstu) {
376: $title = 'Attempt '.$i.', '.$maxstu.' students';
377: } else {
378: $title = 'Attempt '.$i.', '.$minstu.'-'.$maxstu.' students';
379: }
380: my @Datasets;
381: foreach my $option ('_correct',@{$ORdata->{'Options'}}) {
382: next if (! exists($PlotData[$i]->{$option}));
383: push(@Datasets,$PlotData[$i]->{$option});
384: }
385: my $graphlink = &Apache::loncommon::DrawGraph($title,
386: $xlabel,
387: $ylabel,
388: 100,
389: $plotcolors,
390: @Datasets);
391: push(@Plots,$graphlink);
392: }
393: #
394: # Should this be something the user can set? Too many dialogs!
395: while (my $plotlink = shift(@Plots)) {
396: $table .= '<tr><td>'.$plotlink.'</td><td>'.$extrakey."</td></tr>\n";
397: }
398: $table .= "</table>\n";
399: return ($table);
400: }
401:
402: sub analyze_option_data_by_tries {
403: my ($PerformanceData,$mintries,$maxtries) = @_;
404: my %Trydata;
405: $mintries = 1 if (! defined($mintries) || $mintries < 1);
406: $maxtries = $mintries if (! defined($maxtries) || $maxtries < $mintries);
407: foreach my $row (@$PerformanceData) {
408: next if (! defined($row));
409: my ($grading,$submission,$time,$tries) = @$row;
410: next if ($grading eq 'MISSING_ANSWER');
411: my @Foilgrades = split('&',$grading);
412: my @Foilsubs = split('&',$submission);
413: for (my $numtries = 1; $numtries <= $maxtries; $numtries++) {
414: if ($tries == $numtries) {
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;
420: if ($correct) {
421: $Trydata{$foilid}->[$numtries]->{'_correct'}++;
422: } else {
423: $Trydata{$foilid}->[$numtries]->{$submission}++;
424: }
425: $Trydata{$foilid}->[$numtries]->{'_total'}++;
426: }
427: }
428: }
429: }
430: return %Trydata;
431: }
432:
433: #########################################################
434: #########################################################
435: ##
436: ## Time Analysis
437: ##
438: #########################################################
439: #########################################################
440: sub time_analysis {
441: my ($PerformanceData,$ORdata) = @_;
442: my $num_plots = $ENV{'form.NumPlots'};
443: my ($table,$Foils,$Concepts) = &build_foil_index($ORdata);
444: my $num_data = scalar(@$PerformanceData)-1;
445: my $percent = sprintf('%2f',100/$num_plots);
446: my $extratable = '';
447: if ($ENV{'form.AnalyzeAs'} eq 'Foils') {
448: $extratable = &build_option_index($ORdata);
449: }
450: $table .= "<table>\n";
451: for (my $i=0;$i<$num_plots;$i++) {
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;
475: $plottitle = $ENV{'form.plottitle_'.$i};
476: }
477: ($plothtml,$starttime,$endtime,$data) =
478: &analyze_option_data_by_time($PerformanceData,
479: $begin_index,$end_index,
480: $plottitle,$Foils,
481: $Concepts,$ORdata);
482: my $startdateform = &Apache::lonhtmlcommon::date_setter
483: ('Statistics','startdate_'.$i,$starttime);
484: my $enddateform = &Apache::lonhtmlcommon::date_setter
485: ('Statistics','enddate_'.$i,$endtime);
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 />".
489: '<b>Plot Title</b> :'.(" "x3).
490: '<input type="text" size="30" name="plottitle_'.$i.'" value="'.
491: &HTML::Entities::encode($plottitle).'" /><br />'.$extratable.
492: "</td></tr>\n";
493: }
494: $table .="</table>\n";
495: return $table;
496: }
497:
498: sub analyze_option_data_by_time {
499: my ($PerformanceData,$begin_index,
500: $end_index,$description,$Foils,$Concepts,$ORdata) = @_;
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;
512: next if ($grading eq 'MISSING_ANSWER');
513: my @Foilgrades = split('&',$grading);
514: my @Foilsubs = split('&',$submission);
515: for (my $j=0;$j<=$#Foilgrades;$j++) {
516: my ($foilid,$correct) = split('=',$Foilgrades[$j]);
517: my (undef,$submission) = split('=',$Foilsubs[$j]);
518: if ($correct) {
519: $TimeData{$foilid}->{'_correct'}++;
520: } else {
521: $submission = &HTML::Entities::decode($submission);
522: $submission =~ s/\%20/ /g;
523: $TimeData{$foilid}->{$submission}++;
524: }
525: $TimeData{$foilid}->{'_total'}++;
526: }
527: }
528: #
529: # Compute the total and percent correct
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++;
547: }
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'};
558: }
559: if ($total > 0) {
560: push(@{$Plotdata[0]},100 * $correct / $total);
561: } else {
562: push(@{$Plotdata[0]},0);
563: }
564: }
565: }
566: #
567: # Create the plot
568: my $graphlink = &Apache::loncommon::DrawGraph
569: ($description,#'Time Interval Analysis',
570: $xlabel,
571: $ylabel,
572: 100,
573: $plotcolors,
574: @Plotdata);
575: #
576: return ($graphlink,$starttime,$endtime,\%TimeData);
577: }
578:
579: #########################################################
580: #########################################################
581: ##
582: ## Interface
583: ##
584: #########################################################
585: #########################################################
586: sub CreateInterface {
587: ##
588: ## Environment variable initialization
589: if (! exists$ENV{'form.AnalyzeOver'}) {
590: $ENV{'form.AnalyzeOver'} = 'Tries';
591: }
592: ##
593: ## Build the menu
594: my $Str = '';
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>';
599: # $Str .= '<td align="center"><b>'.&mt('Sequences and Folders').'</b></td>';
600: $Str .= '<td align="center"> </td>';
601: $Str .= '</tr>'."\n";
602: ##
603: ##
604: $Str .= '<tr><td align="center">'."\n";
605: $Str .= &Apache::lonstatistics::SectionSelect('Section','multiple',5);
606: $Str .= '</td>';
607: #
608: $Str .= '<td align="center">';
609: $Str .= &Apache::lonhtmlcommon::StatusOptions(undef,undef,5);
610: $Str .= '</td>';
611: #
612: # $Str .= '<td align="center">';
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: };
621: &Apache::lonstatistics::MapSelect('Maps','multiple,all',5,
622: $only_seq_with_assessments);
623: ##
624: ##
625: $Str .= '<td>';
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: }
672: }
673: $Str .= '</td>';
674: ##
675: ##
676: $Str .= '</tr>'."\n";
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'}}) {
688: next if ($res->{'type'} ne 'assessment');
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') {
699: my $value = &Apache::lonnet::escape($res->{'symb'}.':'.$part.':'.$respid);
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";
712: }
713: }
714: }
715: }
716: if ($seq_str ne '') {
717: $Str .= '<tr><td> </td><td><b>'.$seq->{'title'}.'</b></td>'.
718: "</tr>\n".$seq_str;
719: }
720: }
721: $Str .= "</table>\n";
722: return $Str;
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);
736: }
737:
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;
744: }
745: }
746: }
747: return undef;
748: }
749:
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: ##
755: sub get_problem_data {
756: my ($url) = @_;
757: my $Answ=&Apache::lonnet::ssi($url,('grade_target' => 'analyze'));
758: (my $garbage,$Answ)=split(/_HASH_REF__/,$Answ,2);
759: my %Answer;
760: %Answer=&Apache::lonnet::str2hash($Answ);
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') {
768: for(my $i=0;$i<scalar(@$value);$i++) {
769: $value->[$i]=~ s/(\s*$|^\s*)//g;
770: }
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) {
777: $foil =~ s/(\s*$|^\s*)//g;
778: $Partdata{$part}->{'Foils'}->{$foil}->{'Concept'}=
779: $concept;
780: }
781: }
782: } else {
783: $value =~ s/(\s*$|^\s*)//g;
784: if ($key=~ /^foil\.text\.(.*)$/) {
785: my $foil = $1;
786: $foil =~ s/(\s*$|^\s*)//g;
787: $Partdata{$part}->{'Foils'}->{$foil}->{'name'}=$foil;
788: $Partdata{$part}->{'Foils'}->{$foil}->{'text'}=$value;
789: } elsif ($key =~ /^foil\.value\.(.*)$/) {
790: my $foil = $1;
791: $foil =~ s/(\s*$|^\s*)//g;
792: $Partdata{$part}->{'Foils'}->{$foil}->{'value'}=$value;
793: }
794: }
795: }
796: }
797: return %Partdata;
798: }
799:
800: 1;
801:
802: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>