File:  [LON-CAPA] / loncom / interface / Attic / lonspreadsheet.pm
Revision 1.5: download - view: text, annotated - select for diffs
Tue Dec 5 15:09:22 2000 UTC (23 years, 8 months ago) by www
Branches: MAIN
CVS tags: HEAD
Not much new ...

    1: # The LearningOnline Network with CAPA
    2: # Spreadsheet/Grades Display Handler
    3: #
    4: # 11/11,11/15,11/27,12/04,12/05 Gerd Kortemeyer
    5: 
    6: package Apache::lonspreadsheet;
    7: 
    8: use strict;
    9: use Safe;
   10: use Safe::Hole;
   11: use Opcode;
   12: use Apache::lonnet;
   13: use Apache::Constants qw(:common);
   14: use HTML::TokeParser;
   15: 
   16: # =============================================================================
   17: # ===================================== Implements an instance of a spreadsheet
   18: 
   19: sub initsheet {
   20:     my $safeeval = new Safe;
   21:     my $safehole = new Safe::Hole;
   22:     $safeeval->permit("entereval");
   23:     $safeeval->permit(":base_math");
   24:     $safeeval->permit("sort");
   25:     $safeeval->deny(":base_io");
   26:     $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');
   27:     my $code=<<'ENDDEFS';
   28: # ---------------------------------------------------- Inside of the safe space
   29: 
   30: #
   31: # f: formulas
   32: # t: intermediate format (variable references expanded)
   33: # v: output values
   34: #
   35: 
   36: %v=(); 
   37: %t=();
   38: %f=();
   39: $sheettype='';
   40: $filename='';
   41: 
   42: sub mask {
   43:     my ($lower,$upper)=@_;
   44: 
   45:     $lower=~/([A-Z]|\*)(\d+|\*)/;
   46:     my $la=$1;
   47:     my $ld=$2;
   48: 
   49:     $upper=~/([A-Z]|\*)(\d+|\*)/;
   50:     my $ua=$1;
   51:     my $ud=$2;
   52:     my $alpha='';
   53:     my $num='';
   54: 
   55:     if (($la eq '*') || ($ua eq '*')) {
   56:        $alpha='[A-Z]';
   57:     } else {
   58:        $alpha='['.$la.'-'.$ua.']';
   59:     }   
   60: 
   61:     if (($ld eq '*') || ($ud eq '*')) {
   62: 	$num='\d+';
   63:     } else {
   64:         if (length($ld)!=length($ud)) {
   65:            $num.='(';
   66: 	   map {
   67:               $num.='['.$_.'-9]';
   68:            } ($ld=~m/\d/g);
   69:            if (length($ud)-length($ld)>1) {
   70:               $num.='|\d{'.(length($ld)+1).','.(length($ud)-1).'}';
   71: 	   }
   72:            $num.='|';
   73:            map {
   74:                $num.='[0-'.$_.']';
   75:            } ($ud=~m/\d/g);
   76:            $num.=')';
   77:        } else {
   78:            my @lda=($ld=~m/\d/g);
   79:            my @uda=($ud=~m/\d/g);
   80:            my $i; $j=0;
   81:            for ($i=0;$i<=$#lda;$i++) {
   82:                if ($lda[$i]==$uda[$i]) {
   83: 		   $num.=$lda[$i];
   84:                    $j=$i;
   85:                }
   86:            }
   87:            if ($j<$#lda-1) {
   88: 	       $num.='('.$lda[$j+1];
   89:                for ($i=$j+2;$i<=$#lda;$i++) {
   90:                    $num.='['.$lda[$i].'-9]';
   91:                }
   92:                if ($uda[$j+1]-$lda[$j+1]>1) {
   93: 		   $num.='|['.($lda[$j+1]+1).'-'.($uda[$j+1]-1).']\d{'.
   94:                    ($#lda-$j-1).'}';
   95:                }
   96: 	       $num.='|'.$uda[$j+1];
   97:                for ($i=$j+2;$i<=$#uda;$i++) {
   98:                    $num.='[0-'.$uda[$i].']';
   99:                }
  100:                $num.=')';
  101:            } else {
  102:                $num.='['.$lda[$#lda].'-'.$uda[$#uda].']';
  103:            }
  104:        }
  105:     }
  106:     return '^'.$alpha.$num."\$";
  107: }
  108: 
  109: sub NUM {
  110:     my $mask=mask(@_);
  111:     my $num=0;
  112:     map {
  113:         $num++;
  114:     } grep /$mask/,keys %v;
  115:     return $num;   
  116: }
  117: 
  118: sub BIN {
  119:     my ($low,$high,$lower,$upper)=@_;
  120:     my $mask=mask($lower,$upper);
  121:     my $num=0;
  122:     map {
  123:         if (($v{$_}>=$low) && ($v{$_}<=$high)) {
  124:             $num++;
  125:         }
  126:     } grep /$mask/,keys %v;
  127:     return $num;   
  128: }
  129: 
  130: 
  131: sub SUM {
  132:     my $mask=mask(@_);
  133:     my $sum=0;
  134:     map {
  135:         $sum+=$v{$_};
  136:     } grep /$mask/,keys %v;
  137:     return $sum;   
  138: }
  139: 
  140: sub MEAN {
  141:     my $mask=mask(@_);
  142:     my $sum=0; my $num=0;
  143:     map {
  144:         $sum+=$v{$_};
  145:         $num++;
  146:     } grep /$mask/,keys %v;
  147:     if ($num) {
  148:        return $sum/$num;
  149:     } else {
  150:        return undef;
  151:     }   
  152: }
  153: 
  154: sub STDDEV {
  155:     my $mask=mask(@_);
  156:     my $sum=0; my $num=0;
  157:     map {
  158:         $sum+=$v{$_};
  159:         $num++;
  160:     } grep /$mask/,keys %v;
  161:     unless ($num>1) { return undef; }
  162:     my $mean=$sum/$num;
  163:     $sum=0;
  164:     map {
  165:         $sum+=($v{$_}-$mean)**2;
  166:     } grep /$mask/,keys %v;
  167:     return sqrt($sum/($num-1));    
  168: }
  169: 
  170: sub PROD {
  171:     my $mask=mask(@_);
  172:     my $prod=1;
  173:     map {
  174:         $prod*=$v{$_};
  175:     } grep /$mask/,keys %v;
  176:     return $prod;   
  177: }
  178: 
  179: sub MAX {
  180:     my $mask=mask(@_);
  181:     my $max='-';
  182:     map {
  183:         unless ($max) { $max=$v{$_}; }
  184:         if (($v{$_}>$max) || ($max eq '-')) { $max=$v{$_}; }
  185:     } grep /$mask/,keys %v;
  186:     return $max;   
  187: }
  188: 
  189: sub MIN {
  190:     my $mask=mask(@_);
  191:     my $min='-';
  192:     map {
  193:         unless ($max) { $max=$v{$_}; }
  194:         if (($v{$_}<$min) || ($min eq '-')) { $min=$v{$_}; }
  195:     } grep /$mask/,keys %v;
  196:     return $min;   
  197: }
  198: 
  199: sub SUMMAX {
  200:     my ($num,$lower,$upper)=@_;
  201:     my $mask=mask($lower,$upper);
  202:     my @inside=();
  203:     map {
  204: 	$inside[$#inside+1]=$v{$_};
  205:     } grep /$mask/,keys %v;
  206:     @inside=sort(@inside);
  207:     my $sum=0; my $i;
  208:     for ($i=$#inside;(($i>$#inside-$num) && ($i>=0));$i--) { 
  209:         $sum+=$inside[$i];
  210:     }
  211:     return $sum;   
  212: }
  213: 
  214: sub SUMMIN {
  215:     my ($num,$lower,$upper)=@_;
  216:     my $mask=mask($lower,$upper);
  217:     my @inside=();
  218:     map {
  219: 	$inside[$#inside+1]=$v{$_};
  220:     } grep /$mask/,keys %v;
  221:     @inside=sort(@inside);
  222:     my $sum=0; my $i;
  223:     for ($i=0;(($i<$num) && ($i<=$#inside));$i++) { 
  224:         $sum+=$inside[$i];
  225:     }
  226:     return $sum;   
  227: }
  228: 
  229: sub sett {
  230:     %t=();
  231:     map {
  232: 	if ($f{$_}) {
  233: 	    $t{$_}=$f{$_};
  234:             $t{$_}=~s/\.+/\,/g;
  235:             $t{$_}=~s/(^|[^\"\'])([A-Z]\d+)/$1\$v\{\'$2\'\}/g;
  236:         }
  237:     } keys %f;
  238: }
  239: 
  240: sub calc {
  241:     %v=();
  242:     &sett();
  243:     my $notfinished=1;
  244:     my $depth=0;
  245:     while ($notfinished) {
  246: 	$notfinished=0;
  247:         map {
  248:             my $old=$v{$_};
  249:             $v{$_}=eval($t{$_});
  250: 	    if ($@) {
  251: 		%v=();
  252:                 return $@;
  253:             }
  254: 	    if ($v{$_} ne $old) { $notfinished=1; }
  255:         } keys %t;
  256:         $depth++;
  257:         if ($depth>100) {
  258: 	    %v=();
  259:             return 'Maximum calculation depth exceeded';
  260:         }
  261:     }
  262:     return '';
  263: }
  264: 
  265: # ------------------------------------------- End of "Inside of the safe space"
  266: ENDDEFS
  267:     $safeeval->reval($code);
  268:     return $safeeval;
  269: }
  270: 
  271: # ------------------------------------------------ Add or change formula values
  272: 
  273: sub setformulas {
  274:     my ($safeeval,@f)=@_;
  275:     $safeeval->reval('%f=(%f,'."('".join("','",@f)."'));");
  276: }
  277: 
  278: # ------------------------------------------------------- Calculate spreadsheet
  279: 
  280: sub calcsheet {
  281:     my $safeeval=shift;
  282:     $safeeval->reval('&calc();');
  283: }
  284: 
  285: # ------------------------------------------------------------------ Get values
  286: 
  287: sub getvalues {
  288:     my $safeeval=shift;
  289:     return $safeeval->reval('%v');
  290: }
  291: 
  292: # ---------------------------------------------------------------- Get formulas
  293: 
  294: sub getformulas {
  295:     my $safeeval=shift;
  296:     return $safeeval->reval('%f');
  297: }
  298: 
  299: # -------------------------------------------------------------------- Set type
  300: 
  301: sub settype {
  302:     my ($safeeval,$type)=@_;
  303:     $safeeval->reval('$sheettype='.$type.';');
  304: }
  305: 
  306: # -------------------------------------------------------------------- Get type
  307: 
  308: sub gettype {
  309:     my $safeeval=shift;
  310:     return $safeeval->reval('$sheettype');
  311: }
  312: 
  313: # -------------------------------------------------------------------- Set type
  314: 
  315: sub setfilename {
  316:     my ($safeeval,$fn)=@_;
  317:     $safeeval->reval('$filename='.$fn.';');
  318: }
  319: 
  320: # -------------------------------------------------------------------- Get type
  321: 
  322: sub getfilename {
  323:     my $safeeval=shift;
  324:     return $safeeval->reval('$filename');
  325: }
  326:     
  327: # ========================================================== End of Spreadsheet
  328: # =============================================================================
  329: 
  330: 
  331: 
  332: # --------------------------------------- Read spreadsheet formulas from a file
  333: 
  334: sub readsheet {
  335:     my ($safeeval,$fn)=shift;
  336:     &setfilename($safeeval,$fn);
  337:     $fn=~/\.(\w+)/;
  338:     &settype($safeeval,$1);
  339:     my %f=();
  340:     my $content;
  341:     {
  342:       my $fh=Apache::File->new($fn);
  343:       $content=join('',<$fh>);
  344:     }
  345:     {
  346:       my $parser=HTML::TokeParser->new(\$content);
  347:       my $token;
  348:       while ($token=$parser->get_token) {
  349:          if ($token->[0] eq 'S') {
  350: 	     if ($token->[1] eq 'field') {
  351: 		 $f{$token->[2]->{'col'}.$token->[2]->{'row'}}=
  352: 		     $parser->get_text('/field');
  353: 	     }
  354:          }
  355:       }
  356:     }
  357:     &setformulas($safeeval,%f);
  358: }
  359: 
  360: # --------------------------------------------------------------- Read metadata
  361: 
  362: sub readmeta {
  363:     my $fn=shift;
  364:     unless ($fn=~/\.meta$/) { $fn.='meta'; }
  365:     my $content;
  366:     my %returnhash=();
  367:     {
  368:       my $fh=Apache::File->new($fn);
  369:       $content=join('',<$fh>);
  370:     }
  371:    my $parser=HTML::TokeParser->new(\$content);
  372:    my $token;
  373:    while ($token=$parser->get_token) {
  374:       if ($token->[0] eq 'S') {
  375:          my $entry=$token->[1];
  376:          if (($entry eq 'stores') || ($entry eq 'parameter')) {
  377:              my $unikey=$entry;
  378:              $unikey.='_'.$token->[2]->{'part'}; 
  379:              $unikey.='_'.$token->[2]->{'name'}; 
  380:              $returnhash{$unikey}=$token->[2]->{'display'};
  381:          }
  382:      }
  383:   }
  384:     return %returnhash;
  385: }
  386: 
  387: # ----------------------------------------------------------------- Update rows
  388: 
  389: sub updaterows {
  390:     my $safeeval=shift;
  391:     my %bighash;
  392: # -------------------------------------------------------------------- Tie hash
  393:       if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',
  394:                        &GDBM_READER,0640)) {
  395: # --------------------------------------------------------- Get all assessments
  396: 
  397: 	my %allkeys=();
  398:         my %allassess=();
  399: 
  400:         my $stype=&gettype($safeeval);
  401: 
  402:         map {
  403: 	    if ($_=~/^src\_(\d+)\.(\d+)$/) {
  404: 	       my $mapid=$1;
  405:                my $resid=$2;
  406:                my $id=$mapid.'.'.$resid;
  407:                my $srcf=$bighash{$_};
  408:                if ($srcf=~/\.(problem|exam|quiz|assess|survey|form)$/) {
  409:                  my $symb=
  410:                      &Apache::lonnet::declutter($bighash{'map_id_'.$mapid}).
  411: 			    '___'.$resid.'___'.
  412: 			    &Apache::lonnet::declutter($srcf);
  413: 		 $allassess{$symb}=$bighash{'title_'.$id};
  414:                  if ($stype eq 'assesssheet') {
  415:                    map {
  416:                        if ($_=~/^stores\_(.*)/) {
  417: 			  my $key=$_;
  418:                           my $display=
  419: 			      &Apache::lonnet::metadata($srcf,$key.'.display');
  420:                           unless ($display) {
  421:                               $display=
  422: 			         &Apache::lonnet::metadata($srcf,$key.'.name');
  423:                           }
  424:                           $allkeys{$key}=$display;
  425: 		       }
  426:                    } split(/\,/,&Apache::lonnet::metadata($srcf,'keys'));
  427: 	         }
  428: 	      }
  429: 	   }
  430:         } keys %bighash;
  431:         untie(%bighash);
  432:     
  433: #
  434: # %allkeys has a list of storage displays by unikey
  435: # %allassess has a list of all resource displays by symb
  436: #
  437: # -------------------- Find discrepancies between the course row table and this
  438: #
  439:         my %f=&getformulas($safeeval);
  440: 	map {
  441: 	    if ($_=~/^A/) {
  442: 		if ($stype eq 'assesssheet') {
  443:                 } elsif ($stype eq 'coursesheet') {
  444:                 }
  445:             }
  446:         } keys %f;
  447: # ------------------------------------------------ Find new and obsolete values
  448: 
  449:     } else {
  450:         return 'Could not access course data';
  451:     }
  452: }
  453: 
  454: # -----------------------------------------------------------------------------
  455: 
  456: sub handler {
  457: 
  458:     my $r=shift;
  459: 
  460:   $r->content_type('text/html');
  461:   $r->send_http_header;
  462: 
  463:   $r->print('<html><head><title>LON-CAPA Spreadsheet</title></head>');
  464:   $r->print('<body bgcolor="#FFFFFF">');
  465:  
  466:     my $sheetone=initsheet();
  467: 
  468:     &setformulas($sheetone,('A1' => '5', 'B2' => '6', 'C4' => 'A1+B2'));
  469:     $r->print(&calcsheet($sheetone));
  470:     my %output=&getformulas($sheetone);
  471:     
  472:     $r->print('FORM:'.$output{'A1'}.' '.$output{'B2'}.' '.$output{'C4'});
  473: 
  474:     my %output=&getvalues($sheetone);
  475:     
  476:     $r->print('<br>OUT:'.$output{'A1'}.' '.$output{'B2'}.' '.$output{'C4'});
  477: 
  478:     $r->print('</body></html>');
  479:     return OK;
  480: }
  481: 
  482: 1;
  483: __END__
  484: 
  485: 
  486: 
  487: 
  488: 
  489: 
  490: 
  491: 
  492: 
  493: 
  494: 
  495: 
  496: 
  497: 
  498: 
  499: 

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