1: # The LearningOnline Network with CAPA
2: # (Publication Handler
3: #
4: # $Id: lonproblemanalysis.pm,v 1.13 2002/11/22 04:04:10 minaeibi 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: # (Navigate problems for statistical reports
29: # YEAR=2002
30: # 5/12,7/26,9/7,11/22 Behrouz Minaei
31: #
32: ###
33:
34: package Apache::lonproblemanalysis;
35:
36: use strict;
37: use Apache::lonnet();
38: use Apache::lonhtmlcommon();
39: use GDBM_File;
40:
41: my $jr;
42:
43: sub BuildProblemAnalysisPage {
44: my ($cacheDB, $r)=@_;
45:
46: my %cache;
47: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
48: $r->print('Unable to tie database.');
49: return;
50: }
51:
52: my $Ptr = '';
53: $Ptr .= '<table border="0"><tbody>';
54: $Ptr .= '<tr><td align="right"><b>Select Sections</b>';
55: $Ptr .= '</td>'."\n";
56: $Ptr .= '<td align="left">'."\n";
57: my @sectionsSelected = split(':',$cache{'sectionsSelected'});
58: my @sections = split(':',$cache{'sectionList'});
59: $Ptr .= &Apache::lonhtmlcommon::MultipleSectionSelect(\@sections,
60: \@sectionsSelected,
61: 'Statistics');
62: $Ptr .= '</td></tr>'."\n";
63: $Ptr .= '<tr><td align="right"><b>Intervals</b></td>'."\n";
64: $Ptr .= '<td align="left">';
65: $Ptr .= &IntervalOptions($cache{'Interval'});
66: $Ptr .= '</td></tr></table><br>';
67: $r->print($Ptr);
68: $r->rflush();
69: $r->print(&OptionResponseTable($cache{'OptionResponses'}, \%cache, $r));
70:
71: untie(%cache);
72:
73: return;
74: }
75:
76: sub BuildAnalyzePage {
77: my ($cacheDB, $students, $courseID,$r)=@_;
78:
79: $jr = $r;
80: my $c = $r->connection;
81:
82: my $Str = '</form>';
83: my %cache;
84:
85: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
86: $Str .= 'Unable to tie database.';
87: $r->print($Str);
88: return;
89: }
90:
91: # Remove students who don't have the proper section.
92: my @sectionsSelected = split(':',$cache{'sectionsSelected'});
93: for(my $studentIndex=((scalar @$students)-1); $studentIndex>=0;
94: $studentIndex--) {
95: my $value = $cache{$students->[$studentIndex].':section'};
96: my $found = 0;
97: foreach (@sectionsSelected) {
98: if($_ eq 'none') {
99: if($value eq '' || !defined($value) || $value eq ' ') {
100: $found = 1;
101: last;
102: }
103: } else {
104: if($value eq $_) {
105: $found = 1;
106: last;
107: }
108: }
109: }
110: if($found == 0) {
111: splice(@$students, $studentIndex, 1);
112: }
113: }
114: unless(untie(%cache)) {
115: $r->print('Can not untie hash.');
116: $r->rflush();
117: }
118:
119: # my $error =
120: # &Apache::loncoursedata::DownloadStudentCourseDataSeparate($students,
121: # 'true',
122: # $cacheDB,
123: # 'true',
124: # 'true',
125: # $courseID,
126: # $r, $c);
127: # if($error ne 'OK') {
128: # $r->print($error.'<br>Error downloading course data<br>');
129: # return;
130: # }
131:
132:
133: unless(tie(%cache,'GDBM_File',$cacheDB,&GDBM_READER(),0640)) {
134: $Str .= 'Unable to tie database.';
135: $r->print($Str);
136: return;
137: }
138:
139: my ($problemId, $part, $responseId)=split(':',$cache{'AnalyzeInfo'});
140: my $uri = $cache{$problemId.':source'};
141: my $problem = $cache{$problemId.':problem'};
142: my $title = $cache{$problemId.':title'};
143: my $interval = $cache{'Interval'};
144:
145: my %ConceptData;
146: $ConceptData{"Interval"} = $interval;
147:
148: #Initialize the option response true answers
149: my ($analyzeData) = &InitAnalysis($uri, $part, $responseId, $problem,
150: $students->[0], $courseID);
151: if(defined($analyzeData->{'error'})) {
152: $Str .= $analyzeData->{'error'}.'<br>Incorrect part requested.<br>';
153: $r->print($Str);
154: return;
155: }
156:
157: $r->print($Str);
158: $Str = '';
159: if($c->aborted()) { untie(%cache); return; }
160:
161: #compute the intervals
162: &Interval($part, $problem, $interval, $analyzeData->{'concepts'},
163: \%ConceptData);
164:
165: $title =~ s/\ /"_"/eg;
166: $Str .= '<br><b>'.$uri.'</b>';
167:
168: $r->print($Str);
169: $Str = '';
170: if($c->aborted()) { untie(%cache); return; }
171:
172: #Java script Progress window
173: for(my $index=0; $index<(scalar @$students); $index++) {
174: if($c->aborted()) { untie(%cache); return; }
175: &OpStatus($problemId, $students->[$index], \%ConceptData,
176: $analyzeData->{'foil_to_concept'}, $analyzeData,
177: \%cache, $courseID);
178: }
179:
180: $Str .= '<br>';
181: for (my $k=0; $k<$interval; $k++ ) {
182: if($c->aborted()) { untie(%cache); return $Str; }
183: $Str .= &DrawGraph($k, $title, $analyzeData->{'concepts'},
184: \%ConceptData);
185: $r->print($Str);
186: $Str = '';
187: }
188: for (my $k=0; $k<$interval; $k++ ) {
189: if($c->aborted()) { untie(%cache); return $Str; }
190: $Str .= &DrawTable($k, $analyzeData->{'concepts'}, \%ConceptData);
191: $r->print($Str);
192: $Str = '';
193: }
194: my $Answ=&Apache::lonnet::ssi($uri);
195: $Str .= '<br><b>Here you can see the Problem:</b><br>'.$Answ;
196: $Str .= '<form>';
197: $r->print($Str);
198:
199: untie(%cache);
200:
201: return;
202: }
203:
204: #---- Problem Analysis Web Page ----------------------------------------------
205:
206: sub IntervalOptions {
207: my ($selectedInterval)=@_;
208:
209: my $interval = 1;
210: for(my $n=1; $n<=7; $n++) {
211: if($selectedInterval == $n) {
212: $interval = $n;
213: }
214: }
215:
216: my $Ptr = '<select name="Interval">'."\n";
217: for(my $n=1; $n<=7;$ n++) {
218: $Ptr .= '<option';
219: if($interval == $n) {
220: $Ptr .= ' selected';
221: }
222: $Ptr .= '>'.$n."</option>"."\n";
223: }
224: $Ptr .= '</select>'."\n";
225:
226: return $Ptr;
227: }
228:
229: sub OptionResponseTable {
230: my ($optionResponses,$cache,$r)=@_;
231:
232: my @optionResponses=split(':::', $optionResponses);
233: my %partCount;
234: my %sequences;
235: my @orderedSequences=();
236: foreach(@optionResponses) {
237: my ($sequence, $problemId, $part, undef)=split(':',$_);
238: $partCount{$problemId.':'.$part}++;
239: if(!defined($sequences{$sequence})) {
240: push(@orderedSequences, $sequence);
241: $sequences{$sequence} = $_;
242: } else {
243: $sequences{$sequence} .= ':::'.$_;
244: }
245: }
246:
247: my $Str = '';
248:
249: foreach my $sequence (@orderedSequences) {
250: my @optionProblems = split(':::', $sequences{$sequence});
251:
252: $Str .= '<b>'.$cache->{$sequence.':title'}.'</b>'."\n";
253: $Str .= "<table border=2><tr><th> \# </th><th> Problem Title </th>";
254: $Str .= '<th> Resource </th><th> Analysis </th></tr>'."\n";
255:
256: my $count = 1;
257: foreach(@optionProblems) {
258: my (undef, $problemId, $part, $response)=
259: split(':',$optionProblems[$count-1]);
260: # split(':',$sequences{$sequence});
261: my $uri = $cache->{$problemId.':source'};
262: my $title = $cache->{$problemId.':title'};
263:
264: my $Temp = '<a href="'.$uri.'" target="_blank">'.$title.'</a>';
265: $Str .= '<tr>';
266: $Str .= '<td> '.$count.' </td>';
267: $Str .= '<td bgcolor="#DDFFDD">'.$Temp.'</td>';
268: $Str .= '<td bgcolor="#EEFFCC">'.$uri.'</td>';
269: if($partCount{$problemId.':'.$part} < 2) {
270: $Str .= '<td><input type="submit" name="Analyze:::';
271: $Str .= $problemId.':'.$part.'" value="';
272: $Str .= 'Part '.$part;
273: $Str .= '" /></td></tr>'."\n";
274: } else {
275: my $value = $problemId.':'.$part.':'.$response;
276: $Str .= '<td><input type="submit" name="Analyze:::'.$value;
277: $Str .= '" value="';
278: $Str .= 'Part '.$part.' Response '.$response;
279: $Str .= '" /></td></tr>'."\n";
280: }
281: $count++;
282: }
283: $Str .= '</table><br>'."\n";
284: }
285:
286: return $Str;
287: }
288:
289: #---- END Problem Analysis Web Page ------------------------------------------
290:
291: #---- Analyze Web Page -------------------------------------------------------
292:
293: #restore the student submissions and finding the result
294: =pod
295: sub OpStatus {
296: my ($problemID, $student, $ConceptData, $foil_to_concept,
297: $analyzeData, $cache)=@_;
298:
299: my $ids = $analyzeData->{'parts'};
300:
301: my @True = ();
302: my @False = ();
303: my $flag=0;
304:
305: my $tries=0;
306:
307: foreach my $id (@$ids) {
308: my ($part, $response) = split(/\./, $id);
309: my $time=$cache->{$student.':'.$problemID.':'.$part.':timestamp'};
310: my @submissions = split(':::', $cache->{$student.':'.$problemID.':'.
311: $part.':'.$response.
312: ':submission'});
313: foreach my $Resp (@submissions) {
314: my %submission=&Apache::lonnet::str2hash($Resp);
315: foreach (keys(%submission)) {
316: if($submission{$_}) {
317: my $answer = $analyzeData->{$id.'.foil.value.'.$_};
318: if($submission{$_} eq $answer) {
319: &Decide("true", $foil_to_concept->{$_},
320: $time, $ConceptData);
321: } else {
322: &Decide("false", $foil_to_concept->{$_},
323: $time, $ConceptData);
324: }
325: }
326: }
327: }
328: }
329:
330: return;
331: }
332: =cut
333:
334: sub OpStatus {
335: my ($problemID, $student, $ConceptData, $foil_to_concept,
336: $analyzeData, $cache, $courseID)=@_;
337:
338: my $ids = $analyzeData->{'parts'};
339: my ($uname,$udom)=split(/\:/,$student);
340: my $symb = $cache->{$problemID.':problem'};
341:
342: my @True = ();
343: my @False = ();
344: my $flag=0;
345: my $tries=0;
346:
347: # $jr->print("<br> ID= $problemID <br> student= $student<br> prob= $symb<br>");
348:
349: foreach my $id (@$ids) {
350: my ($part, $response) = split(/\./, $id);
351: #=pod
352: my %reshash=&Apache::lonnet::restore($symb,$courseID,$udom,$uname);
353: if ($reshash{'version'}) {
354: my $tries=0;
355: #&Apache::lonhomework::showhash(%$analyzeData);
356: for (my $version=1;$version<=$reshash{'version'};$version++) {
357: my $time=$reshash{"$version:timestamp"};
358:
359: foreach my $key (sort(split(/\:/,$reshash{$version.':keys'}))) {
360: if (($key=~/\.(\w+)\.(\w+)\.submission$/)) {
361: my $Id1 = $1; my $Id2 = $2;
362: #check if this is a repeat submission, if so skip it
363: if ($reshash{"$version:resource.$Id1.previous"}) { next; }
364: #if no solved this wasn't a real submission, ignore it
365: if (!defined($reshash{"$version:resource.$Id1.solved"})) {
366: &Apache::lonxml::debug("skipping ");
367: next;
368: }
369: my $Resp = $reshash{"$version:$key"};
370: my %submission=&Apache::lonnet::str2hash($Resp);
371: foreach (keys %submission) {
372: my $Ansr = $analyzeData->{"$Id1.$Id2.foil.value.$_"};
373: if($submission{$_} eq $Ansr) {
374: &Decide("true", $foil_to_concept->{$_},
375: $time, $ConceptData);
376: } else {
377: &Decide("false", $foil_to_concept->{$_},
378: $time, $ConceptData);
379: }
380: }
381: }
382: }
383: }
384: }
385: #=cut
386: =pod
387: my $time=$cache->{$student.':'.$problemID.':'.$part.':timestamp'};
388: my @submissions = split(':::', $cache->{$student.':'.$problemID.':'.
389: $part.':'.$response.
390: ':submission'});
391: foreach my $Resp (@submissions) {
392: my %submission=&Apache::lonnet::str2hash($Resp);
393: foreach (keys(%submission)) {
394: if($submission{$_}) {
395: my $answer = $analyzeData->{$id.'.foil.value.'.$_};
396: if($submission{$_} eq $answer) {
397: &Decide("true", $foil_to_concept->{$_},
398: $time, $ConceptData);
399: } else {
400: &Decide("false", $foil_to_concept->{$_},
401: $time, $ConceptData);
402: }
403: }
404: }
405: }
406: =cut
407: }
408:
409: return;
410: }
411:
412: =pod
413: sub OpStatus {
414: my ($rid,$student,$ConceptData,$foil_to_concept,$analyzeData,$cache)=@_;
415: my ($uname,$udom)=split(/\:/,$student);
416: my $code='U';
417: $rid=~/(\d+)\.(\d+)/;
418: my $symb=&Apache::lonnet::declutter($hash{'map_id_'.$1}).'___'.$2.'___'.
419: &Apache::lonnet::declutter($hash{'src_'.$rid});
420: my %reshash=&Apache::lonnet::restore($symb,$cid,$udom,$uname);
421: my @True = ();
422: my @False = ();
423: my $flag=0;
424: if ($reshash{'version'}) {
425: my $tries=0;
426: &Apache::lonhomework::showhash(%Answer);
427: for (my $version=1;$version<=$reshash{'version'};$version++) {
428: my $time=$reshash{"$version:timestamp"};
429:
430: foreach my $key (sort(split(/\:/,$reshash{$version.':keys'}))) {
431: if (($key=~/\.(\w+)\.(\w+)\.submission$/)) {
432: my $Id1 = $1; my $Id2 = $2;
433: #check if this is a repeat submission, if so skip it
434: if ($reshash{"$version:resource.$Id1.previous"}) { next; }
435: #if no solved this wasn't a real submission, ignore it
436: if (!defined($reshash{"$version:resource.$Id1.solved"})) {
437: &Apache::lonxml::debug("skipping ");
438: next;
439: }
440: my $Resp = $reshash{"$version:$key"};
441: my %submission=&Apache::lonnet::str2hash($Resp);
442: foreach (keys %submission) {
443: my $Ansr = $Answer{"$Id1.$Id2.foil.value.$_"};
444: if ($submission{$_}) {
445: if ($submission{$_} eq $Ansr) {
446: &Decide("true",$_,$time );
447: }
448: else {&Decide("false",$_,$time );}
449: }
450: }
451: }
452: }
453: }
454: }
455: }
456: =cut
457:
458: sub DrawGraph {
459: my ($k,$Src,$Concepts,$ConceptData)=@_;
460: my $Max=0;
461: my @data1;
462: my @data2;
463:
464: # Adjust Data and find the Max
465: for (my $n=0; $n<(scalar @$Concepts); $n++ ) {
466: my $tmp=$Concepts->[$n];
467: $data1[$n]=$ConceptData->{$tmp.'.'.$k.'.true'};
468: $data2[$n]=$ConceptData->{$tmp.'.'.$k.'.false'};
469: my $Sum=$data1[$n]+$data2[$n];
470: if($Max < $Sum) {
471: $Max=$Sum;
472: }
473: }
474: for (my $n=0; $n<(scalar @$Concepts); $n++ ) {
475: if ($data1[$n]+$data2[$n]<$Max) {
476: $data2[$n]+=$Max-($data1[$n]+$data2[$n]);
477: }
478: }
479: my $P_No = (scalar @data1);
480:
481: if($Max > 1) {
482: $Max += (10 - $Max % 10);
483: $Max = int($Max);
484: } else {
485: $Max = 1;
486: }
487:
488: my $Titr=($ConceptData->{'Interval'}>1) ? $Src.'_interval_'.($k+1) : $Src;
489: # $GData=$Titr.'&Concepts'.'&'.'Answers'.'&'.$Max.'&'.$P_No.'&'.$data1.'&'.$data2;
490: my $GData = '';
491: $GData = $Titr.'&Concepts&Answers&'.$Max.'&'.$P_No.'&';
492: $GData .= (join(',',@data1)).'&'.(join(',',@data2));
493:
494: return '<IMG src="/cgi-bin/graph.gif?'.$GData.'" border=1/>';
495: }
496:
497: sub DrawTable {
498: my ($k,$Concepts,$ConceptData)=@_;
499: my $Max=0;
500: my @data1;
501: my @data2;
502: my $Correct=0;
503: my $Wrong=0;
504: for(my $n=0; $n<(scalar @$Concepts); $n++ ) {
505: my $tmp=$Concepts->[$n];
506: $data1[$n]=$ConceptData->{$tmp.'.'.$k.'.true'};
507: $Correct+=$data1[$n];
508: $data2[$n]=$ConceptData->{$tmp.'.'.$k.'.false'};
509: $Wrong+=$data2[$n];
510: my $Sum=$data1[$n]+$data2[$n];
511: if($Max < $Sum) {
512: $Max=$Sum;
513: }
514: }
515: for(my $n=0; $n<(scalar @$Concepts); $n++ ) {
516: if ($data1[$n]+$data2[$n]<$Max) {
517: $data2[$n]+=$Max-($data1[$n]+$data2[$n]);
518: }
519: }
520: my $P_No = (scalar @data1);
521: my $Str = '';
522: # $Str .= '<br><b>From: ['.localtime($ConceptData->{'Int.'.($k-1)});
523: # $Str .= '] To: ['.localtime($ConceptData->{"Int.$k"}).']</b>';
524: $Str .= "\n".'<table border=2>'.
525: "\n".'<tr>'.
526: "\n".'<th> # </th>'.
527: "\n".'<th> Concept </th>'.
528: "\n".'<th> Correct </th>'.
529: "\n".'<th> Wrong </th>'.
530: "\n".'</tr>';
531:
532: for(my $n=0; $n<(scalar @$Concepts); $n++ ) {
533: $Str .= '<tr>'."\n";
534: $Str .= '<td>'.($n+1).'</td>'."\n";
535: my ($currentConcept) = split('::',$Concepts->[$n]);
536: $Str .= '<td bgcolor="EEFFCC">'.$currentConcept;
537: $Str .= '</td>'."\n";
538: $Str .= '<td bgcolor="DDFFDD">'.$data1[$n].'</td>'."\n";
539: $Str .= '<td bgcolor="FFDDDD">'.$data2[$n].'</td>'."\n";
540: $Str .= '</tr>'."\n";
541: }
542: $Str .= '<td></td><td><b>From:['.localtime($ConceptData->{'Int.'.$k});
543: $Str .= '] To: ['.localtime($ConceptData->{'Int.'.($k+1)}-1);
544: $Str .= ']</b></td><td>'.$Correct.'</td><td>'.$Wrong.'</td>';
545: $Str .= '</table>'."\n";
546:
547: return $Str;
548: #$Apache::lonxml::debug=1;
549: #&Apache::lonhomework::showhash(%ConceptData);
550: #$Apache::lonxml::debug=0;
551: }
552:
553: #---- END Analyze Web Page ----------------------------------------------
554:
555: sub Decide {
556: #deciding the true or false answer belongs to each interval
557: my ($type,$concept,$time,$ConceptData)=@_;
558: my $k=0;
559: while($time > $ConceptData->{'Int.'.($k+1)} &&
560: $k < $ConceptData->{'Interval'}) {
561: $k++;
562: }
563: $ConceptData->{$concept.'.'.$k.'.'.$type}++;
564:
565: return;
566: }
567:
568: sub InitAnalysis {
569: my ($uri,$part,$responseId,$problem,$student,$courseID)=@_;
570: my ($name,$domain)=split(/\:/,$student);
571:
572: my %analyzeData;
573: # Render the student's view of the problem. $Answ is the problem
574: # Stringafied
575: my $Answ=&Apache::lonnet::ssi($uri,('grade_target' => 'analyze',
576: 'grade_username' => $name,
577: 'grade_domain' => $domain,
578: 'grade_courseid' => $courseID,
579: 'grade_symb' => $problem));
580: my ($Answer)=&Apache::lonnet::str2hashref($Answ);
581:
582: my $found = 0;
583: my @parts=();
584: if(defined($responseId)) {
585: foreach (@{$Answer->{'parts'}}) {
586: if($_ eq $part.'.'.$responseId) {
587: push(@parts, $_);
588: $found = 1;
589: last;
590: }
591: }
592: } else {
593: foreach (@{$Answer->{'parts'}}) {
594: if($_ =~ /$part/) {
595: push(@parts, $_);
596: $found = 1;
597: last;
598: }
599: }
600: }
601:
602: if($found == 0) {
603: $analyzeData{'error'} = 'No parts matching selected values';
604: return \%analyzeData;
605: }
606:
607: my @Concepts=();
608: my %foil_to_concept;
609: foreach my $currentPart (@parts) {
610: if(defined($Answer->{$currentPart.'.concepts'})) {
611: foreach my $concept (@{$Answer->{$currentPart.'.concepts'}}) {
612: push(@Concepts, $concept);
613: foreach my $foil (@{$Answer->{$currentPart.'.concept.'.
614: $concept}}) {
615: $analyzeData{$currentPart.'.foil.value.'.$foil} =
616: $Answer->{$currentPart.'.foil.value.'.$foil};
617: $foil_to_concept{$foil} = $concept;
618: }
619: }
620: } else {
621: foreach (keys(%$Answer)) {
622: if(/$currentPart.foil\.value\.(.*)$/) {
623: push(@Concepts, $1);
624: $foil_to_concept{$1} = $1;
625: $analyzeData{$currentPart.'.foil.value.'.$1} =
626: $Answer->{$currentPart.'.foil.value.'.$1};
627: }
628: }
629: }
630: }
631:
632: $analyzeData{'parts'} = \@parts;
633: $analyzeData{'concepts'} = \@Concepts;
634: $analyzeData{'foil_to_concept'} = \%foil_to_concept;
635:
636: return \%analyzeData;
637: }
638:
639: sub Interval {
640: my ($part,$symb,$interval,$Concepts,$ConceptData)=@_;
641: my $Int=$interval;
642: my $due = &Apache::lonnet::EXT('resource.'.$part.'.duedate',$symb);
643: my $opn = &Apache::lonnet::EXT('resource.'.$part.'.opendate',$symb);
644: my $add=int(($due-$opn)/$Int);
645: $ConceptData->{'Int.0'}=$opn;
646: for(my $i=1; $i<$Int; $i++) {
647: $ConceptData->{'Int.'.$i}=$opn+$i*$add;
648: }
649: $ConceptData->{'Int.'.$Int}=$due;
650: for(my $i=0; $i<$Int; $i++) {
651: for(my $n=0; $n<(scalar @$Concepts); $n++ ) {
652: my $tmp=$Concepts->[$n];
653: $ConceptData->{$tmp.'.'.$i.'.true'}=0;
654: $ConceptData->{$tmp.'.'.$i.'.false'}=0;
655: }
656: }
657: }
658: 1;
659: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>