Annotation of loncom/interface/lonspreadsheet.pm, revision 1.2

1.1       www         1: # The LearningOnline Network with CAPA
                      2: # Spreadsheet/Grades Display Handler
                      3: #
1.2     ! www         4: # 11/11,11/15,11/27 Gerd Kortemeyer
1.1       www         5: 
                      6: package Apache::lonspreadsheet;
                      7: 
                      8: use strict;
                      9: use Safe;
                     10: use Opcode;
                     11: use vars qw(%v %t %f);
                     12: use Apache::lonnet;
                     13: use Apache::Constants qw(:common);
                     14: 
                     15: sub deffunc {
                     16:     my $safeeval=shift;
                     17:     my $code=<<'ENDDEFS';
                     18: # ---------------------------------------------------- Inside of the safe space
                     19: 
                     20: sub mask {
                     21:     my ($lower,$upper)=@_;
                     22: 
                     23:     $lower=~/([A-Z]|\*)(\d+|\*)/;
                     24:     my $la=$1;
                     25:     my $ld=$2;
                     26: 
                     27:     $upper=~/([A-Z]|\*)(\d+|\*)/;
                     28:     my $ua=$1;
                     29:     my $ud=$2;
                     30:     my $alpha='';
                     31:     my $num='';
                     32: 
                     33:     if (($la eq '*') || ($ua eq '*')) {
                     34:        $alpha='[A-Z]';
                     35:     } else {
                     36:        $alpha='['.$la.'-'.$ua.']';
                     37:     }   
                     38: 
                     39:     if (($ld eq '*') || ($ud eq '*')) {
                     40: 	$num='\d+';
                     41:     } else {
                     42:         if (length($ld)!=length($ud)) {
                     43:            $num.='(';
                     44: 	   map {
                     45:               $num.='['.$_.'-9]';
                     46:            } ($ld=~m/\d/g);
                     47:            if (length($ud)-length($ld)>1) {
                     48:               $num.='|\d{'.(length($ld)+1).','.(length($ud)-1).'}';
                     49: 	   }
                     50:            $num.='|';
                     51:            map {
                     52:                $num.='[0-'.$_.']';
                     53:            } ($ud=~m/\d/g);
                     54:            $num.=')';
                     55:        } else {
                     56:            my @lda=($ld=~m/\d/g);
                     57:            my @uda=($ud=~m/\d/g);
                     58:            my $i; $j=0;
                     59:            for ($i=0;$i<=$#lda;$i++) {
                     60:                if ($lda[$i]==$uda[$i]) {
                     61: 		   $num.=$lda[$i];
                     62:                    $j=$i;
                     63:                }
                     64:            }
                     65:            if ($j<$#lda-1) {
                     66: 	       $num.='('.$lda[$j+1];
                     67:                for ($i=$j+2;$i<=$#lda;$i++) {
                     68:                    $num.='['.$lda[$i].'-9]';
                     69:                }
                     70:                if ($uda[$j+1]-$lda[$j+1]>1) {
                     71: 		   $num.='|['.($lda[$j+1]+1).'-'.($uda[$j+1]-1).']\d{'.
                     72:                    ($#lda-$j-1).'}';
                     73:                }
                     74: 	       $num.='|'.$uda[$j+1];
                     75:                for ($i=$j+2;$i<=$#uda;$i++) {
                     76:                    $num.='[0-'.$uda[$i].']';
                     77:                }
                     78:                $num.=')';
                     79:            } else {
                     80:                $num.='['.$lda[$#lda].'-'.$uda[$#uda].']';
                     81:            }
                     82:        }
                     83:     }
                     84:     return '^'.$alpha.$num.'$';
                     85: }
                     86: 
                     87: 
                     88: sub NUM {
                     89:     my $mask=mask(@_);
                     90:     my $num=0;
                     91:     map {
                     92:         $num++;
                     93:     } grep /$mask/,keys %v;
                     94:     return $num;   
                     95: }
                     96: 
                     97: sub BIN {
                     98:     my ($low,$high,$lower,$upper)=@_;
                     99:     my $mask=mask($lower,$upper);
                    100:     my $num=0;
                    101:     map {
                    102:         if (($v{$_}>=$low) && ($v{$_}<=$high)) {
                    103:             $num++;
                    104:         }
                    105:     } grep /$mask/,keys %v;
                    106:     return $num;   
                    107: }
                    108: 
                    109: 
                    110: sub SUM {
                    111:     my $mask=mask(@_);
                    112:     my $sum=0;
                    113:     map {
                    114:         $sum+=$v{$_};
                    115:     } grep /$mask/,keys %v;
                    116:     return $sum;   
                    117: }
                    118: 
                    119: sub MEAN {
                    120:     my $mask=mask(@_);
                    121:     my $sum=0; my $num=0;
                    122:     map {
                    123:         $sum+=$v{$_};
                    124:         $num++;
                    125:     } grep /$mask/,keys %v;
                    126:     if ($num) {
                    127:        return $sum/$num;
                    128:     } else {
                    129:        return undef;
                    130:     }   
                    131: }
                    132: 
                    133: sub STDDEV {
                    134:     my $mask=mask(@_);
                    135:     my $sum=0; my $num=0;
                    136:     map {
                    137:         $sum+=$v{$_};
                    138:         $num++;
                    139:     } grep /$mask/,keys %v;
                    140:     unless ($num>1) { return undef; }
                    141:     my $mean=$sum/$num;
                    142:     $sum=0;
                    143:     map {
                    144:         $sum+=($v{$_}-$mean)**2;
                    145:     } grep /$mask/,keys %v;
                    146:     return sqrt($sum/($num-1));    
                    147: }
                    148: 
                    149: sub PROD {
                    150:     my $mask=mask(@_);
                    151:     my $prod=1;
                    152:     map {
                    153:         $prod*=$v{$_};
                    154:     } grep /$mask/,keys %v;
                    155:     return $prod;   
                    156: }
                    157: 
                    158: sub MAX {
                    159:     my $mask=mask(@_);
                    160:     my $max='-';
                    161:     map {
                    162:         unless ($max) { $max=$v{$_}; }
                    163:         if (($v{$_}>$max) || ($max eq '-')) { $max=$v{$_}; }
                    164:     } grep /$mask/,keys %v;
                    165:     return $max;   
                    166: }
                    167: 
                    168: sub MIN {
                    169:     my $mask=mask(@_);
                    170:     my $min='-';
                    171:     map {
                    172:         unless ($max) { $max=$v{$_}; }
                    173:         if (($v{$_}<$min) || ($min eq '-')) { $min=$v{$_}; }
                    174:     } grep /$mask/,keys %v;
                    175:     return $min;   
                    176: }
                    177: 
                    178: sub SUMMAX {
                    179:     my ($num,$lower,$upper)=@_;
                    180:     my $mask=mask($lower,$upper);
                    181:     my @inside=();
                    182:     map {
                    183: 	$inside[$#inside+1]=$v{$_};
                    184:     } grep /$mask/,keys %v;
                    185:     @inside=sort(@inside);
                    186:     my $sum=0; my $i;
                    187:     for ($i=$#inside;(($i>$#inside-$num) && ($i>=0));$i--) { 
                    188:         $sum+=$inside[$i];
                    189:     }
                    190:     return $sum;   
                    191: }
                    192: 
                    193: sub SUMMIN {
                    194:     my ($num,$lower,$upper)=@_;
                    195:     my $mask=mask($lower,$upper);
                    196:     my @inside=();
                    197:     map {
                    198: 	$inside[$#inside+1]=$v{$_};
                    199:     } grep /$mask/,keys %v;
                    200:     @inside=sort(@inside);
                    201:     my $sum=0; my $i;
                    202:     for ($i=0;(($i<$num) && ($i<=$#inside));$i++) { 
                    203:         $sum+=$inside[$i];
                    204:     }
                    205:     return $sum;   
                    206: }
                    207: 
                    208: 
                    209: # ------------------------------------------- End of "Inside of the safe space"
                    210: ENDDEFS
                    211:     $safeeval->reval($code);
                    212: }
                    213: 
                    214: sub sett {
                    215:     %t=();
                    216:     map {
                    217: 	if ($f{$_}) {
                    218: 	    $t{$_}=$f{$_};
                    219:             $t{$_}=~s/\.+/\,/g;
                    220:             $t{$_}=~s/(^|[^\"\'])([A-Z]\d+)/$1\$v\{\'$2\'\}/g;
                    221:         }
                    222:     } keys %f;
                    223: }
                    224: 
                    225: sub calcv {
                    226:     my $safeeval=shift;
                    227:     %v=();
                    228:     my $notfinished=1;
                    229:     my $depth=0;
                    230:     while ($notfinished) {
                    231: 	$notfinished=0;
                    232:         map {
                    233:             my $old=$v{$_};
                    234:             $v{$_}=$safeeval->reval($t{$_});
                    235: 	    if ($@) {
                    236: 		%v=();
                    237:                 return $@;
                    238:             }
                    239: 	    if ($v{$_} ne $old) { $notfinished=1; }
                    240:         } keys %t;
                    241:         $depth++;
                    242:         if ($depth>100) {
                    243: 	    %v=();
                    244:             return 'Maximum calculation depth exceeded';
                    245:         }
                    246:     }
                    247:     return '';
                    248: }
                    249: 
                    250: sub handler {
                    251:     %v=();
                    252:     %t=();
                    253:     %f=();
                    254:     my $safeeval = new Safe;
1.2     ! www       255:     my $safehole = new Safe::Hole;
1.1       www       256:     $safeeval->permit("entereval");
                    257:     $safeeval->permit(":base_math");
                    258:     $safeeval->permit("sort");
                    259:     $safeeval->deny(":base_io");
1.2     ! www       260:     $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');
1.1       www       261:     $safeeval->share('%v','%t','%f');
                    262:     &deffunc($safeeval);
                    263:     $f{'A3'}=5;
                    264:     $f{'A4'}=3;
                    265:     $f{'A5'}=8;
                    266:     $f{'E100'}=5;
                    267:     $f{'C3'}='A3+6';
                    268:     $f{'B4'}='8+int(C3/3)';
                    269:     $f{'C7'}='A3+B4';
                    270:     $f{'G8'}='MEAN("E*")';
                    271:     $f{'G5'}='A3+SUMMIN(2,"A*")';
                    272:     $f{'G6'}='A3+SUM("A*")';
                    273:     $f{'G7'}='STDDEV("A*")';
                    274:     $f{'G9'}='NUM("G*")';
                    275:     $f{'H10'}='MEAN("E*")';
                    276:     $f{'G10'}='BIN(3,5,"A*")';
                    277:     &sett();
                    278:     print &calcv($safeeval)."\n";
                    279:     print $v{'C7'}."\n";
                    280:     print $t{'G5'}.' - '.$v{'G5'}."\n";
                    281:     print $t{'G6'}.' - '.$v{'G6'}."\n";
                    282:     print $t{'G7'}.' - '.$v{'G7'}."\n";
                    283:     print $t{'G8'}.' - '.$v{'G8'}."\n";
                    284:     print $t{'G9'}.' - '.$v{'G9'}."\n";
                    285:     print $t{'G10'}.' - '.$v{'G10'}."\n";
                    286: }
                    287: 
                    288: 1;
                    289: __END__
                    290: 
                    291: 
                    292: 
                    293: 
                    294: 
                    295: 
                    296: 
                    297: 
                    298: 
                    299: 
                    300: 
                    301: 
                    302: 
                    303: 
                    304: 
                    305: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>