Annotation of loncom/interface/lonspreadsheet.pm, revision 1.51
1.1 www 1: # The LearningOnline Network with CAPA
2: # Spreadsheet/Grades Display Handler
3: #
1.15 www 4: # 11/11,11/15,11/27,12/04,12/05,12/06,12/07,
1.23 www 5: # 12/08,12/09,12/11,12/12,12/15,12/16,12/18,12/19,12/30,
1.39 www 6: # 01/01/01,02/01,03/01,19/01,20/01,22/01,
1.47 www 7: # 03/05,03/08,03/10,03/12,03/13,03/15,03/17,
1.51 ! www 8: # 03/19,03/20,03/21 Gerd Kortemeyer
1.1 www 9:
10: package Apache::lonspreadsheet;
1.36 www 11:
1.1 www 12: use strict;
13: use Safe;
1.3 www 14: use Safe::Hole;
1.1 www 15: use Opcode;
16: use Apache::lonnet;
1.7 www 17: use Apache::Constants qw(:common :http);
1.19 www 18: use GDBM_File;
1.3 www 19: use HTML::TokeParser;
20:
1.11 www 21: #
1.44 www 22: # Caches for previously calculated spreadsheets
23: #
24:
25: my %oldsheets;
1.46 www 26: my %loadedcaches;
1.47 www 27: my %expiredates;
1.44 www 28:
29: #
1.39 www 30: # Cache for stores of an individual user
31: #
32:
33: my $cachedassess;
34: my %cachedstores;
35:
36: #
1.11 www 37: # These cache hashes need to be independent of user, resource and course
1.27 www 38: # (user and course can/should be in the keys)
1.11 www 39: #
1.33 www 40:
41: my %spreadsheets;
42: my %courserdatas;
43: my %userrdatas;
44: my %defaultsheets;
1.35 www 45: my %updatedata;
1.27 www 46:
1.11 www 47: #
48: # These global hashes are dependent on user, course and resource,
49: # and need to be initialized every time when a sheet is calculated
50: #
51: my %courseopt;
52: my %useropt;
53: my %parmhash;
54:
1.28 www 55: # Stuff that only the screen handler can know
56:
57: my $includedir;
58: my $tmpdir;
59:
1.5 www 60: # =============================================================================
61: # ===================================== Implements an instance of a spreadsheet
1.4 www 62:
63: sub initsheet {
1.36 www 64: my $safeeval = new Safe(shift);
1.4 www 65: my $safehole = new Safe::Hole;
66: $safeeval->permit("entereval");
67: $safeeval->permit(":base_math");
68: $safeeval->permit("sort");
69: $safeeval->deny(":base_io");
70: $safehole->wrap(\&Apache::lonnet::EXT,$safeeval,'&EXT');
71: my $code=<<'ENDDEFS';
72: # ---------------------------------------------------- Inside of the safe space
73:
1.3 www 74: #
75: # f: formulas
1.4 www 76: # t: intermediate format (variable references expanded)
77: # v: output values
1.6 www 78: # c: preloaded constants (A-column)
79: # rl: row label
1.3 www 80:
1.36 www 81: undef %v;
82: undef %t;
83: undef %f;
84: undef %c;
85: undef %rl;
1.6 www 86:
87: $maxrow=0;
1.5 www 88: $sheettype='';
1.27 www 89:
90: # filename/reference of the sheet
91:
1.5 www 92: $filename='';
1.1 www 93:
1.27 www 94: # user data
95: $uname='';
96: $uhome='';
97: $udom='';
98:
99: # course data
100:
101: $csec='';
102: $chome='';
103: $cnum='';
104: $cdom='';
1.28 www 105: $cid='';
1.29 www 106: $cfn='';
1.27 www 107:
108: # symb
109:
110: $usymb='';
111:
1.1 www 112: sub mask {
113: my ($lower,$upper)=@_;
114:
1.7 www 115: $lower=~/([A-Za-z]|\*)(\d+|\*)/;
1.1 www 116: my $la=$1;
117: my $ld=$2;
118:
1.7 www 119: $upper=~/([A-Za-z]|\*)(\d+|\*)/;
1.1 www 120: my $ua=$1;
121: my $ud=$2;
122: my $alpha='';
123: my $num='';
124:
125: if (($la eq '*') || ($ua eq '*')) {
1.7 www 126: $alpha='[A-Za-z]';
1.1 www 127: } else {
1.7 www 128: if (($la=~/[A-Z]/) && ($ua=~/[A-Z]/) ||
129: ($la=~/[a-z]/) && ($ua=~/[a-z]/)) {
130: $alpha='['.$la.'-'.$ua.']';
131: } else {
132: $alpha='['.$la.'-Za-'.$ua.']';
133: }
1.1 www 134: }
135:
136: if (($ld eq '*') || ($ud eq '*')) {
137: $num='\d+';
138: } else {
139: if (length($ld)!=length($ud)) {
140: $num.='(';
141: map {
142: $num.='['.$_.'-9]';
143: } ($ld=~m/\d/g);
144: if (length($ud)-length($ld)>1) {
145: $num.='|\d{'.(length($ld)+1).','.(length($ud)-1).'}';
146: }
147: $num.='|';
148: map {
149: $num.='[0-'.$_.']';
150: } ($ud=~m/\d/g);
151: $num.=')';
152: } else {
153: my @lda=($ld=~m/\d/g);
154: my @uda=($ud=~m/\d/g);
1.7 www 155: my $i; $j=0; $notdone=1;
156: for ($i=0;($i<=$#lda)&&($notdone);$i++) {
1.1 www 157: if ($lda[$i]==$uda[$i]) {
158: $num.=$lda[$i];
159: $j=$i;
1.7 www 160: } else {
161: $notdone=0;
1.1 www 162: }
163: }
164: if ($j<$#lda-1) {
165: $num.='('.$lda[$j+1];
166: for ($i=$j+2;$i<=$#lda;$i++) {
167: $num.='['.$lda[$i].'-9]';
168: }
169: if ($uda[$j+1]-$lda[$j+1]>1) {
170: $num.='|['.($lda[$j+1]+1).'-'.($uda[$j+1]-1).']\d{'.
171: ($#lda-$j-1).'}';
172: }
173: $num.='|'.$uda[$j+1];
174: for ($i=$j+2;$i<=$#uda;$i++) {
175: $num.='[0-'.$uda[$i].']';
176: }
177: $num.=')';
178: } else {
1.7 www 179: if ($lda[$#lda]!=$uda[$#uda]) {
180: $num.='['.$lda[$#lda].'-'.$uda[$#uda].']';
181: }
1.1 www 182: }
183: }
184: }
1.4 www 185: return '^'.$alpha.$num."\$";
1.1 www 186: }
187:
188: sub NUM {
189: my $mask=mask(@_);
190: my $num=0;
191: map {
192: $num++;
193: } grep /$mask/,keys %v;
194: return $num;
195: }
196:
197: sub BIN {
198: my ($low,$high,$lower,$upper)=@_;
199: my $mask=mask($lower,$upper);
200: my $num=0;
201: map {
202: if (($v{$_}>=$low) && ($v{$_}<=$high)) {
203: $num++;
204: }
205: } grep /$mask/,keys %v;
206: return $num;
207: }
208:
209:
210: sub SUM {
211: my $mask=mask(@_);
212: my $sum=0;
213: map {
214: $sum+=$v{$_};
215: } grep /$mask/,keys %v;
216: return $sum;
217: }
218:
219: sub MEAN {
220: my $mask=mask(@_);
221: my $sum=0; my $num=0;
222: map {
223: $sum+=$v{$_};
224: $num++;
225: } grep /$mask/,keys %v;
226: if ($num) {
227: return $sum/$num;
228: } else {
229: return undef;
230: }
231: }
232:
233: sub STDDEV {
234: my $mask=mask(@_);
235: my $sum=0; my $num=0;
236: map {
237: $sum+=$v{$_};
238: $num++;
239: } grep /$mask/,keys %v;
240: unless ($num>1) { return undef; }
241: my $mean=$sum/$num;
242: $sum=0;
243: map {
244: $sum+=($v{$_}-$mean)**2;
245: } grep /$mask/,keys %v;
246: return sqrt($sum/($num-1));
247: }
248:
249: sub PROD {
250: my $mask=mask(@_);
251: my $prod=1;
252: map {
253: $prod*=$v{$_};
254: } grep /$mask/,keys %v;
255: return $prod;
256: }
257:
258: sub MAX {
259: my $mask=mask(@_);
260: my $max='-';
261: map {
262: unless ($max) { $max=$v{$_}; }
263: if (($v{$_}>$max) || ($max eq '-')) { $max=$v{$_}; }
264: } grep /$mask/,keys %v;
265: return $max;
266: }
267:
268: sub MIN {
269: my $mask=mask(@_);
270: my $min='-';
271: map {
272: unless ($max) { $max=$v{$_}; }
273: if (($v{$_}<$min) || ($min eq '-')) { $min=$v{$_}; }
274: } grep /$mask/,keys %v;
275: return $min;
276: }
277:
278: sub SUMMAX {
279: my ($num,$lower,$upper)=@_;
280: my $mask=mask($lower,$upper);
281: my @inside=();
282: map {
283: $inside[$#inside+1]=$v{$_};
284: } grep /$mask/,keys %v;
285: @inside=sort(@inside);
286: my $sum=0; my $i;
287: for ($i=$#inside;(($i>$#inside-$num) && ($i>=0));$i--) {
288: $sum+=$inside[$i];
289: }
290: return $sum;
291: }
292:
293: sub SUMMIN {
294: my ($num,$lower,$upper)=@_;
295: my $mask=mask($lower,$upper);
296: my @inside=();
297: map {
298: $inside[$#inside+1]=$v{$_};
299: } grep /$mask/,keys %v;
300: @inside=sort(@inside);
301: my $sum=0; my $i;
302: for ($i=0;(($i<$num) && ($i<=$#inside));$i++) {
303: $sum+=$inside[$i];
304: }
305: return $sum;
306: }
307:
308: sub sett {
309: %t=();
1.16 www 310: my $pattern='';
311: if ($sheettype eq 'assesscalc') {
312: $pattern='A';
313: } else {
314: $pattern='[A-Z]';
315: }
1.1 www 316: map {
1.20 www 317: if ($_=~/template\_(\w)/) {
318: my $col=$1;
319: unless ($col=~/^$pattern/) {
320: map {
321: if ($_=~/A(\d+)/) {
322: my $trow=$1;
323: if ($trow) {
324: my $lb=$col.$trow;
325: $t{$lb}=$f{'template_'.$col};
326: $t{$lb}=~s/\#/$trow/g;
327: $t{$lb}=~s/\.\.+/\,/g;
328: $t{$lb}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;
1.40 www 329: $t{$lb}=~s/(^|[^\"\'])\[(\w+)\]/$1\$c\{\'$2\'\}/g;
1.20 www 330: }
331: }
332: } keys %f;
333: }
334: }
335: } keys %f;
336: map {
337: if (($f{$_}) && ($_!~/template\_/)) {
1.42 www 338: my $matches=($_=~/^$pattern(\d+)/);
339: if (($matches) && ($1)) {
1.6 www 340: unless ($f{$_}=~/^\!/) {
341: $t{$_}=$c{$_};
342: }
343: } else {
344: $t{$_}=$f{$_};
1.7 www 345: $t{$_}=~s/\.\.+/\,/g;
346: $t{$_}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;
1.38 www 347: $t{$_}=~s/(^|[^\"\'])\[([\w\.]+)\]/$1\$c\{\'$2\'\}/g;
1.6 www 348: }
1.1 www 349: }
350: } keys %f;
1.17 www 351: $t{'A0'}=$f{'A0'};
352: $t{'A0'}=~s/\.\.+/\,/g;
353: $t{'A0'}=~s/(^|[^\"\'])([A-Za-z]\d+)/$1\$v\{\'$2\'\}/g;
1.38 www 354: $t{'A0'}=~s/(^|[^\"\'])\[([\w\.]+)\]/$1\$c\{\'$2\'\}/g;
1.1 www 355: }
356:
1.4 www 357: sub calc {
1.1 www 358: %v=();
1.4 www 359: &sett();
1.1 www 360: my $notfinished=1;
361: my $depth=0;
362: while ($notfinished) {
363: $notfinished=0;
364: map {
365: my $old=$v{$_};
1.4 www 366: $v{$_}=eval($t{$_});
1.1 www 367: if ($@) {
368: %v=();
369: return $@;
370: }
371: if ($v{$_} ne $old) { $notfinished=1; }
372: } keys %t;
373: $depth++;
374: if ($depth>100) {
375: %v=();
376: return 'Maximum calculation depth exceeded';
377: }
378: }
379: return '';
380: }
381:
1.21 www 382: sub templaterow {
383: my @cols=();
384: $cols[0]='<b><font size=+1>Template</font></b>';
385: map {
386: my $fm=$f{'template_'.$_};
387: $fm=~s/[\'\"]/\&\#34;/g;
388: $cols[$#cols+1]="'template_$_','$fm'".'___eq___'.$fm;
389: } ('A','B','C','D','E','F','G','H','I','J','K','L','M',
390: 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
391: 'a','b','c','d','e','f','g','h','i','j','k','l','m',
392: 'n','o','p','q','r','s','t','u','v','w','x','y','z');
393: return @cols;
394: }
395:
1.16 www 396: sub outrowassess {
1.6 www 397: my $n=shift;
398: my @cols=();
399: if ($n) {
400: $cols[0]=$rl{$f{'A'.$n}};
401: } else {
402: $cols[0]='<b><font size=+1>Export</font></b>';
403: }
404: map {
405: my $fm=$f{$_.$n};
406: $fm=~s/[\'\"]/\&\#34;/g;
407: $cols[$#cols+1]="'$_$n','$fm'".'___eq___'.$v{$_.$n};
408: } ('A','B','C','D','E','F','G','H','I','J','K','L','M',
1.7 www 409: 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
410: 'a','b','c','d','e','f','g','h','i','j','k','l','m',
411: 'n','o','p','q','r','s','t','u','v','w','x','y','z');
1.6 www 412: return @cols;
413: }
414:
1.18 www 415: sub outrow {
416: my $n=shift;
417: my @cols=();
418: if ($n) {
1.21 www 419: $cols[0]=$rl{$f{'A'.$n}};
1.18 www 420: } else {
421: $cols[0]='<b><font size=+1>Export</font></b>';
422: }
423: map {
424: my $fm=$f{$_.$n};
425: $fm=~s/[\'\"]/\&\#34;/g;
426: $cols[$#cols+1]="'$_$n','$fm'".'___eq___'.$v{$_.$n};
427: } ('A','B','C','D','E','F','G','H','I','J','K','L','M',
428: 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
429: 'a','b','c','d','e','f','g','h','i','j','k','l','m',
430: 'n','o','p','q','r','s','t','u','v','w','x','y','z');
431: return @cols;
432: }
433:
1.14 www 434: sub exportrowa {
1.28 www 435: my @exportarray=();
1.14 www 436: map {
1.28 www 437: $exportarray[$#exportarray+1]=$v{$_.'0'};
1.14 www 438: } ('A','B','C','D','E','F','G','H','I','J','K','L','M',
439: 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
1.28 www 440: return @exportarray;
1.14 www 441: }
442:
1.4 www 443: # ------------------------------------------- End of "Inside of the safe space"
444: ENDDEFS
445: $safeeval->reval($code);
446: return $safeeval;
447: }
448:
449: # ------------------------------------------------ Add or change formula values
450:
451: sub setformulas {
1.34 www 452: my ($safeeval,%f)=@_;
453: %{$safeeval->varglob('f')}=%f;
1.6 www 454: }
455:
456: # ------------------------------------------------ Add or change formula values
457:
458: sub setconstants {
1.34 www 459: my ($safeeval,%c)=@_;
460: %{$safeeval->varglob('c')}=%c;
1.6 www 461: }
462:
463: # ------------------------------------------------ Add or change formula values
464:
465: sub setrowlabels {
1.34 www 466: my ($safeeval,%rl)=@_;
467: %{$safeeval->varglob('rl')}=%rl;
1.4 www 468: }
469:
470: # ------------------------------------------------------- Calculate spreadsheet
471:
472: sub calcsheet {
473: my $safeeval=shift;
474: $safeeval->reval('&calc();');
475: }
476:
477: # ------------------------------------------------------------------ Get values
478:
479: sub getvalues {
480: my $safeeval=shift;
481: return $safeeval->reval('%v');
482: }
483:
484: # ---------------------------------------------------------------- Get formulas
485:
486: sub getformulas {
487: my $safeeval=shift;
1.34 www 488: return %{$safeeval->varglob('f')};
1.4 www 489: }
490:
1.5 www 491: # -------------------------------------------------------------------- Get type
492:
493: sub gettype {
494: my $safeeval=shift;
495: return $safeeval->reval('$sheettype');
496: }
1.27 www 497:
1.6 www 498: # ------------------------------------------------------------------ Set maxrow
499:
500: sub setmaxrow {
501: my ($safeeval,$row)=@_;
502: $safeeval->reval('$maxrow='.$row.';');
503: }
504:
505: # ------------------------------------------------------------------ Get maxrow
506:
507: sub getmaxrow {
508: my $safeeval=shift;
509: return $safeeval->reval('$maxrow');
510: }
1.5 www 511:
1.6 www 512: # ---------------------------------------------------------------- Set filename
1.5 www 513:
514: sub setfilename {
515: my ($safeeval,$fn)=@_;
1.11 www 516: $safeeval->reval('$filename="'.$fn.'";');
1.5 www 517: }
518:
1.6 www 519: # ---------------------------------------------------------------- Get filename
1.5 www 520:
521: sub getfilename {
522: my $safeeval=shift;
523: return $safeeval->reval('$filename');
524: }
1.29 www 525:
1.28 www 526: # --------------------------------------------------------------- Get course ID
527:
528: sub getcid {
529: my $safeeval=shift;
530: return $safeeval->reval('$cid');
531: }
1.14 www 532:
1.29 www 533: # --------------------------------------------------------- Get course filename
534:
535: sub getcfn {
536: my $safeeval=shift;
537: return $safeeval->reval('$cfn');
538: }
539:
1.27 www 540: # ----------------------------------------------------------- Get course number
541:
542: sub getcnum {
543: my $safeeval=shift;
544: return $safeeval->reval('$cnum');
545: }
546:
547: # ------------------------------------------------------------- Get course home
548:
549: sub getchome {
550: my $safeeval=shift;
551: return $safeeval->reval('$chome');
552: }
553:
554: # ----------------------------------------------------------- Get course domain
555:
556: sub getcdom {
557: my $safeeval=shift;
558: return $safeeval->reval('$cdom');
559: }
560:
561: # ---------------------------------------------------------- Get course section
562:
563: sub getcsec {
564: my $safeeval=shift;
565: return $safeeval->reval('$csec');
566: }
567:
568: # --------------------------------------------------------------- Get user name
569:
570: sub getuname {
571: my $safeeval=shift;
572: return $safeeval->reval('$uname');
573: }
574:
575: # ------------------------------------------------------------- Get user domain
576:
577: sub getudom {
578: my $safeeval=shift;
579: return $safeeval->reval('$udom');
580: }
581:
582: # --------------------------------------------------------------- Get user home
583:
584: sub getuhome {
585: my $safeeval=shift;
586: return $safeeval->reval('$uhome');
587: }
588:
589: # -------------------------------------------------------------------- Get symb
590:
591: sub getusymb {
592: my $safeeval=shift;
593: return $safeeval->reval('$usymb');
594: }
595:
1.14 www 596: # ------------------------------------------------------------- Export of A-row
597:
1.28 www 598: sub exportdata {
1.14 www 599: my $safeeval=shift;
600: return $safeeval->reval('&exportrowa()');
601: }
602:
1.5 www 603: # ========================================================== End of Spreadsheet
604: # =============================================================================
605:
1.27 www 606: #
607: # Procedures for screen output
608: #
1.6 www 609: # --------------------------------------------- Produce output row n from sheet
610:
611: sub rown {
612: my ($safeeval,$n)=@_;
1.21 www 613: my $defaultbg;
1.24 www 614: my $rowdata='';
1.21 www 615: unless ($n eq '-') {
616: $defaultbg=((($n-1)/5)==int(($n-1)/5))?'#E0E0':'#FFFF';
617: } else {
618: $defaultbg='#E0FF';
619: }
1.24 www 620: if ((($n-1)/25)==int(($n-1)/25)) {
621: my $what='Student';
622: if (&gettype($safeeval) eq 'assesscalc') {
623: $what='Item';
624: } elsif (&gettype($safeeval) eq 'studentcalc') {
625: $what='Assessment';
626: }
627: $rowdata.="</table>\n<br><table border=2>".
628: '<tr><td> <td>'.$what.'</td>';
629: map {
630: $rowdata.='<td>'.$_.'</td>';
631: } ('A','B','C','D','E','F','G','H','I','J','K','L','M',
632: 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
633: 'a','b','c','d','e','f','g','h','i','j','k','l','m',
634: 'n','o','p','q','r','s','t','u','v','w','x','y','z');
635: $rowdata.='</tr>';
636: }
637: $rowdata.="\n<tr><td><b><font size=+1>$n</font></b></td>";
1.6 www 638: my $showf=0;
1.16 www 639: my $proc;
1.18 www 640: my $maxred;
1.16 www 641: if (&gettype($safeeval) eq 'assesscalc') {
642: $proc='&outrowassess';
1.18 www 643: $maxred=1;
1.16 www 644: } else {
645: $proc='&outrow';
1.18 www 646: $maxred=26;
1.16 www 647: }
1.21 www 648: if ($n eq '-') { $proc='&templaterow'; $n=-1; }
1.6 www 649: map {
1.9 www 650: my $bgcolor=$defaultbg.((($showf-1)/5==int(($showf-1)/5))?'99':'DD');
1.6 www 651: my ($fm,$vl)=split(/\_\_\_eq\_\_\_/,$_);
652: if ($showf==0) { $vl=$_; }
1.18 www 653: if ($showf<=$maxred) { $bgcolor='#FFDDDD'; }
1.9 www 654: if (($n==0) && ($showf<=26)) { $bgcolor='#CCCCFF'; }
1.18 www 655: if (($showf>$maxred) || ((!$n) && ($showf>0))) {
1.6 www 656: if ($vl eq '') {
1.9 www 657: $vl='<font size=+2 color='.$bgcolor.'>#</font>';
1.6 www 658: }
659: $rowdata.=
1.10 www 660: '<td bgcolor='.$bgcolor.'><a href="javascript:celledit('.$fm.');">'.$vl.
1.6 www 661: '</a></td>';
662: } else {
1.9 www 663: $rowdata.='<td bgcolor='.$bgcolor.'> '.$vl.' </td>';
1.6 www 664: }
665: $showf++;
1.16 www 666: } $safeeval->reval($proc.'('.$n.')');
1.6 www 667: return $rowdata.'</tr>';
668: }
669:
670: # ------------------------------------------------------------- Print out sheet
671:
672: sub outsheet {
1.24 www 673: my ($r,$safeeval)=@_;
1.18 www 674: my $maxred;
675: my $realm;
676: if (&gettype($safeeval) eq 'assesscalc') {
677: $maxred=1;
678: $realm='Assessment';
679: } elsif (&gettype($safeeval) eq 'studentcalc') {
680: $maxred=26;
681: $realm='User';
682: } else {
683: $maxred=26;
684: $realm='Course';
685: }
686: my $maxyellow=52-$maxred;
1.24 www 687: my $tabledata=
688: '<table border=2><tr><th colspan=2 rowspan=2><font size=+2>'.
1.18 www 689: $realm.'</font></th>'.
690: '<td bgcolor=#FFDDDD colspan='.$maxred.
691: '><b><font size=+1>Import</font></b></td>'.
692: '<td colspan='.$maxyellow.
693: '><b><font size=+1>Calculations</font></b></td></tr><tr>';
694: my $showf=0;
1.6 www 695: map {
1.18 www 696: $showf++;
697: if ($showf<=$maxred) {
698: $tabledata.='<td bgcolor="#FFDDDD">';
699: } else {
700: $tabledata.='<td>';
701: }
702: $tabledata.="<b><font size=+1>$_</font></b></td>";
703: } ('A','B','C','D','E','F','G','H','I','J','K','L','M',
1.7 www 704: 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
705: 'a','b','c','d','e','f','g','h','i','j','k','l','m',
706: 'n','o','p','q','r','s','t','u','v','w','x','y','z');
1.6 www 707: $tabledata.='</tr>';
708: my $row;
709: my $maxrow=&getmaxrow($safeeval);
1.21 www 710: $tabledata.=&rown($safeeval,'-');
1.24 www 711: $r->print($tabledata);
1.6 www 712: for ($row=0;$row<=$maxrow;$row++) {
1.24 www 713: $r->print(&rown($safeeval,$row));
1.6 www 714: }
1.24 www 715: $r->print('</table>');
1.6 www 716: }
717:
1.27 www 718: #
719: # -------------------------------------- Read spreadsheet formulas for a course
720: #
721:
722: sub readsheet {
723: my ($safeeval,$fn)=@_;
724: my $stype=&gettype($safeeval);
725: my $cnum=&getcnum($safeeval);
1.28 www 726: my $cdom=&getcdom($safeeval);
727: my $chome=&getchome($safeeval);
1.27 www 728:
729: # --------- There is no filename. Look for defaults in course and global, cache
730:
731: unless($fn) {
1.28 www 732: unless ($fn=$defaultsheets{$cnum.'_'.$cdom.'_'.$stype}) {
733: $fn=&Apache::lonnet::reply('get:'.$cdom.':'.$cnum.
734: ':environment:spreadsheet_default_'.$stype,
735: $chome);
1.27 www 736: unless (($fn) && ($fn!~/^error\:/)) {
737: $fn='default_'.$stype;
738: }
1.28 www 739: $defaultsheets{$cnum.'_'.$cdom.'_'.$stype}=$fn;
1.27 www 740: }
741: }
742:
743: # ---------------------------------------------------------- fn now has a value
744:
745: &setfilename($safeeval,$fn);
746:
747: # ------------------------------------------------------ see if sheet is cached
748: my $fstring='';
1.28 www 749: if ($fstring=$spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}) {
750: &setformulas($safeeval,split(/\_\_\_\;\_\_\_/,$fstring));
1.27 www 751: } else {
1.6 www 752:
1.27 www 753: # ---------------------------------------------------- Not cached, need to read
1.5 www 754:
1.27 www 755: my %f=();
1.3 www 756:
1.27 www 757: if ($fn=~/^default\_/) {
1.19 www 758: my $sheetxml='';
1.10 www 759: {
760: my $fh;
1.28 www 761: if ($fh=Apache::File->new($includedir.
1.19 www 762: '/default.'.&gettype($safeeval))) {
763: $sheetxml=join('',<$fh>);
764: }
765: }
1.27 www 766: my $parser=HTML::TokeParser->new(\$sheetxml);
767: my $token;
768: while ($token=$parser->get_token) {
1.19 www 769: if ($token->[0] eq 'S') {
770: if ($token->[1] eq 'field') {
771: $f{$token->[2]->{'col'}.$token->[2]->{'row'}}=
772: $parser->get_text('/field');
773: }
1.20 www 774: if ($token->[1] eq 'template') {
775: $f{'template_'.$token->[2]->{'col'}}=
776: $parser->get_text('/template');
777: }
1.19 www 778: }
1.27 www 779: }
780: } else {
1.28 www 781: my $sheet='';
782: my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.':'.$fn,
783: $chome);
1.19 www 784: unless ($reply=~/^error\:/) {
1.27 www 785: $sheet=$reply;
786: }
787: map {
788: my ($name,$value)=split(/\=/,$_);
789: $f{&Apache::lonnet::unescape($name)}=
790: &Apache::lonnet::unescape($value);
791: } split(/\&/,$sheet);
1.10 www 792: }
1.27 www 793: # --------------------------------------------------------------- Cache and set
1.28 www 794: $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);
1.27 www 795: &setformulas($safeeval,%f);
1.3 www 796: }
797: }
798:
1.28 www 799: # -------------------------------------------------------- Make new spreadsheet
800:
801: sub makenewsheet {
802: my ($uname,$udom,$stype,$usymb)=@_;
1.36 www 803: my $safeeval=initsheet($stype);
1.28 www 804: $safeeval->reval(
1.29 www 805: '$uname="'.$uname.
806: '";$udom="'.$udom.
807: '";$uhome="'.&Apache::lonnet::homeserver($uname,$udom).
808: '";$sheettype="'.$stype.
809: '";$usymb="'.$usymb.
1.30 www 810: '";$csec="'.&Apache::lonnet::usection($udom,$uname,
811: $ENV{'request.course.id'}).
1.29 www 812: '";$cid="'.$ENV{'request.course.id'}.
813: '";$cfn="'.$ENV{'request.course.fn'}.
814: '";$cnum="'.$ENV{'course.'.$ENV{'request.course.id'}.'.num'}.
815: '";$cdom="'.$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.
816: '";$chome="'.$ENV{'course.'.$ENV{'request.course.id'}.'.home'}.'";');
1.28 www 817: return $safeeval;
818: }
819:
1.19 www 820: # ------------------------------------------------------------ Save spreadsheet
821:
822: sub writesheet {
1.28 www 823: my ($safeeval,$makedef)=@_;
824: my $cid=&getcid($safeeval);
825: if (&Apache::lonnet::allowed('opa',$cid)) {
1.19 www 826: my %f=&getformulas($safeeval);
1.28 www 827: my $stype=&gettype($safeeval);
828: my $cnum=&getcnum($safeeval);
829: my $cdom=&getcdom($safeeval);
830: my $chome=&getchome($safeeval);
831: my $fn=&getfilename($safeeval);
832:
833: # ------------------------------------------------------------- Cache new sheet
834: $spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f);
835: # ----------------------------------------------------------------- Write sheet
1.19 www 836: my $sheetdata='';
837: map {
838: $sheetdata.=&Apache::lonnet::escape($_).'='.
839: &Apache::lonnet::escape($f{$_}).'&';
840: } keys %f;
841: $sheetdata=~s/\&$//;
1.28 www 842: my $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'.$fn.':'.
843: $sheetdata,$chome);
1.22 www 844: if ($reply eq 'ok') {
1.28 www 845: $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'.
1.30 www 846: $stype.'_spreadsheets:'.
1.28 www 847: &Apache::lonnet::escape($fn).'='.$ENV{'user.name'},
848: $chome);
849: if ($reply eq 'ok') {
850: if ($makedef) {
851: return &Apache::lonnet::reply('put:'.$cdom.':'.$cnum.
852: ':environment:spreadsheet_default_'.$stype.'='.
853: &Apache::lonnet::escape($fn),
854: $chome);
855: } else {
856: return $reply;
857: }
858: } else {
859: return $reply;
860: }
1.22 www 861: } else {
862: return $reply;
863: }
864: }
865: return 'unauthorized';
1.19 www 866: }
867:
1.10 www 868: # ----------------------------------------------- Make a temp copy of the sheet
1.28 www 869: # "Modified workcopy" - interactive only
870: #
1.10 www 871:
872: sub tmpwrite {
1.28 www 873: my $safeeval=shift;
874: my $fn=$ENV{'user.name'}.'_'.
875: $ENV{'user.domain'}.'_spreadsheet_'.&getusymb($safeeval).'_'.
876: &getfilename($safeeval);
1.10 www 877: $fn=~s/\W/\_/g;
878: $fn=$tmpdir.$fn.'.tmp';
879: my $fh;
880: if ($fh=Apache::File->new('>'.$fn)) {
881: print $fh join("\n",&getformulas($safeeval));
882: }
883: }
884:
885: # ---------------------------------------------------------- Read the temp copy
886:
887: sub tmpread {
1.28 www 888: my ($safeeval,$nfield,$nform)=@_;
889: my $fn=$ENV{'user.name'}.'_'.
890: $ENV{'user.domain'}.'_spreadsheet_'.&getusymb($safeeval).'_'.
891: &getfilename($safeeval);
1.10 www 892: $fn=~s/\W/\_/g;
893: $fn=$tmpdir.$fn.'.tmp';
894: my $fh;
895: my %fo=();
896: if ($fh=Apache::File->new($fn)) {
897: my $name;
898: while ($name=<$fh>) {
899: chomp($name);
900: my $value=<$fh>;
901: chomp($value);
902: $fo{$name}=$value;
903: }
904: }
1.22 www 905: if ($nfield) { $fo{$nfield}=$nform; }
1.10 www 906: &setformulas($safeeval,%fo);
907: }
908:
1.11 www 909: # ================================================================== Parameters
910: # -------------------------------------------- Figure out a cascading parameter
1.28 www 911: #
1.29 www 912: # For this function to work
913: #
914: # * parmhash needs to be tied
915: # * courseopt and useropt need to be initialized for this user and course
916: #
1.11 www 917:
918: sub parmval {
1.28 www 919: my ($what,$safeeval)=@_;
920: my $cid=&getcid($safeeval);
921: my $csec=&getcsec($safeeval);
922: my $uname=&getuname($safeeval);
923: my $udom=&getudom($safeeval);
924: my $symb=&getusymb($safeeval);
1.11 www 925:
926: unless ($symb) { return ''; }
927: my $result='';
928:
929: my ($mapname,$id,$fn)=split(/\_\_\_/,$symb);
930: # ----------------------------------------------------- Cascading lookup scheme
1.12 www 931: my $rwhat=$what;
932: $what=~s/^parameter\_//;
933: $what=~s/\_/\./;
1.11 www 934:
935: my $symbparm=$symb.'.'.$what;
936: my $mapparm=$mapname.'___(all).'.$what;
1.29 www 937: my $usercourseprefix=$uname.'_'.$udom.'_'.$cid;
1.11 www 938:
939: my $seclevel=
1.28 www 940: $usercourseprefix.'.['.
1.11 www 941: $csec.'].'.$what;
942: my $seclevelr=
1.28 www 943: $usercourseprefix.'.['.
1.11 www 944: $csec.'].'.$symbparm;
945: my $seclevelm=
1.28 www 946: $usercourseprefix.'.['.
1.11 www 947: $csec.'].'.$mapparm;
948:
949: my $courselevel=
1.28 www 950: $usercourseprefix.'.'.$what;
1.11 www 951: my $courselevelr=
1.28 www 952: $usercourseprefix.'.'.$symbparm;
1.11 www 953: my $courselevelm=
1.28 www 954: $usercourseprefix.'.'.$mapparm;
1.12 www 955:
1.11 www 956: # ---------------------------------------------------------- fourth, check user
957:
958: if ($uname) {
959:
960: if ($useropt{$courselevelr}) { return $useropt{$courselevelr}; }
961:
962: if ($useropt{$courselevelm}) { return $useropt{$courselevelm}; }
963:
964: if ($useropt{$courselevel}) { return $useropt{$courselevel}; }
965:
966: }
967:
968: # --------------------------------------------------------- third, check course
969:
970: if ($csec) {
971:
972: if ($courseopt{$seclevelr}) { return $courseopt{$seclevelr}; }
973:
974: if ($courseopt{$seclevelm}) { return $courseopt{$seclevelm}; }
975:
976: if ($courseopt{$seclevel}) { return $courseopt{$seclevel}; }
977:
978: }
979:
980: if ($courseopt{$courselevelr}) { return $courseopt{$courselevelr}; }
981:
982: if ($courseopt{$courselevelm}) { return $courseopt{$courselevelm}; }
983:
984: if ($courseopt{$courselevel}) { return $courseopt{$courselevel}; }
985:
986: # ----------------------------------------------------- second, check map parms
987:
988: my $thisparm=$parmhash{$symbparm};
989: if ($thisparm) { return $thisparm; }
990:
991: # -------------------------------------------------------- first, check default
992:
1.12 www 993: return &Apache::lonnet::metadata($fn,$rwhat.'.default');
1.11 www 994:
995: }
996:
1.23 www 997: # ---------------------------------------------- Update rows for course listing
1.11 www 998:
1.28 www 999: sub updateclasssheet {
1.23 www 1000: my $safeeval=shift;
1.28 www 1001: my $cnum=&getcnum($safeeval);
1002: my $cdom=&getcdom($safeeval);
1003: my $cid=&getcid($safeeval);
1004: my $chome=&getchome($safeeval);
1005:
1006: # ---------------------------------------------- Read class list and row labels
1007:
1.23 www 1008: my $classlst=&Apache::lonnet::reply
1.28 www 1009: ('dump:'.$cdom.':'.$cnum.':classlist',$chome);
1.23 www 1010: my %currentlist=();
1011: my $now=time;
1012: unless ($classlst=~/^error\:/) {
1013: map {
1014: my ($name,$value)=split(/\=/,$_);
1.24 www 1015: my ($end,$start)=split(/\:/,&Apache::lonnet::unescape($value));
1.23 www 1016: my $active=1;
1017: if (($end) && ($now>$end)) { $active=0; }
1018: if ($active) {
1.24 www 1019: my $rowlabel='';
1020: $name=&Apache::lonnet::unescape($name);
1.28 www 1021: my ($sname,$sdom)=split(/\:/,$name);
1022: my $ssec=&Apache::lonnet::usection($sdom,$sname,$cid);
1023: if ($ssec==-1) {
1.24 www 1024: $rowlabel='<font color=red>Data not available: '.$name.
1025: '</font>';
1026: } else {
1.28 www 1027: my %reply=&Apache::lonnet::idrget($sdom,$sname);
1028: my $reply=&Apache::lonnet::reply('get:'.$sdom.':'.$sname.
1.24 www 1029: ':environment:firstname&middlename&lastname&generation',
1.28 www 1030: &Apache::lonnet::homeserver($sname,$sdom));
1.45 www 1031: $rowlabel='<a href="/adm/studentcalc?uname='.$sname.
1032: '&udom='.$sdom.'">'.
1033: $ssec.' '.$reply{$sname}.'<br>';
1.24 www 1034: map {
1035: $rowlabel.=&Apache::lonnet::unescape($_).' ';
1036: } split(/\&/,$reply);
1.45 www 1037: $rowlabel.='</a>';
1.24 www 1038: }
1039: $currentlist{&Apache::lonnet::unescape($name)}=$rowlabel;
1.23 www 1040: }
1041: } split(/\&/,$classlst);
1042: #
1043: # -------------------- Find discrepancies between the course row table and this
1044: #
1045: my %f=&getformulas($safeeval);
1046: my $changed=0;
1047:
1048: my $maxrow=0;
1049: my %existing=();
1050:
1051: # ----------------------------------------------------------- Now obsolete rows
1052: map {
1053: if ($_=~/^A(\d+)/) {
1054: $maxrow=($1>$maxrow)?$1:$maxrow;
1055: $existing{$f{$_}}=1;
1056: unless ((defined($currentlist{$f{$_}})) || (!$1)) {
1057: $f{$_}='!!! Obsolete';
1058: $changed=1;
1059: }
1060: }
1061: } keys %f;
1062:
1063: # -------------------------------------------------------- New and unknown keys
1064:
1065: map {
1066: unless ($existing{$_}) {
1067: $changed=1;
1068: $maxrow++;
1069: $f{'A'.$maxrow}=$_;
1070: }
1.24 www 1071: } sort keys %currentlist;
1.23 www 1072:
1073: if ($changed) { &setformulas($safeeval,%f); }
1074:
1075: &setmaxrow($safeeval,$maxrow);
1076: &setrowlabels($safeeval,%currentlist);
1077:
1078: } else {
1079: return 'Could not access course data';
1080: }
1081: }
1.5 www 1082:
1.28 www 1083: # ----------------------------------- Update rows for student and assess sheets
1084:
1085: sub updatestudentassesssheet {
1.5 www 1086: my $safeeval=shift;
1087: my %bighash;
1.35 www 1088: my $stype=&gettype($safeeval);
1089: my %current=();
1090: unless ($updatedata{$ENV{'request.course.fn'}.'_'.$stype}) {
1.5 www 1091: # -------------------------------------------------------------------- Tie hash
1092: if (tie(%bighash,'GDBM_File',$ENV{'request.course.fn'}.'.db',
1093: &GDBM_READER,0640)) {
1094: # --------------------------------------------------------- Get all assessments
1095:
1096: my %allkeys=();
1097: my %allassess=();
1098:
1.50 www 1099: my $adduserstr='';
1100: if ((&getuname($safeeval) ne $ENV{'user.name'}) ||
1101: (&getudom($safeeval) ne $ENV{'user.domain'})) {
1102: $adduserstr='&uname='.&getuname($safeeval).
1103: '&udom='.&getudom($safeeval);
1104: }
1105:
1.5 www 1106: map {
1107: if ($_=~/^src\_(\d+)\.(\d+)$/) {
1108: my $mapid=$1;
1109: my $resid=$2;
1110: my $id=$mapid.'.'.$resid;
1111: my $srcf=$bighash{$_};
1112: if ($srcf=~/\.(problem|exam|quiz|assess|survey|form)$/) {
1113: my $symb=
1114: &Apache::lonnet::declutter($bighash{'map_id_'.$mapid}).
1115: '___'.$resid.'___'.
1116: &Apache::lonnet::declutter($srcf);
1.38 www 1117: $allassess{$symb}=
1.50 www 1118: '<a href="/adm/assesscalc?usymb='.$symb.$adduserstr.'">'.
1119: $bighash{'title_'.$id}.'</a>';
1.6 www 1120: if ($stype eq 'assesscalc') {
1.5 www 1121: map {
1.11 www 1122: if (($_=~/^stores\_(.*)/) || ($_=~/^parameter\_(.*)/)) {
1.5 www 1123: my $key=$_;
1124: my $display=
1125: &Apache::lonnet::metadata($srcf,$key.'.display');
1126: unless ($display) {
1.40 www 1127: $display.=
1.5 www 1128: &Apache::lonnet::metadata($srcf,$key.'.name');
1129: }
1.40 www 1130: $display.='<br>'.$key;
1.5 www 1131: $allkeys{$key}=$display;
1132: }
1133: } split(/\,/,&Apache::lonnet::metadata($srcf,'keys'));
1134: }
1135: }
1136: }
1137: } keys %bighash;
1138: untie(%bighash);
1139:
1140: #
1.11 www 1141: # %allkeys has a list of storage and parameter displays by unikey
1.5 www 1142: # %allassess has a list of all resource displays by symb
1143: #
1.6 www 1144:
1145: if ($stype eq 'assesscalc') {
1146: %current=%allkeys;
1147: } elsif ($stype eq 'studentcalc') {
1148: %current=%allassess;
1149: }
1.35 www 1150: $updatedata{$ENV{'request.course.fn'}.'_'.$stype}=
1151: join('___;___',%current);
1152: } else {
1153: return 'Could not access course data';
1154: }
1155: # ------------------------------------------------------ Get current from cache
1156: } else {
1157: %current=split(/\_\_\_\;\_\_\_/,
1158: $updatedata{$ENV{'request.course.fn'}.'_'.$stype});
1159: }
1160: # -------------------- Find discrepancies between the course row table and this
1161: #
1162: my %f=&getformulas($safeeval);
1163: my $changed=0;
1.6 www 1164:
1165: my $maxrow=0;
1166: my %existing=();
1167:
1168: # ----------------------------------------------------------- Now obsolete rows
1.5 www 1169: map {
1.6 www 1170: if ($_=~/^A(\d+)/) {
1171: $maxrow=($1>$maxrow)?$1:$maxrow;
1172: $existing{$f{$_}}=1;
1.17 www 1173: unless ((defined($current{$f{$_}})) || (!$1)) {
1.6 www 1174: $f{$_}='!!! Obsolete';
1175: $changed=1;
1.5 www 1176: }
1177: }
1178: } keys %f;
1.6 www 1179:
1180: # -------------------------------------------------------- New and unknown keys
1181:
1182: map {
1183: unless ($existing{$_}) {
1184: $changed=1;
1185: $maxrow++;
1186: $f{'A'.$maxrow}=$_;
1187: }
1188: } keys %current;
1.35 www 1189:
1.6 www 1190: if ($changed) { &setformulas($safeeval,%f); }
1191:
1192: &setmaxrow($safeeval,$maxrow);
1193: &setrowlabels($safeeval,%current);
1.35 www 1194:
1195: undef %current;
1196: undef %existing;
1.5 www 1197: }
1.3 www 1198:
1.24 www 1199: # ------------------------------------------------ Load data for one assessment
1.16 www 1200:
1.29 www 1201: sub loadstudent {
1.16 www 1202: my $safeeval=shift;
1203: my %c=();
1204: my %f=&getformulas($safeeval);
1.39 www 1205: $cachedassess=&getuname($safeeval).':'.&getudom($safeeval);
1206: %cachedstores=();
1207: {
1208: my $reply=&Apache::lonnet::reply('dump:'.&getudom($safeeval).':'.
1209: &getuname($safeeval).':'.
1210: &getcid($safeeval),
1211: &getuhome($safeeval));
1212: unless ($reply=~/^error\:/) {
1213: map {
1214: my ($name,$value)=split(/\=/,$_);
1215: $cachedstores{&Apache::lonnet::unescape($name)}=
1216: &Apache::lonnet::unescape($value);
1217: } split(/\&/,$reply);
1218: }
1219: }
1.36 www 1220: my @assessdata=();
1.16 www 1221: map {
1.17 www 1222: if ($_=~/^A(\d+)/) {
1223: my $row=$1;
1.42 www 1224: unless (($f{$_}=~/^\!/) || ($row==0)) {
1.36 www 1225: @assessdata=&exportsheet(&getuname($safeeval),
1226: &getudom($safeeval),
1227: 'assesscalc',$f{$_});
1.17 www 1228: my $index=0;
1.30 www 1229: map {
1230: if ($assessdata[$index]) {
1.41 www 1231: my $col=$_;
1232: if ($assessdata[$index]=~/\D/) {
1233: $c{$col.$row}="'".$assessdata[$index]."'";
1234: } else {
1235: $c{$col.$row}=$assessdata[$index];
1236: }
1237: unless ($col eq 'A') {
1238: $f{$col.$row}='import';
1.30 www 1239: }
1240: }
1241: $index++;
1242: } ('A','B','C','D','E','F','G','H','I','J','K','L','M',
1243: 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
1.16 www 1244: }
1245: }
1246: } keys %f;
1.39 www 1247: $cachedassess='';
1248: undef %cachedstores;
1.18 www 1249: &setformulas($safeeval,%f);
1.16 www 1250: &setconstants($safeeval,%c);
1251: }
1252:
1.24 www 1253: # --------------------------------------------------- Load data for one student
1254:
1.30 www 1255: sub loadcourse {
1.37 www 1256: my ($safeeval,$r)=@_;
1.24 www 1257: my %c=();
1258: my %f=&getformulas($safeeval);
1.37 www 1259: my $total=0;
1260: map {
1261: if ($_=~/^A(\d+)/) {
1262: unless ($f{$_}=~/^\!/) { $total++; }
1263: }
1264: } keys %f;
1265: my $now=0;
1266: my $since=time;
1.39 www 1267: $r->print(<<ENDPOP);
1268: <script>
1269: popwin=open('','popwin','width=400,height=100');
1270: popwin.document.writeln('<html><body bgcolor="#FFFFFF">'+
1.50 www 1271: '<h3>Spreadsheet Calculation Progress</h3>'+
1.39 www 1272: '<form name=popremain>'+
1273: '<input type=text size=35 name=remaining value=Starting></form>'+
1274: '</body></html>');
1.42 www 1275: popwin.document.close();
1.39 www 1276: </script>
1277: ENDPOP
1.37 www 1278: $r->rflush();
1.24 www 1279: map {
1280: if ($_=~/^A(\d+)/) {
1281: my $row=$1;
1.42 www 1282: unless (($f{$_}=~/^\!/) || ($row==0)) {
1.37 www 1283: my @studentdata=&exportsheet(split(/\:/,$f{$_}),
1.30 www 1284: 'studentcalc');
1.37 www 1285: undef %userrdatas;
1286: $now++;
1.39 www 1287: $r->print('<script>popwin.document.popremain.remaining.value="'.
1.37 www 1288: $now.'/'.$total.': '.int((time-$since)/$now*($total-$now)).
1.39 www 1289: ' secs remaining";</script>');
1.37 www 1290: $r->rflush();
1291:
1.24 www 1292: my $index=0;
1.30 www 1293: map {
1294: if ($studentdata[$index]) {
1.41 www 1295: my $col=$_;
1296: if ($studentdata[$index]=~/\D/) {
1297: $c{$col.$row}="'".$studentdata[$index]."'";
1298: } else {
1299: $c{$col.$row}=$studentdata[$index];
1300: }
1301: unless ($col eq 'A') {
1302: $f{$col.$row}='import';
1.30 www 1303: }
1304: }
1305: $index++;
1306: } ('A','B','C','D','E','F','G','H','I','J','K','L','M',
1307: 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
1.24 www 1308: }
1309: }
1310: } keys %f;
1311: &setformulas($safeeval,%f);
1312: &setconstants($safeeval,%c);
1.43 www 1313: $r->print('<script>popwin.close()</script>');
1.37 www 1314: $r->rflush();
1.24 www 1315: }
1316:
1.6 www 1317: # ------------------------------------------------ Load data for one assessment
1318:
1.29 www 1319: sub loadassessment {
1320: my $safeeval=shift;
1321:
1322: my $uhome=&getuhome($safeeval);
1323: my $uname=&getuname($safeeval);
1324: my $udom=&getudom($safeeval);
1325: my $symb=&getusymb($safeeval);
1326: my $cid=&getcid($safeeval);
1327: my $cnum=&getcnum($safeeval);
1328: my $cdom=&getcdom($safeeval);
1329: my $chome=&getchome($safeeval);
1330:
1.6 www 1331: my $namespace;
1.29 www 1332: unless ($namespace=$cid) { return ''; }
1.11 www 1333:
1334: # ----------------------------------------------------------- Get stored values
1.39 www 1335:
1336: my %returnhash=();
1337:
1338: if ($cachedassess eq $uname.':'.$udom) {
1339: #
1340: # get data out of the dumped stores
1341: #
1342:
1343: my $version=$cachedstores{'version:'.$symb};
1344: my $scope;
1345: for ($scope=1;$scope<=$version;$scope++) {
1346: map {
1347: $returnhash{$_}=$cachedstores{$scope.':'.$symb.':'.$_};
1348: } split(/\:/,$cachedstores{$scope.':keys:'.$symb});
1349: }
1350:
1351: } else {
1352: #
1353: # restore individual
1354: #
1355:
1.11 www 1356: my $answer=&Apache::lonnet::reply(
1.15 www 1357: "restore:$udom:$uname:".
1358: &Apache::lonnet::escape($namespace).":".
1359: &Apache::lonnet::escape($symb),$uhome);
1.6 www 1360: map {
1361: my ($name,$value)=split(/\=/,$_);
1.11 www 1362: $returnhash{&Apache::lonnet::unescape($name)}=
1363: &Apache::lonnet::unescape($value);
1.6 www 1364: } split(/\&/,$answer);
1365: my $version;
1366: for ($version=1;$version<=$returnhash{'version'};$version++) {
1367: map {
1368: $returnhash{$_}=$returnhash{$version.':'.$_};
1369: } split(/\:/,$returnhash{$version.':keys'});
1370: }
1.39 www 1371: }
1.11 www 1372: # ----------------------------- returnhash now has all stores for this resource
1373:
1374: # ---------------------------- initialize coursedata and userdata for this user
1.31 www 1375: undef %courseopt;
1376: undef %useropt;
1.29 www 1377:
1378: my $userprefix=$uname.'_'.$udom.'_';
1379:
1.11 www 1380: unless ($uhome eq 'no_host') {
1381: # -------------------------------------------------------------- Get coursedata
1.13 www 1382: unless
1.32 www 1383: ((time-$courserdatas{$cid.'.last_cache'})<240) {
1.29 www 1384: my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.
1385: ':resourcedata',$chome);
1.11 www 1386: if ($reply!~/^error\:/) {
1.29 www 1387: $courserdatas{$cid}=$reply;
1388: $courserdatas{$cid.'.last_cache'}=time;
1.11 www 1389: }
1390: }
1391: map {
1392: my ($name,$value)=split(/\=/,$_);
1.29 www 1393: $courseopt{$userprefix.&Apache::lonnet::unescape($name)}=
1.11 www 1394: &Apache::lonnet::unescape($value);
1.36 www 1395: } split(/\&/,$courserdatas{$cid});
1.11 www 1396: # --------------------------------------------------- Get userdata (if present)
1.13 www 1397: unless
1.32 www 1398: ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) {
1.11 www 1399: my $reply=
1400: &Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome);
1401: if ($reply!~/^error\:/) {
1402: $userrdatas{$uname.'___'.$udom}=$reply;
1.13 www 1403: $userrdatas{$uname.'___'.$udom.'.last_cache'}=time;
1.11 www 1404: }
1405: }
1406: map {
1407: my ($name,$value)=split(/\=/,$_);
1.29 www 1408: $useropt{$userprefix.&Apache::lonnet::unescape($name)}=
1.15 www 1409: &Apache::lonnet::unescape($value);
1.11 www 1410: } split(/\&/,$userrdatas{$uname.'___'.$udom});
1.29 www 1411: }
1412: # ----------------- now courseopt, useropt initialized for this user and course
1413: # (used by parmval)
1414:
1415: my %c=();
1.6 www 1416:
1.29 www 1417: if (tie(%parmhash,'GDBM_File',
1418: &getcfn($safeeval).'_parms.db',&GDBM_READER,0640)) {
1.6 www 1419: my %f=&getformulas($safeeval);
1420: map {
1421: if ($_=~/^A/) {
1422: unless ($f{$_}=~/^\!/) {
1.11 www 1423: if ($f{$_}=~/^parameter/) {
1.40 www 1424: my $val=&parmval($f{$_},$safeeval);
1425: $c{$_}=$val;
1426: $c{$f{$_}}=$val;
1.11 www 1427: } else {
1.15 www 1428: my $key=$f{$_};
1.40 www 1429: my $ckey=$key;
1.15 www 1430: $key=~s/^stores\_/resource\./;
1431: $key=~s/\_/\./;
1432: $c{$_}=$returnhash{$key};
1.40 www 1433: $c{$ckey}=$returnhash{$key};
1.11 www 1434: }
1435: }
1.6 www 1436: }
1437: } keys %f;
1.29 www 1438: untie(%parmhash);
1439: }
1440: &setconstants($safeeval,%c);
1.6 www 1441: }
1442:
1.10 www 1443: # --------------------------------------------------------- Various form fields
1444:
1445: sub textfield {
1446: my ($title,$name,$value)=@_;
1447: return "\n<p><b>$title:</b><br>".
1448: '<input type=text name="'.$name.'" size=80 value="'.$value.'">';
1449: }
1450:
1451: sub hiddenfield {
1452: my ($name,$value)=@_;
1453: return "\n".'<input type=hidden name="'.$name.'" value="'.$value.'">';
1454: }
1455:
1456: sub selectbox {
1457: my ($title,$name,$value,%options)=@_;
1458: my $selout="\n<p><b>$title:</b><br>".'<select name="'.$name.'">';
1459: map {
1460: $selout.='<option value="'.$_.'"';
1461: if ($_ eq $value) { $selout.=' selected'; }
1462: $selout.='>'.$options{$_}.'</option>';
1463: } sort keys %options;
1464: return $selout.'</select>';
1465: }
1466:
1.28 www 1467: # =============================================== Update information in a sheet
1468: #
1469: # Add new users or assessments, etc.
1470: #
1471:
1472: sub updatesheet {
1473: my $safeeval=shift;
1474: my $stype=&gettype($safeeval);
1475: if ($stype eq 'classcalc') {
1476: return &updateclasssheet($safeeval);
1477: } else {
1478: return &updatestudentassesssheet($safeeval);
1479: }
1480: }
1481:
1482: # =================================================== Load the rows for a sheet
1483: #
1484: # Import the data for rows
1485: #
1486:
1.37 www 1487: sub loadrows {
1488: my ($safeeval,$r)=@_;
1.28 www 1489: my $stype=&gettype($safeeval);
1490: if ($stype eq 'classcalc') {
1.37 www 1491: &loadcourse($safeeval,$r);
1.28 www 1492: } elsif ($stype eq 'studentcalc') {
1.29 www 1493: &loadstudent($safeeval);
1.28 www 1494: } else {
1.29 www 1495: &loadassessment($safeeval);
1.28 www 1496: }
1497: }
1498:
1.47 www 1499: # ======================================================= Forced recalculation?
1500:
1501: sub checkthis {
1502: my ($keyname,$time)=@_;
1503: return ($time<$expiredates{$keyname});
1504: }
1505: sub forcedrecalc {
1506: my ($uname,$udom,$stype,$usymb)=@_;
1507: my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;
1508: my $time=$oldsheets{$key.'.time'};
1509: unless ($time) { return 1; }
1510: if ($stype eq 'assesscalc') {
1511: my $map=(split(/\_\_\_/,$usymb))[0];
1512: if (&checkthis('::assesscalc:',$time) ||
1513: &checkthis('::assesscalc:'.$map,$time) ||
1514: &checkthis('::assesscalc:'.$usymb,$time) ||
1.49 www 1515: &checkthis($uname.':'.$udom.':assesscalc:',$time) ||
1516: &checkthis($uname.':'.$udom.':assesscalc:'.$map,$time) ||
1517: &checkthis($uname.':'.$udom.':assesscalc:'.$usymb,$time)) {
1.47 www 1518: return 1;
1519: }
1520: } else {
1521: if (&checkthis('::studentcalc:',$time) ||
1.51 ! www 1522: &checkthis($uname.':'.$udom.':studentcalc:',$time)) {
1.47 www 1523: return 1;
1524: }
1525: }
1526: return 0;
1527: }
1528:
1.28 www 1529: # ============================================================== Export handler
1530: #
1531: # Non-interactive call from with program
1532: #
1533:
1534: sub exportsheet {
1.44 www 1535: my ($uname,$udom,$stype,$usymb,$fn)=@_;
1536: my @exportarr=();
1537: #
1538: # Check if cached
1539: #
1.46 www 1540:
1.44 www 1541: my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;
1542: my $found='';
1543:
1544: if ($oldsheets{$key}) {
1545: map {
1546: my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);
1547: if ($name eq $fn) {
1548: $found=$value;
1549: }
1550: } split(/\_\_\_\&\_\_\_/,$oldsheets{$key});
1551: }
1552:
1.46 www 1553: unless ($found) {
1554: &cachedssheets($uname,$udom,&Apache::lonnet::homeserver($uname,$udom));
1555: if ($oldsheets{$key}) {
1556: map {
1557: my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);
1558: if ($name eq $fn) {
1559: $found=$value;
1560: }
1561: } split(/\_\_\_\&\_\_\_/,$oldsheets{$key});
1562: }
1563: }
1.47 www 1564: #
1565: # Check if still valid
1566: #
1567: if ($found) {
1568: if (&forcedrecalc($uname,$udom,$stype,$usymb)) {
1569: $found='';
1570: }
1571: }
1572:
1.44 www 1573: if ($found) {
1574: #
1575: # Return what was cached
1576: #
1577: @exportarr=split(/\_\_\_\;\_\_\_/,$found);
1578:
1579: } else {
1580: #
1581: # Not cached
1.46 www 1582: #
1583:
1.29 www 1584: my $thissheet=&makenewsheet($uname,$udom,$stype,$usymb);
1.28 www 1585: &readsheet($thissheet,$fn);
1586: &updatesheet($thissheet);
1587: &loadrows($thissheet);
1.44 www 1588: &calcsheet($thissheet);
1589: @exportarr=&exportdata($thissheet);
1590: #
1591: # Store now
1592: #
1593: my $cid=$ENV{'request.course.id'};
1.47 www 1594: my $current='';
1.46 www 1595: if ($stype eq 'studentcalc') {
1596: $current=&Apache::lonnet::reply('get:'.
1.44 www 1597: $ENV{'course.'.$cid.'.domain'}.':'.
1598: $ENV{'course.'.$cid.'.num'}.
1599: ':nohist_calculatedsheets:'.
1600: &Apache::lonnet::escape($key),
1601: $ENV{'course.'.$cid.'.home'});
1.46 www 1602: } else {
1603: $current=&Apache::lonnet::reply('get:'.
1604: &getudom($thissheet).':'.
1605: &getuname($thissheet).
1606: ':nohist_calculatedsheets_'.
1607: $ENV{'request.course.id'}.':'.
1608: &Apache::lonnet::escape($key),
1609: &getuhome($thissheet));
1610:
1611: }
1.44 www 1612: my %currentlystored=();
1613: unless ($current=~/^error\:/) {
1614: map {
1615: my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);
1616: $currentlystored{$name}=$value;
1617: } split(/\_\_\_\&\_\_\_/,&Apache::lonnet::unescape($current));
1618: }
1619: $currentlystored{$fn}=join('___;___',@exportarr);
1620:
1621: my $newstore='';
1622: map {
1623: if ($newstore) { $newstore.='___&___'; }
1624: $newstore.=$_.'___=___'.$currentlystored{$_};
1625: } keys %currentlystored;
1.47 www 1626: my $now=time;
1.46 www 1627: if ($stype eq 'studentcalc') {
1628: &Apache::lonnet::reply('put:'.
1.44 www 1629: $ENV{'course.'.$cid.'.domain'}.':'.
1630: $ENV{'course.'.$cid.'.num'}.
1631: ':nohist_calculatedsheets:'.
1632: &Apache::lonnet::escape($key).'='.
1.47 www 1633: &Apache::lonnet::escape($newstore).'&'.
1634: &Apache::lonnet::escape($key).'.time='.$now,
1.44 www 1635: $ENV{'course.'.$cid.'.home'});
1.46 www 1636: } else {
1637: &Apache::lonnet::reply('put:'.
1638: &getudom($thissheet).':'.
1639: &getuname($thissheet).
1640: ':nohist_calculatedsheets_'.
1641: $ENV{'request.course.id'}.':'.
1642: &Apache::lonnet::escape($key).'='.
1.47 www 1643: &Apache::lonnet::escape($newstore).'&'.
1644: &Apache::lonnet::escape($key).'.time='.$now,
1.46 www 1645: &getuhome($thissheet));
1646: }
1.44 www 1647: }
1648: return @exportarr;
1649: }
1.48 www 1650: # ============================================================ Expiration Dates
1651: #
1652: # Load previously cached student spreadsheets for this course
1653: #
1654:
1655: sub expirationdates {
1656: undef %expiredates;
1657: my $cid=$ENV{'request.course.id'};
1658: my $reply=&Apache::lonnet::reply('dump:'.
1659: $ENV{'course.'.$cid.'.domain'}.':'.
1660: $ENV{'course.'.$cid.'.num'}.
1661: ':nohist_expirationdates',
1662: $ENV{'course.'.$cid.'.home'});
1663: unless ($reply=~/^error\:/) {
1664: map {
1665: my ($name,$value)=split(/\=/,$_);
1666: $expiredates{&Apache::lonnet::unescape($name)}
1667: =&Apache::lonnet::unescape($value);
1668: } split(/\&/,$reply);
1669: }
1670: }
1.44 www 1671:
1672: # ===================================================== Calculated sheets cache
1673: #
1.46 www 1674: # Load previously cached student spreadsheets for this course
1.44 www 1675: #
1676:
1.46 www 1677: sub cachedcsheets {
1.44 www 1678: my $cid=$ENV{'request.course.id'};
1679: my $reply=&Apache::lonnet::reply('dump:'.
1680: $ENV{'course.'.$cid.'.domain'}.':'.
1681: $ENV{'course.'.$cid.'.num'}.
1682: ':nohist_calculatedsheets',
1683: $ENV{'course.'.$cid.'.home'});
1684: unless ($reply=~/^error\:/) {
1685: map {
1686: my ($name,$value)=split(/\=/,$_);
1687: $oldsheets{&Apache::lonnet::unescape($name)}
1688: =&Apache::lonnet::unescape($value);
1689: } split(/\&/,$reply);
1690: }
1.28 www 1691: }
1692:
1.46 www 1693: # ===================================================== Calculated sheets cache
1694: #
1695: # Load previously cached assessment spreadsheets for this student
1696: #
1697:
1698: sub cachedssheets {
1699: my ($sname,$sdom,$shome)=@_;
1700: unless (($loadedcaches{$sname.'_'.$sdom}) || ($shome eq 'no_host')) {
1701: my $cid=$ENV{'request.course.id'};
1702: my $reply=&Apache::lonnet::reply('dump:'.$sdom.':'.$sname.
1703: ':nohist_calculatedsheets_'.
1704: $ENV{'request.course.id'},
1705: $shome);
1706: unless ($reply=~/^error\:/) {
1707: map {
1708: my ($name,$value)=split(/\=/,$_);
1709: $oldsheets{&Apache::lonnet::unescape($name)}
1710: =&Apache::lonnet::unescape($value);
1711: } split(/\&/,$reply);
1712: }
1713: $loadedcaches{$sname.'_'.$sdom}=1;
1714: }
1715: }
1716:
1717: # ===================================================== Calculated sheets cache
1718: #
1719: # Load previously cached assessment spreadsheets for this student
1720: #
1721:
1.12 www 1722: # ================================================================ Main handler
1.28 www 1723: #
1724: # Interactive call to screen
1725: #
1726: #
1727:
1.3 www 1728:
1729: sub handler {
1.7 www 1730: my $r=shift;
1731:
1.28 www 1732: if ($r->header_only) {
1.7 www 1733: $r->content_type('text/html');
1734: $r->send_http_header;
1735: return OK;
1.28 www 1736: }
1737:
1738: # ---------------------------------------------------- Global directory configs
1739:
1.29 www 1740: $includedir=$r->dir_config('lonIncludes');
1.28 www 1741: $tmpdir=$r->dir_config('lonDaemons').'/tmp/';
1.3 www 1742:
1.7 www 1743: # ----------------------------------------------------- Needs to be in a course
1.3 www 1744:
1.29 www 1745: if ($ENV{'request.course.fn'}) {
1.10 www 1746:
1747: # --------------------------- Get query string for limited number of parameters
1.17 www 1748:
1.10 www 1749: map {
1750: my ($name, $value) = split(/=/,$_);
1751: $value =~ tr/+/ /;
1752: $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.19 www 1753: if (($name eq 'uname') || ($name eq 'udom') ||
1754: ($name eq 'usymb') || ($name eq 'ufn')) {
1.10 www 1755: unless ($ENV{'form.'.$name}) {
1756: $ENV{'form.'.$name}=$value;
1757: }
1758: }
1759: } (split(/&/,$ENV{'QUERY_STRING'}));
1760:
1761: # ------------------------------------------- Nothing there? Must be login user
1.29 www 1762:
1763: my $aname;
1764: my $adom;
1765:
1.10 www 1766: unless ($ENV{'form.uname'}) {
1.29 www 1767: $aname=$ENV{'user.name'};
1768: $adom=$ENV{'user.domain'};
1.11 www 1769: } else {
1.29 www 1770: $aname=$ENV{'form.uname'};
1771: $adom=$ENV{'form.udom'};
1.10 www 1772: }
1.14 www 1773:
1.10 www 1774: # ------------------------------------------------------------------- Open page
1775:
1.7 www 1776: $r->content_type('text/html');
1.11 www 1777: $r->header_out('Cache-control','no-cache');
1778: $r->header_out('Pragma','no-cache');
1.7 www 1779: $r->send_http_header;
1.3 www 1780:
1.14 www 1781: # --------------------------------------------------------------- Screen output
1782:
1.10 www 1783: $r->print('<html><head><title>LON-CAPA Spreadsheet</title>');
1784: $r->print(<<ENDSCRIPT);
1785: <script language="JavaScript">
1786:
1787: function celledit(cn,cf) {
1788: var cnf=prompt(cn,cf);
1789: if (cnf!=null) {
1790: document.sheet.unewfield.value=cn;
1791: document.sheet.unewformula.value=cnf;
1792: document.sheet.submit();
1793: }
1794: }
1795:
1796: </script>
1797: ENDSCRIPT
1798: $r->print('</head><body bgcolor="#FFFFFF">'.
1.21 www 1799: '<img align=right src=/adm/lonIcons/lonlogos.gif>'.
1800: '<h1>LON-CAPA Spreadsheet</h1>'.
1.10 www 1801: '<form action="'.$r->uri.'" name=sheet method=post>'.
1802: &hiddenfield('uname',$ENV{'form.uname'}).
1803: &hiddenfield('udom',$ENV{'form.udom'}).
1804: &hiddenfield('usymb',$ENV{'form.usymb'}).
1805: &hiddenfield('unewfield','').
1806: &hiddenfield('unewformula',''));
1.29 www 1807:
1808: # ---------------------- Make sure that this gets out, even if user hits "stop"
1809:
1.24 www 1810: $r->rflush();
1.29 www 1811:
1.14 www 1812: # ---------------------------------------- Read new sheet or modified worksheet
1813:
1.19 www 1814: $r->uri=~/\/(\w+)$/;
1.14 www 1815:
1.29 www 1816: my $asheet=&makenewsheet($aname,$adom,$1,$ENV{'form.usymb'});
1817:
1.30 www 1818: # ------------------------ If a new formula had been entered, go from work copy
1819:
1820: if ($ENV{'form.unewfield'}) {
1821: $r->print('<h2>Modified Workcopy</h2>');
1822: $ENV{'form.unewformula'}=~s/\'/\"/g;
1823: $r->print('<p>New formula: '.$ENV{'form.unewfield'}.'='.
1824: $ENV{'form.unewformula'}.'<p>');
1825: &setfilename($asheet,$ENV{'form.ufn'});
1826: &tmpread($asheet,
1827: $ENV{'form.unewfield'},$ENV{'form.unewformula'});
1828:
1829: } elsif ($ENV{'form.saveas'}) {
1830: &setfilename($asheet,$ENV{'form.ufn'});
1831: &tmpread($asheet);
1832: } else {
1833: &readsheet($asheet,$ENV{'form.ufn'});
1834: }
1835:
1836: # -------------------------------------------------- Print out user information
1837:
1838: unless (&gettype($asheet) eq 'classcalc') {
1839: $r->print('<p><b>User:</b> '.&getuname($asheet).
1840: '<br><b>Domain:</b> '.&getudom($asheet));
1841: if (&getcsec($asheet) eq '-1') {
1842: $r->print('<h3><font color=red>'.
1843: 'Not a student in this course</font></h3>');
1844: } else {
1845: $r->print('<br><b>Section/Group:</b> '.&getcsec($asheet));
1846: }
1847: }
1848:
1849: # ---------------------------------------------------------------- Course title
1850:
1851: $r->print('<h1>'.
1852: $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.'</h1>');
1853:
1854:
1.22 www 1855: # ---------------------------------------------------- See if something to save
1.30 www 1856:
1857: if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {
1858: my $fname='';
1859: if ($ENV{'form.saveas'} && ($fname=$ENV{'form.newfn'})) {
1860: $fname=~s/\W/\_/g;
1861: if ($fname eq 'default') { $fname='course_default'; }
1862: $fname.='_'.&gettype($asheet);
1863: &setfilename($asheet,$fname);
1864: $ENV{'form.ufn'}=$fname;
1865: $r->print('<p>Saving spreadsheet: '.
1866: &writesheet($asheet,$ENV{'form.makedefufn'}).'<p>');
1867: }
1868: }
1869:
1.14 www 1870: # ------------------------------------------------ Write the modified worksheet
1871:
1.30 www 1872: $r->print('<b>Current sheet:</b> '.&getfilename($asheet).'<p>');
1873:
1874: &tmpwrite($asheet);
1875:
1876: # ----------------------------------------------------------------- Save dialog
1877:
1878:
1879: if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {
1880: my $fname=$ENV{'form.ufn'};
1881: $fname=~s/\_[^\_]+$//;
1882: if ($fname eq 'default') { $fname='course_default'; }
1883: $r->print('<input type=submit name=saveas value="Save as ...">'.
1884: '<input type=text size=20 name=newfn value="'.$fname.
1885: '"> (make default: <input type=checkbox name="makedefufn">)<p>');
1886: }
1887:
1888: $r->print(&hiddenfield('ufn',&getfilename($asheet)));
1889:
1.46 www 1890: # --------------------------------------------------------------- Cached sheets
1.48 www 1891:
1892: &expirationdates();
1.46 www 1893:
1894: undef %oldsheets;
1895: undef %loadedcaches;
1.44 www 1896:
1.46 www 1897: if (&gettype($asheet) eq 'classcalc') {
1898: $r->print("Loading previously calculated student sheets ...<br>\n");
1899: $r->rflush();
1900: &cachedcsheets();
1901: } elsif (&gettype($asheet) eq 'studentcalc') {
1902: $r->print("Loading previously calculated assessment sheets ...<br>\n");
1903: $r->rflush();
1904: &cachedssheets(&getuname($asheet),&getudom($asheet),
1905: &getuhome($asheet));
1906: }
1.30 www 1907:
1908: # ----------------------------------------------------- Update sheet, load rows
1.14 www 1909:
1.44 www 1910: $r->print("Loaded sheet(s), updating rows ...<br>\n");
1.36 www 1911: $r->rflush();
1912:
1.29 www 1913: &updatesheet($asheet);
1.36 www 1914:
1915: $r->print("Updated rows, loading row data ...<br>\n");
1916: $r->rflush();
1917:
1.37 www 1918: &loadrows($asheet,$r);
1.14 www 1919:
1.36 www 1920: $r->print("Loaded row data, calculating sheet ...<br>\n");
1921: $r->rflush();
1.14 www 1922:
1.29 www 1923: my $calcoutput=&calcsheet($asheet);
1924: $r->print('<h3><font color=red>'.$calcoutput.'</h3></font>');
1.8 www 1925:
1.29 www 1926: &outsheet($r,$asheet);
1.10 www 1927: $r->print('</form></body></html>');
1.29 www 1928:
1.14 www 1929: # ------------------------------------------------------------------------ Done
1.7 www 1930: } else {
1931: # ----------------------------- Not in a course, or not allowed to modify parms
1932: $ENV{'user.error.msg'}=
1933: $r->uri.":opa:0:0:Cannot modify spreadsheet";
1934: return HTTP_NOT_ACCEPTABLE;
1935: }
1.3 www 1936: return OK;
1.28 www 1937:
1.1 www 1938: }
1939:
1940: 1;
1941: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>