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