Annotation of loncom/homework/matchresponse.pm, revision 1.2
1.1 albertel 1: # The LearningOnline Network with CAPA
2: # Full matching style response
3: #
1.2 ! albertel 4: # $Id: matchresponse.pm,v 1.1 2003/01/28 00:11:49 albertel Exp $
1.1 albertel 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: # 2/21 Guy
29:
30: package Apache::matchresponse;
31: use strict;
32: use HTML::Entities();
33: use Math::Random();
34:
35: BEGIN {
36: &Apache::lonxml::register('Apache::matchresponse',('matchresponse'));
37: }
38:
39: sub start_matchresponse {
40: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
41: my $result;
42: #when in a matchresponse use these
43: &Apache::lonxml::register('Apache::matchresponse',
44: ('foilgroup','foil','conceptgroup','item',
45: 'itemgroup'));
46: push (@Apache::lonxml::namespace,'matchresponse');
47: my $id = &Apache::response::start_response($parstack,$safeeval);
48: %Apache::hint::match=();
49: if ($target eq 'meta') {
50: $result=&Apache::response::meta_package_write('matchresponse');
51: } elsif ($target eq 'edit' ) {
52: $result.=&Apache::edit::start_table($token).
53: '<tr><td>'.&Apache::lonxml::description($token)."</td><td>Delete:".
54: &Apache::edit::deletelist($target,$token)
55: ."</td><td> ".&Apache::edit::end_row()
56: .&Apache::edit::start_spanning_row();
57:
58: $result.=
59: &Apache::edit::text_arg('Max Number Of Shown Foils:','max',$token,'4').
60: &Apache::edit::select_arg('Randomize Foil Order','randomize',
61: ['yes','no'],$token).
62: &Apache::edit::end_row().&Apache::edit::start_spanning_row()."\n";
63: } elsif ($target eq 'modified') {
64: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
65: $safeeval,'max',
66: 'randomize');
67: if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
68: }
69: return $result;
70: }
71:
72: sub end_matchresponse {
73: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
74: my $result;
75: if ($target eq 'edit') { $result=&Apache::edit::end_table(); }
76: &Apache::response::end_response;
77: pop @Apache::lonxml::namespace;
78: &Apache::lonxml::deregister('Apache::matchresponse',
79: ('foilgroup','foil','conceptgroup'));
80: return $result;
81: }
82:
83: %Apache::response::itemgroup=();
84: sub start_itemgroup {
85: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
86: my $result;
87: %Apache::response::itemgroup=();
88: if ($target eq 'edit') {
89: $result=&Apache::edit::tag_start($target,$token);
90: $result.=&Apache::edit::select_arg('Randomize Order:','randomize',
91: ['yes','no'],$token);
92: $result.=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
93: }
94: return $result;
95: }
96:
97: sub end_itemgroup {
98: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
99: my $result;
100:
101: if (!defined(@{ $Apache::response::itemgroup{'names'} })) { return; }
102: my @names=@{ $Apache::response::itemgroup{'names'} };
103: my $randomize =&Apache::lonxml::get_param('randomize',$parstack,$safeeval);
1.2 ! albertel 104: if ($randomize ne 'no' ) {
1.1 albertel 105: @names=&Math::Random::random_permutation(@names);
106: }
107: my %letter_name_map;
108: my %name_letter_map;
109: my @alphabet=('A'..'Z');
110: my $i=0;
111: foreach my $name (@names) {
112: $letter_name_map{$alphabet[$i]}=$name;
113: $name_letter_map{$name}=$alphabet[$i];
114: $i++;
115: }
116: $Apache::response::itemgroup{'letter_name_map'}=\%letter_name_map;
117: $Apache::response::itemgroup{'name_letter_map'}=\%name_letter_map;
118: if ($target eq 'web') {
119: $result.='<table>';
120: my $i=0;
121: foreach my $name (@names) {
122: $result.='<tr><td>'.$alphabet[$i].'</td><td>'.
123: $Apache::response::itemgroup{$name.'.text'}.
124: '</td></tr>';
125: $i++;
126: }
127: $result.='</table>';
128: } elsif ($target eq 'edit') { $result=&Apache::edit::end_table(); }
129: return $result;
130: }
131:
132: sub start_item {
133: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
134: my $result='';
135: if ($target eq 'web' ) {
136: &Apache::lonxml::startredirection;
137: } elsif ($target eq 'edit') {
138: $result=&Apache::edit::tag_start($target,$token,"Item");
139: $result.=&Apache::edit::text_arg('Name:','name',$token);
140: $result .=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
141: } elsif ($target eq 'modified') {
142: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
143: $safeeval,'name');
144: if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
145: }
146: return $result;
147: }
148:
149: sub end_item {
150: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
151: my $text ='';
152: my $result = '';
153: if ($target eq 'web') {
154: $text=&Apache::lonxml::endredirection;
155: }
156: if ($target eq 'web' || $target eq 'grade' || $target eq 'answer' ||
157: $target eq 'edit') {
158: my $name = &Apache::lonxml::get_param('name',$parstack,$safeeval);
159: &Apache::lonxml::debug("Got a name of :$name:");
160: if (!$name) { $name=$Apache::lonxml::curdepth; }
161: &Apache::lonxml::debug("Using a name of :$name:");
162: push @{ $Apache::response::itemgroup{'names'} }, $name;
163: $Apache::response::itemgroup{"$name.text"} = $text;
164: }
165: if ($target eq 'edit') {
166: $result.= &Apache::edit::tag_end($target,$token,'');
167: }
168: return $result;
169: }
170:
171: sub insert_item {
172: return '
173: <item name="">
174: <startouttext />
175: <endouttext />
176: </item>';
177: }
178:
179: %Apache::response::foilgroup=();
180: sub start_foilgroup {
181: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
182: my $result;
183: %Apache::response::foilgroup=();
184: $Apache::matchresponse::conceptgroup=0;
185: &Apache::response::setrandomnumber();
186: if ($target eq 'edit') {
187: $result.=&Apache::edit::start_table($token)
188: ."<tr><td>Collection Of Foils</td><td>Delete:"
189: .&Apache::edit::deletelist($target,$token)
190: ."</td><td> ".&Apache::edit::end_row()
191: .&Apache::edit::start_spanning_row()."\n";
192: }
193: return $result;
194: }
195:
196: sub end_foilgroup {
197: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
198: my $result;
199: if ($target eq 'grade' || $target eq 'web' || $target eq 'answer') {
200: my $max = &Apache::lonxml::get_param('max',$parstack,$safeeval,'-2');
201: my $randomize = &Apache::lonxml::get_param('randomize',$parstack,
202: $safeeval,'-2');
203: if ($target eq 'web') {
204: $result=&displayfoils($target,$max,$randomize);
205: } elsif ($target eq 'answer' ) {
206: $result=&displayanswers($max,$randomize);
207: } elsif ( $target eq 'grade') {
208: &grade_response($max,$randomize);
209: }
210: } elsif ($target eq 'edit') {
211: $result=&Apache::edit::end_table();
212: }
213: return $result;
214: }
215:
216: sub displayanswers {
217: my ($max,$randomize,@opt)=@_;
218: if (!defined(@{ $Apache::response::foilgroup{'names'} })) { return; }
219: my @names = @{ $Apache::response::foilgroup{'names'} };
220: my @whichfoils = &whichfoils($max,$randomize);
221: my $result=&Apache::response::answer_header('matchresponse');
222: my %name_letter_map;
223: if (defined(%{ $Apache::response::itemgroup{'name_letter_map'} })) {
224: %name_letter_map=
225: %{ $Apache::response::itemgroup{'name_letter_map'} };
226: }
227: foreach my $name (@whichfoils) {
228: my $value_name=$Apache::response::foilgroup{$name.'.value'};
229: my $letter=$name_letter_map{$value_name};
230: $result.=&Apache::response::answer_part('matchresponse',$letter);
231: }
232: $result.=&Apache::response::answer_footer('matchresponse');
233: return $result;
234: }
235:
236:
237: sub grade_response {
238: my ($max,$randomize)=@_;
239: my (@whichfoils)=&whichfoils($max,$randomize);
240: if (!defined($ENV{'form.submitted'})) { return; }
241: my %responsehash;
242: my %grade;
243: my ($temp,$right,$wrong,$ignored)=(0,0,0,0);
244: my %letter_name_map;
245: if (defined(%{ $Apache::response::itemgroup{'letter_name_map'} })) {
246: %letter_name_map=
247: %{ $Apache::response::itemgroup{'letter_name_map'} };
248: }
249: foreach my $name (@whichfoils) {
250: my $response = $ENV{'form.HWVAL_'.$Apache::inputtags::response['-1'].":$temp"};
251: my $responsename = $letter_name_map{$response};
252: $responsehash{$name}=$responsename;
253: my $value=$Apache::response::foilgroup{$name.'.value'};
254: if ( $response =~ /[^\s]/) {
255: &Apache::lonxml::debug("submitted a $response for $value<br />\n");
256: if ($value eq $responsename) {
257: $grade{$name}='1'; $right++;
258: } else {
259: $grade{$name}='0'; $wrong++;
260: }
261: } else {
262: $ignored++;
263: }
264: $temp++;
265: }
266: my $part=$Apache::inputtags::part;
267: my $id = $Apache::inputtags::response['-1'];
268: my $responsestr=&Apache::lonnet::hash2str(%responsehash);
269: my $gradestr =&Apache::lonnet::hash2str(%grade);
270: my %previous =&Apache::response::check_for_previous($responsestr,
271: $part,$id);
272: &Apache::lonxml::debug("Got $right right and $wrong wrong, and $ignored were ignored ");
273: my $ad;
274: if ($wrong==0 && $ignored==0) {
275: $ad='EXACT_ANS';
276: } elsif ($wrong==0 && $right==0) {
277: #nothing submitted
278: } else {
279: if ($ignored==0) {
280: $ad='INCORRECT';
281: } else {
282: $ad='MISSING_ANSWER';
283: }
284: }
285: $Apache::lonhomework::results{"resource.$part.$id.submission"}=
286: $responsestr;
287: $Apache::lonhomework::results{"resource.$part.$id.submissiongrading"}=
288: $gradestr;
289: $Apache::lonhomework::results{"resource.$part.$id.awarddetail"}=$ad;
290: &Apache::response::handle_previous(\%previous,$ad);
291: }
292:
293: sub displayfoils {
294: my ($target,$max,$randomize)=@_;
295: my $result;
296: my (@whichfoils)=&whichfoils($max,$randomize);
297: my $part=$Apache::inputtags::part;
298: my $solved=$Apache::lonhomework::history{"resource.$part.solved"};
299: my $status=$Apache::inputtags::status[-1];
300: my %letter_name_map;
301: if (defined(%{ $Apache::response::itemgroup{'letter_name_map'} })) {
302: %letter_name_map=
303: %{ $Apache::response::itemgroup{'letter_name_map'} };
304: }
305: my %name_letter_map;
306: if (defined(%{ $Apache::response::itemgroup{'name_letter_map'} })) {
307: %name_letter_map=
308: %{ $Apache::response::itemgroup{'name_letter_map'} };
309: }
310: if (($solved =~ /^correct/) || ($status eq 'SHOW_ANSWER')) {
311: foreach my $name (@whichfoils) {
312: my $text=$Apache::response::foilgroup{$name.'.text'};
313: my $value=$Apache::response::foilgroup{$name.'.value'};
314: my $letter=$name_letter_map{$value};
315: $result.='<br />'.$letter.':'.$text;
316: }
317: } else {
318: my $i = 0;
319: my $temp=0;
320: my $id=$Apache::inputtags::response[-1];
321: my $part=$Apache::inputtags::part;
322: my $lastresponse=$Apache::lonhomework::history{"resource.$part.$id.submission"};
323: my %lastresponse=&Apache::lonnet::str2hash($lastresponse);
324: foreach my $name (@whichfoils) {
325: my $lastopt=$lastresponse{$name};
326: my $last_letter=$name_letter_map{$lastopt};
327: my $optionlist="<option></option>\n";
328: my $option;
329: foreach $option (sort(keys(%letter_name_map))) {
330: if ($option eq $last_letter) {
331: $optionlist.="<option selected=\"on\">$option</option>\n";
332: } else {
333: $optionlist.="<option>$option</option>\n";
334: }
335: }
336: $optionlist='<select name="HWVAL_'.
337: $Apache::inputtags::response[-1].':'.$temp.'">'.
338: $optionlist."</select>\n";
339: my $text=$Apache::response::foilgroup{$name.'.text'};
340: $result.='<br />'.$optionlist.$text."\n";
341: $temp++;
342: }
343: }
344: $result.="<br />";
345: return $result;
346: }
347:
348: sub getfoilcounts {
349: my ($max)=@_;
350: # +1 since instructors will count from 1
351: my $count = $#{ $Apache::response::foilgroup{'names'} }+1;
352: if (&Apache::response::showallfoils()) { $max=$count; }
353: if ($count>$max) { $count=$max }
354: &Apache::lonxml::debug("Count is $count from $max");
355: return $count;
356: }
357:
358: sub whichfoils {
359: my ($max,$randomize)=@_;
360: $max = &getfoilcounts($max);
361: # &Apache::lonxml::debug("man $max randomize $randomize");
362: if (!defined(@{ $Apache::response::foilgroup{'names'} })) { return; }
363: my @names = @{ $Apache::response::foilgroup{'names'} };
364: my @whichopt =();
365: my (%top,@toplist,%bottom,@bottomlist);
366: if (!(&Apache::response::showallfoils() || ($randomize eq 'no'))) {
367: my $current=0;
368: foreach my $name (@names) {
369: $current++;
370: if ($Apache::response::foilgroup{"$name.location"} eq 'top') {
371: $top{$name}=$current;
372: } elsif ($Apache::response::foilgroup{"$name.location"} eq
373: 'bottom') {
374: $bottom{$name}=$current;
375: }
376: }
377: }
378: while ((($#whichopt+1) < $max) && ($#names > -1)) {
379: # &Apache::lonxml::debug("Have $#whichopt max is $max");
380: my $aopt;
381: if (&Apache::response::showallfoils() || ($randomize eq 'no')) {
382: $aopt=0;
383: } else {
384: $aopt=int(&Math::Random::random_uniform() * ($#names+1));
385: }
386: # &Apache::lonxml::debug("From $#whichopt $max $#names elms, picking $aopt");
387: $aopt=splice(@names,$aopt,1);
388: # &Apache::lonxml::debug("Picked $aopt");
389: if ($top{$aopt}) {
390: $toplist[$top{$aopt}]=$aopt;
391: } elsif ($bottom{$aopt}) {
392: $bottomlist[$bottom{$aopt}]=$aopt;
393: } else {
394: push (@whichopt,$aopt);
395: }
396: }
397: for (my $i=0;$i<=$#toplist;$i++) {
398: if ($toplist[$i]) { unshift(@whichopt,$toplist[$i]) }
399: }
400: for (my $i=0;$i<=$#bottomlist;$i++) {
401: if ($bottomlist[$i]) { push(@whichopt,$bottomlist[$i]) }
402: }
403:
404: return @whichopt;
405: }
406:
407: sub start_conceptgroup {
408: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
409: $Apache::matchresponse::conceptgroup=1;
410: %Apache::response::conceptgroup=();
411: my $result;
412: if ($target eq 'edit') {
413: $result.=&Apache::edit::tag_start($target,$token,
414: "Concept Grouped Foils");
415: $result.=&Apache::edit::text_arg('Concept:','concept',$token,'50').
416: &Apache::edit::end_row().&Apache::edit::start_spanning_row();
417: }
418: if ($target eq 'modified') {
419: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
420: $safeeval,'concept');
421: if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
422: }
423: return $result;
424: }
425:
426: sub end_conceptgroup {
427: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
428: $Apache::matchresponse::conceptgroup=0;
429: my $result='';
430: if ($target eq 'web' || $target eq 'grade' || $target eq 'answer' ) {
431: #if not there aren't any foils to display and thus no question
432: if (defined(@{ $Apache::response::conceptgroup{'names'} })) {
433: my @names = @{ $Apache::response::conceptgroup{'names'} };
434: my $pick=int(&Math::Random::random_uniform() * ($#names+1));
435: my $name=$names[$pick];
436: push @{ $Apache::response::foilgroup{'names'} }, $name;
437: $Apache::response::foilgroup{"$name.value"} =
438: $Apache::response::conceptgroup{"$name.value"};
439: $Apache::response::foilgroup{"$name.text"} =
440: $Apache::response::conceptgroup{"$name.text"};
441: $Apache::response::foilgroup{"$name.location"} =
442: $Apache::response::conceptgroup{"$name.location"};
443: my $concept = &Apache::lonxml::get_param('concept',$parstack,
444: $safeeval);
445: $Apache::response::foilgroup{"$name.concept"} = $concept;
446: &Apache::lonxml::debug("Selecting $name in $concept");
447: if ($target eq 'web') {
448: my $part_id="$Apache::inputtags::part.$Apache::inputtags::response[-1]";
449: push(@{ $Apache::hint::match{"$part_id.concepts"} },
450: $concept);
451: $Apache::hint::match{"$part_id.concept.$concept"}=
452: $Apache::response::conceptgroup{'names'};
453: }
454: }
455: } elsif ($target eq 'edit') {
456: $result=&Apache::edit::end_table();
457: }
458: return $result;
459: }
460:
461: sub insert_conceptgroup {
462: my $result="\n\t\t<conceptgroup concept=\"\">".&insert_foil()."\n\t\t</conceptgroup>\n";
463: return $result;
464: }
465:
466: sub start_foil {
467: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
468: my $result='';
469: if ($target eq 'web' ) {
470: &Apache::lonxml::startredirection;
471: } elsif ($target eq 'edit') {
472: $result=&Apache::edit::tag_start($target,$token,"Foil");
473: my $level='-2';
474: if ($$tagstack[-2] eq 'conceptgroup') { $level = '-3'; }
475: $result.=&Apache::edit::text_arg('Name:','name',$token);
476: my @names;
477: if (defined(@{ $Apache::response::itemgroup{'names'} })) {
478: @names=@{ $Apache::response::itemgroup{'names'} };
479: }
480: $result.= &Apache::edit::select_or_text_arg('Correct Option:','value',['unused',@names],$token,'15');
481: my $randomize=&Apache::lonxml::get_param('randomize',$parstack,
482: $safeeval,'-3');
483: if ($randomize ne 'no') {
484: $result.=&Apache::edit::select_arg('Location:','location',
485: ['random','top','bottom'],$token);
486: }
487: $result .=&Apache::edit::end_row().&Apache::edit::start_spanning_row();
488: } elsif ($target eq 'modified') {
489: my $constructtag=&Apache::edit::get_new_args($token,$parstack,
490: $safeeval,'value',
491: 'name','location');
492: if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); }
493: }
494: return $result;
495: }
496:
497: sub end_foil {
498: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_;
499: my $text ='';
500: my $result = '';
501: if ($target eq 'web') {
502: $text=&Apache::lonxml::endredirection;
503: }
504: if ($target eq 'web' || $target eq 'grade' || $target eq 'answer') {
505: my $value = &Apache::lonxml::get_param('value',$parstack,$safeeval);
506: if ($value ne 'unused') {
507: my $name = &Apache::lonxml::get_param('name',$parstack,$safeeval);
508: my $location =&Apache::lonxml::get_param('location',$parstack,$safeeval);
509: &Apache::lonxml::debug("Got a name of :$name:");
510: if (!$name) { $name=$Apache::lonxml::curdepth; }
511: &Apache::lonxml::debug("Using a name of :$name:");
512: if ( $Apache::matchresponse::conceptgroup
513: && !&Apache::response::showallfoils() ) {
514: push @{ $Apache::response::conceptgroup{'names'} }, $name;
515: $Apache::response::conceptgroup{"$name.value"} = $value;
516: $Apache::response::conceptgroup{"$name.text"} = $text;
517: $Apache::response::conceptgroup{"$name.location"} = $location;
518: } else {
519: push @{ $Apache::response::foilgroup{'names'} }, $name;
520: $Apache::response::foilgroup{"$name.value"} = $value;
521: $Apache::response::foilgroup{"$name.text"} = $text;
522: $Apache::response::foilgroup{"$name.location"} = $location;
523: }
524: }
525: }
526: if ($target eq 'edit') {
527: $result.= &Apache::edit::tag_end($target,$token,'');
528: }
529: return $result;
530: }
531:
532: sub insert_foil {
533: return '
534: <foil name="" value="unused">
535: <startouttext />
536: <endouttext />
537: </foil>';
538: }
539: 1;
540: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>