Annotation of loncom/interface/lonspreadsheet.pm, revision 1.52
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.52 ! www 8: # 03/19,03/20,03/21,03/27 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:
1.52 ! www 1096: my %allkeys=('timestamp' =>
! 1097: 'Timestamp of Last Transaction<br>timestamp');
1.5 www 1098: my %allassess=();
1099:
1.50 www 1100: my $adduserstr='';
1101: if ((&getuname($safeeval) ne $ENV{'user.name'}) ||
1102: (&getudom($safeeval) ne $ENV{'user.domain'})) {
1103: $adduserstr='&uname='.&getuname($safeeval).
1104: '&udom='.&getudom($safeeval);
1105: }
1106:
1.5 www 1107: map {
1108: if ($_=~/^src\_(\d+)\.(\d+)$/) {
1109: my $mapid=$1;
1110: my $resid=$2;
1111: my $id=$mapid.'.'.$resid;
1112: my $srcf=$bighash{$_};
1113: if ($srcf=~/\.(problem|exam|quiz|assess|survey|form)$/) {
1114: my $symb=
1115: &Apache::lonnet::declutter($bighash{'map_id_'.$mapid}).
1116: '___'.$resid.'___'.
1117: &Apache::lonnet::declutter($srcf);
1.38 www 1118: $allassess{$symb}=
1.50 www 1119: '<a href="/adm/assesscalc?usymb='.$symb.$adduserstr.'">'.
1120: $bighash{'title_'.$id}.'</a>';
1.6 www 1121: if ($stype eq 'assesscalc') {
1.5 www 1122: map {
1.11 www 1123: if (($_=~/^stores\_(.*)/) || ($_=~/^parameter\_(.*)/)) {
1.5 www 1124: my $key=$_;
1125: my $display=
1126: &Apache::lonnet::metadata($srcf,$key.'.display');
1127: unless ($display) {
1.40 www 1128: $display.=
1.5 www 1129: &Apache::lonnet::metadata($srcf,$key.'.name');
1130: }
1.40 www 1131: $display.='<br>'.$key;
1.5 www 1132: $allkeys{$key}=$display;
1133: }
1134: } split(/\,/,&Apache::lonnet::metadata($srcf,'keys'));
1135: }
1136: }
1137: }
1138: } keys %bighash;
1139: untie(%bighash);
1140:
1141: #
1.11 www 1142: # %allkeys has a list of storage and parameter displays by unikey
1.5 www 1143: # %allassess has a list of all resource displays by symb
1144: #
1.6 www 1145:
1146: if ($stype eq 'assesscalc') {
1147: %current=%allkeys;
1148: } elsif ($stype eq 'studentcalc') {
1149: %current=%allassess;
1150: }
1.35 www 1151: $updatedata{$ENV{'request.course.fn'}.'_'.$stype}=
1152: join('___;___',%current);
1153: } else {
1154: return 'Could not access course data';
1155: }
1156: # ------------------------------------------------------ Get current from cache
1157: } else {
1158: %current=split(/\_\_\_\;\_\_\_/,
1159: $updatedata{$ENV{'request.course.fn'}.'_'.$stype});
1160: }
1161: # -------------------- Find discrepancies between the course row table and this
1162: #
1163: my %f=&getformulas($safeeval);
1164: my $changed=0;
1.6 www 1165:
1166: my $maxrow=0;
1167: my %existing=();
1168:
1169: # ----------------------------------------------------------- Now obsolete rows
1.5 www 1170: map {
1.6 www 1171: if ($_=~/^A(\d+)/) {
1172: $maxrow=($1>$maxrow)?$1:$maxrow;
1173: $existing{$f{$_}}=1;
1.17 www 1174: unless ((defined($current{$f{$_}})) || (!$1)) {
1.6 www 1175: $f{$_}='!!! Obsolete';
1176: $changed=1;
1.5 www 1177: }
1178: }
1179: } keys %f;
1.6 www 1180:
1181: # -------------------------------------------------------- New and unknown keys
1182:
1183: map {
1184: unless ($existing{$_}) {
1185: $changed=1;
1186: $maxrow++;
1187: $f{'A'.$maxrow}=$_;
1188: }
1189: } keys %current;
1.35 www 1190:
1.6 www 1191: if ($changed) { &setformulas($safeeval,%f); }
1192:
1193: &setmaxrow($safeeval,$maxrow);
1194: &setrowlabels($safeeval,%current);
1.35 www 1195:
1196: undef %current;
1197: undef %existing;
1.5 www 1198: }
1.3 www 1199:
1.24 www 1200: # ------------------------------------------------ Load data for one assessment
1.16 www 1201:
1.29 www 1202: sub loadstudent {
1.16 www 1203: my $safeeval=shift;
1204: my %c=();
1205: my %f=&getformulas($safeeval);
1.39 www 1206: $cachedassess=&getuname($safeeval).':'.&getudom($safeeval);
1207: %cachedstores=();
1208: {
1209: my $reply=&Apache::lonnet::reply('dump:'.&getudom($safeeval).':'.
1210: &getuname($safeeval).':'.
1211: &getcid($safeeval),
1212: &getuhome($safeeval));
1213: unless ($reply=~/^error\:/) {
1214: map {
1215: my ($name,$value)=split(/\=/,$_);
1216: $cachedstores{&Apache::lonnet::unescape($name)}=
1217: &Apache::lonnet::unescape($value);
1218: } split(/\&/,$reply);
1219: }
1220: }
1.36 www 1221: my @assessdata=();
1.16 www 1222: map {
1.17 www 1223: if ($_=~/^A(\d+)/) {
1224: my $row=$1;
1.42 www 1225: unless (($f{$_}=~/^\!/) || ($row==0)) {
1.36 www 1226: @assessdata=&exportsheet(&getuname($safeeval),
1227: &getudom($safeeval),
1228: 'assesscalc',$f{$_});
1.17 www 1229: my $index=0;
1.30 www 1230: map {
1231: if ($assessdata[$index]) {
1.41 www 1232: my $col=$_;
1233: if ($assessdata[$index]=~/\D/) {
1234: $c{$col.$row}="'".$assessdata[$index]."'";
1235: } else {
1236: $c{$col.$row}=$assessdata[$index];
1237: }
1238: unless ($col eq 'A') {
1239: $f{$col.$row}='import';
1.30 www 1240: }
1241: }
1242: $index++;
1243: } ('A','B','C','D','E','F','G','H','I','J','K','L','M',
1244: 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
1.16 www 1245: }
1246: }
1247: } keys %f;
1.39 www 1248: $cachedassess='';
1249: undef %cachedstores;
1.18 www 1250: &setformulas($safeeval,%f);
1.16 www 1251: &setconstants($safeeval,%c);
1252: }
1253:
1.24 www 1254: # --------------------------------------------------- Load data for one student
1255:
1.30 www 1256: sub loadcourse {
1.37 www 1257: my ($safeeval,$r)=@_;
1.24 www 1258: my %c=();
1259: my %f=&getformulas($safeeval);
1.37 www 1260: my $total=0;
1261: map {
1262: if ($_=~/^A(\d+)/) {
1263: unless ($f{$_}=~/^\!/) { $total++; }
1264: }
1265: } keys %f;
1266: my $now=0;
1267: my $since=time;
1.39 www 1268: $r->print(<<ENDPOP);
1269: <script>
1270: popwin=open('','popwin','width=400,height=100');
1271: popwin.document.writeln('<html><body bgcolor="#FFFFFF">'+
1.50 www 1272: '<h3>Spreadsheet Calculation Progress</h3>'+
1.39 www 1273: '<form name=popremain>'+
1274: '<input type=text size=35 name=remaining value=Starting></form>'+
1275: '</body></html>');
1.42 www 1276: popwin.document.close();
1.39 www 1277: </script>
1278: ENDPOP
1.37 www 1279: $r->rflush();
1.24 www 1280: map {
1281: if ($_=~/^A(\d+)/) {
1282: my $row=$1;
1.42 www 1283: unless (($f{$_}=~/^\!/) || ($row==0)) {
1.37 www 1284: my @studentdata=&exportsheet(split(/\:/,$f{$_}),
1.30 www 1285: 'studentcalc');
1.37 www 1286: undef %userrdatas;
1287: $now++;
1.39 www 1288: $r->print('<script>popwin.document.popremain.remaining.value="'.
1.37 www 1289: $now.'/'.$total.': '.int((time-$since)/$now*($total-$now)).
1.39 www 1290: ' secs remaining";</script>');
1.37 www 1291: $r->rflush();
1292:
1.24 www 1293: my $index=0;
1.30 www 1294: map {
1295: if ($studentdata[$index]) {
1.41 www 1296: my $col=$_;
1297: if ($studentdata[$index]=~/\D/) {
1298: $c{$col.$row}="'".$studentdata[$index]."'";
1299: } else {
1300: $c{$col.$row}=$studentdata[$index];
1301: }
1302: unless ($col eq 'A') {
1303: $f{$col.$row}='import';
1.30 www 1304: }
1305: }
1306: $index++;
1307: } ('A','B','C','D','E','F','G','H','I','J','K','L','M',
1308: 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z');
1.24 www 1309: }
1310: }
1311: } keys %f;
1312: &setformulas($safeeval,%f);
1313: &setconstants($safeeval,%c);
1.43 www 1314: $r->print('<script>popwin.close()</script>');
1.37 www 1315: $r->rflush();
1.24 www 1316: }
1317:
1.6 www 1318: # ------------------------------------------------ Load data for one assessment
1319:
1.29 www 1320: sub loadassessment {
1321: my $safeeval=shift;
1322:
1323: my $uhome=&getuhome($safeeval);
1324: my $uname=&getuname($safeeval);
1325: my $udom=&getudom($safeeval);
1326: my $symb=&getusymb($safeeval);
1327: my $cid=&getcid($safeeval);
1328: my $cnum=&getcnum($safeeval);
1329: my $cdom=&getcdom($safeeval);
1330: my $chome=&getchome($safeeval);
1331:
1.6 www 1332: my $namespace;
1.29 www 1333: unless ($namespace=$cid) { return ''; }
1.11 www 1334:
1335: # ----------------------------------------------------------- Get stored values
1.39 www 1336:
1337: my %returnhash=();
1338:
1339: if ($cachedassess eq $uname.':'.$udom) {
1340: #
1341: # get data out of the dumped stores
1342: #
1343:
1344: my $version=$cachedstores{'version:'.$symb};
1345: my $scope;
1346: for ($scope=1;$scope<=$version;$scope++) {
1347: map {
1348: $returnhash{$_}=$cachedstores{$scope.':'.$symb.':'.$_};
1349: } split(/\:/,$cachedstores{$scope.':keys:'.$symb});
1350: }
1351:
1352: } else {
1353: #
1354: # restore individual
1355: #
1356:
1.11 www 1357: my $answer=&Apache::lonnet::reply(
1.15 www 1358: "restore:$udom:$uname:".
1359: &Apache::lonnet::escape($namespace).":".
1360: &Apache::lonnet::escape($symb),$uhome);
1.6 www 1361: map {
1362: my ($name,$value)=split(/\=/,$_);
1.11 www 1363: $returnhash{&Apache::lonnet::unescape($name)}=
1364: &Apache::lonnet::unescape($value);
1.6 www 1365: } split(/\&/,$answer);
1366: my $version;
1367: for ($version=1;$version<=$returnhash{'version'};$version++) {
1368: map {
1369: $returnhash{$_}=$returnhash{$version.':'.$_};
1370: } split(/\:/,$returnhash{$version.':keys'});
1371: }
1.39 www 1372: }
1.11 www 1373: # ----------------------------- returnhash now has all stores for this resource
1374:
1375: # ---------------------------- initialize coursedata and userdata for this user
1.31 www 1376: undef %courseopt;
1377: undef %useropt;
1.29 www 1378:
1379: my $userprefix=$uname.'_'.$udom.'_';
1380:
1.11 www 1381: unless ($uhome eq 'no_host') {
1382: # -------------------------------------------------------------- Get coursedata
1.13 www 1383: unless
1.32 www 1384: ((time-$courserdatas{$cid.'.last_cache'})<240) {
1.29 www 1385: my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.
1386: ':resourcedata',$chome);
1.11 www 1387: if ($reply!~/^error\:/) {
1.29 www 1388: $courserdatas{$cid}=$reply;
1389: $courserdatas{$cid.'.last_cache'}=time;
1.11 www 1390: }
1391: }
1392: map {
1393: my ($name,$value)=split(/\=/,$_);
1.29 www 1394: $courseopt{$userprefix.&Apache::lonnet::unescape($name)}=
1.11 www 1395: &Apache::lonnet::unescape($value);
1.36 www 1396: } split(/\&/,$courserdatas{$cid});
1.11 www 1397: # --------------------------------------------------- Get userdata (if present)
1.13 www 1398: unless
1.32 www 1399: ((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) {
1.11 www 1400: my $reply=
1401: &Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome);
1402: if ($reply!~/^error\:/) {
1403: $userrdatas{$uname.'___'.$udom}=$reply;
1.13 www 1404: $userrdatas{$uname.'___'.$udom.'.last_cache'}=time;
1.11 www 1405: }
1406: }
1407: map {
1408: my ($name,$value)=split(/\=/,$_);
1.29 www 1409: $useropt{$userprefix.&Apache::lonnet::unescape($name)}=
1.15 www 1410: &Apache::lonnet::unescape($value);
1.11 www 1411: } split(/\&/,$userrdatas{$uname.'___'.$udom});
1.29 www 1412: }
1413: # ----------------- now courseopt, useropt initialized for this user and course
1414: # (used by parmval)
1415:
1416: my %c=();
1.6 www 1417:
1.29 www 1418: if (tie(%parmhash,'GDBM_File',
1419: &getcfn($safeeval).'_parms.db',&GDBM_READER,0640)) {
1.6 www 1420: my %f=&getformulas($safeeval);
1421: map {
1422: if ($_=~/^A/) {
1423: unless ($f{$_}=~/^\!/) {
1.11 www 1424: if ($f{$_}=~/^parameter/) {
1.40 www 1425: my $val=&parmval($f{$_},$safeeval);
1426: $c{$_}=$val;
1427: $c{$f{$_}}=$val;
1.11 www 1428: } else {
1.15 www 1429: my $key=$f{$_};
1.40 www 1430: my $ckey=$key;
1.15 www 1431: $key=~s/^stores\_/resource\./;
1432: $key=~s/\_/\./;
1433: $c{$_}=$returnhash{$key};
1.40 www 1434: $c{$ckey}=$returnhash{$key};
1.11 www 1435: }
1436: }
1.6 www 1437: }
1438: } keys %f;
1.29 www 1439: untie(%parmhash);
1440: }
1441: &setconstants($safeeval,%c);
1.6 www 1442: }
1443:
1.10 www 1444: # --------------------------------------------------------- Various form fields
1445:
1446: sub textfield {
1447: my ($title,$name,$value)=@_;
1448: return "\n<p><b>$title:</b><br>".
1449: '<input type=text name="'.$name.'" size=80 value="'.$value.'">';
1450: }
1451:
1452: sub hiddenfield {
1453: my ($name,$value)=@_;
1454: return "\n".'<input type=hidden name="'.$name.'" value="'.$value.'">';
1455: }
1456:
1457: sub selectbox {
1458: my ($title,$name,$value,%options)=@_;
1459: my $selout="\n<p><b>$title:</b><br>".'<select name="'.$name.'">';
1460: map {
1461: $selout.='<option value="'.$_.'"';
1462: if ($_ eq $value) { $selout.=' selected'; }
1463: $selout.='>'.$options{$_}.'</option>';
1464: } sort keys %options;
1465: return $selout.'</select>';
1466: }
1467:
1.28 www 1468: # =============================================== Update information in a sheet
1469: #
1470: # Add new users or assessments, etc.
1471: #
1472:
1473: sub updatesheet {
1474: my $safeeval=shift;
1475: my $stype=&gettype($safeeval);
1476: if ($stype eq 'classcalc') {
1477: return &updateclasssheet($safeeval);
1478: } else {
1479: return &updatestudentassesssheet($safeeval);
1480: }
1481: }
1482:
1483: # =================================================== Load the rows for a sheet
1484: #
1485: # Import the data for rows
1486: #
1487:
1.37 www 1488: sub loadrows {
1489: my ($safeeval,$r)=@_;
1.28 www 1490: my $stype=&gettype($safeeval);
1491: if ($stype eq 'classcalc') {
1.37 www 1492: &loadcourse($safeeval,$r);
1.28 www 1493: } elsif ($stype eq 'studentcalc') {
1.29 www 1494: &loadstudent($safeeval);
1.28 www 1495: } else {
1.29 www 1496: &loadassessment($safeeval);
1.28 www 1497: }
1498: }
1499:
1.47 www 1500: # ======================================================= Forced recalculation?
1501:
1502: sub checkthis {
1503: my ($keyname,$time)=@_;
1504: return ($time<$expiredates{$keyname});
1505: }
1506: sub forcedrecalc {
1507: my ($uname,$udom,$stype,$usymb)=@_;
1508: my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;
1509: my $time=$oldsheets{$key.'.time'};
1510: unless ($time) { return 1; }
1511: if ($stype eq 'assesscalc') {
1512: my $map=(split(/\_\_\_/,$usymb))[0];
1513: if (&checkthis('::assesscalc:',$time) ||
1514: &checkthis('::assesscalc:'.$map,$time) ||
1515: &checkthis('::assesscalc:'.$usymb,$time) ||
1.49 www 1516: &checkthis($uname.':'.$udom.':assesscalc:',$time) ||
1517: &checkthis($uname.':'.$udom.':assesscalc:'.$map,$time) ||
1518: &checkthis($uname.':'.$udom.':assesscalc:'.$usymb,$time)) {
1.47 www 1519: return 1;
1520: }
1521: } else {
1522: if (&checkthis('::studentcalc:',$time) ||
1.51 www 1523: &checkthis($uname.':'.$udom.':studentcalc:',$time)) {
1.47 www 1524: return 1;
1525: }
1526: }
1527: return 0;
1528: }
1529:
1.28 www 1530: # ============================================================== Export handler
1531: #
1532: # Non-interactive call from with program
1533: #
1534:
1535: sub exportsheet {
1.44 www 1536: my ($uname,$udom,$stype,$usymb,$fn)=@_;
1537: my @exportarr=();
1538: #
1539: # Check if cached
1540: #
1.46 www 1541:
1.44 www 1542: my $key=$uname.':'.$udom.':'.$stype.':'.$usymb;
1543: my $found='';
1544:
1545: if ($oldsheets{$key}) {
1546: map {
1547: my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);
1548: if ($name eq $fn) {
1549: $found=$value;
1550: }
1551: } split(/\_\_\_\&\_\_\_/,$oldsheets{$key});
1552: }
1553:
1.46 www 1554: unless ($found) {
1555: &cachedssheets($uname,$udom,&Apache::lonnet::homeserver($uname,$udom));
1556: if ($oldsheets{$key}) {
1557: map {
1558: my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);
1559: if ($name eq $fn) {
1560: $found=$value;
1561: }
1562: } split(/\_\_\_\&\_\_\_/,$oldsheets{$key});
1563: }
1564: }
1.47 www 1565: #
1566: # Check if still valid
1567: #
1568: if ($found) {
1569: if (&forcedrecalc($uname,$udom,$stype,$usymb)) {
1570: $found='';
1571: }
1572: }
1573:
1.44 www 1574: if ($found) {
1575: #
1576: # Return what was cached
1577: #
1578: @exportarr=split(/\_\_\_\;\_\_\_/,$found);
1579:
1580: } else {
1581: #
1582: # Not cached
1.46 www 1583: #
1584:
1.29 www 1585: my $thissheet=&makenewsheet($uname,$udom,$stype,$usymb);
1.28 www 1586: &readsheet($thissheet,$fn);
1587: &updatesheet($thissheet);
1588: &loadrows($thissheet);
1.44 www 1589: &calcsheet($thissheet);
1590: @exportarr=&exportdata($thissheet);
1591: #
1592: # Store now
1593: #
1594: my $cid=$ENV{'request.course.id'};
1.47 www 1595: my $current='';
1.46 www 1596: if ($stype eq 'studentcalc') {
1597: $current=&Apache::lonnet::reply('get:'.
1.44 www 1598: $ENV{'course.'.$cid.'.domain'}.':'.
1599: $ENV{'course.'.$cid.'.num'}.
1600: ':nohist_calculatedsheets:'.
1601: &Apache::lonnet::escape($key),
1602: $ENV{'course.'.$cid.'.home'});
1.46 www 1603: } else {
1604: $current=&Apache::lonnet::reply('get:'.
1605: &getudom($thissheet).':'.
1606: &getuname($thissheet).
1607: ':nohist_calculatedsheets_'.
1608: $ENV{'request.course.id'}.':'.
1609: &Apache::lonnet::escape($key),
1610: &getuhome($thissheet));
1611:
1612: }
1.44 www 1613: my %currentlystored=();
1614: unless ($current=~/^error\:/) {
1615: map {
1616: my ($name,$value)=split(/\_\_\_\=\_\_\_/,$_);
1617: $currentlystored{$name}=$value;
1618: } split(/\_\_\_\&\_\_\_/,&Apache::lonnet::unescape($current));
1619: }
1620: $currentlystored{$fn}=join('___;___',@exportarr);
1621:
1622: my $newstore='';
1623: map {
1624: if ($newstore) { $newstore.='___&___'; }
1625: $newstore.=$_.'___=___'.$currentlystored{$_};
1626: } keys %currentlystored;
1.47 www 1627: my $now=time;
1.46 www 1628: if ($stype eq 'studentcalc') {
1629: &Apache::lonnet::reply('put:'.
1.44 www 1630: $ENV{'course.'.$cid.'.domain'}.':'.
1631: $ENV{'course.'.$cid.'.num'}.
1632: ':nohist_calculatedsheets:'.
1633: &Apache::lonnet::escape($key).'='.
1.47 www 1634: &Apache::lonnet::escape($newstore).'&'.
1635: &Apache::lonnet::escape($key).'.time='.$now,
1.44 www 1636: $ENV{'course.'.$cid.'.home'});
1.46 www 1637: } else {
1638: &Apache::lonnet::reply('put:'.
1639: &getudom($thissheet).':'.
1640: &getuname($thissheet).
1641: ':nohist_calculatedsheets_'.
1642: $ENV{'request.course.id'}.':'.
1643: &Apache::lonnet::escape($key).'='.
1.47 www 1644: &Apache::lonnet::escape($newstore).'&'.
1645: &Apache::lonnet::escape($key).'.time='.$now,
1.46 www 1646: &getuhome($thissheet));
1647: }
1.44 www 1648: }
1649: return @exportarr;
1650: }
1.48 www 1651: # ============================================================ Expiration Dates
1652: #
1653: # Load previously cached student spreadsheets for this course
1654: #
1655:
1656: sub expirationdates {
1657: undef %expiredates;
1658: my $cid=$ENV{'request.course.id'};
1659: my $reply=&Apache::lonnet::reply('dump:'.
1660: $ENV{'course.'.$cid.'.domain'}.':'.
1661: $ENV{'course.'.$cid.'.num'}.
1662: ':nohist_expirationdates',
1663: $ENV{'course.'.$cid.'.home'});
1664: unless ($reply=~/^error\:/) {
1665: map {
1666: my ($name,$value)=split(/\=/,$_);
1667: $expiredates{&Apache::lonnet::unescape($name)}
1668: =&Apache::lonnet::unescape($value);
1669: } split(/\&/,$reply);
1670: }
1671: }
1.44 www 1672:
1673: # ===================================================== Calculated sheets cache
1674: #
1.46 www 1675: # Load previously cached student spreadsheets for this course
1.44 www 1676: #
1677:
1.46 www 1678: sub cachedcsheets {
1.44 www 1679: my $cid=$ENV{'request.course.id'};
1680: my $reply=&Apache::lonnet::reply('dump:'.
1681: $ENV{'course.'.$cid.'.domain'}.':'.
1682: $ENV{'course.'.$cid.'.num'}.
1683: ':nohist_calculatedsheets',
1684: $ENV{'course.'.$cid.'.home'});
1685: unless ($reply=~/^error\:/) {
1686: map {
1687: my ($name,$value)=split(/\=/,$_);
1688: $oldsheets{&Apache::lonnet::unescape($name)}
1689: =&Apache::lonnet::unescape($value);
1690: } split(/\&/,$reply);
1691: }
1.28 www 1692: }
1693:
1.46 www 1694: # ===================================================== Calculated sheets cache
1695: #
1696: # Load previously cached assessment spreadsheets for this student
1697: #
1698:
1699: sub cachedssheets {
1700: my ($sname,$sdom,$shome)=@_;
1701: unless (($loadedcaches{$sname.'_'.$sdom}) || ($shome eq 'no_host')) {
1702: my $cid=$ENV{'request.course.id'};
1703: my $reply=&Apache::lonnet::reply('dump:'.$sdom.':'.$sname.
1704: ':nohist_calculatedsheets_'.
1705: $ENV{'request.course.id'},
1706: $shome);
1707: unless ($reply=~/^error\:/) {
1708: map {
1709: my ($name,$value)=split(/\=/,$_);
1710: $oldsheets{&Apache::lonnet::unescape($name)}
1711: =&Apache::lonnet::unescape($value);
1712: } split(/\&/,$reply);
1713: }
1714: $loadedcaches{$sname.'_'.$sdom}=1;
1715: }
1716: }
1717:
1718: # ===================================================== Calculated sheets cache
1719: #
1720: # Load previously cached assessment spreadsheets for this student
1721: #
1722:
1.12 www 1723: # ================================================================ Main handler
1.28 www 1724: #
1725: # Interactive call to screen
1726: #
1727: #
1728:
1.3 www 1729:
1730: sub handler {
1.7 www 1731: my $r=shift;
1732:
1.28 www 1733: if ($r->header_only) {
1.7 www 1734: $r->content_type('text/html');
1735: $r->send_http_header;
1736: return OK;
1.28 www 1737: }
1738:
1739: # ---------------------------------------------------- Global directory configs
1740:
1.29 www 1741: $includedir=$r->dir_config('lonIncludes');
1.28 www 1742: $tmpdir=$r->dir_config('lonDaemons').'/tmp/';
1.3 www 1743:
1.7 www 1744: # ----------------------------------------------------- Needs to be in a course
1.3 www 1745:
1.29 www 1746: if ($ENV{'request.course.fn'}) {
1.10 www 1747:
1748: # --------------------------- Get query string for limited number of parameters
1.17 www 1749:
1.10 www 1750: map {
1751: my ($name, $value) = split(/=/,$_);
1752: $value =~ tr/+/ /;
1753: $value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C",hex($1))/eg;
1.19 www 1754: if (($name eq 'uname') || ($name eq 'udom') ||
1755: ($name eq 'usymb') || ($name eq 'ufn')) {
1.10 www 1756: unless ($ENV{'form.'.$name}) {
1757: $ENV{'form.'.$name}=$value;
1758: }
1759: }
1760: } (split(/&/,$ENV{'QUERY_STRING'}));
1761:
1762: # ------------------------------------------- Nothing there? Must be login user
1.29 www 1763:
1764: my $aname;
1765: my $adom;
1766:
1.10 www 1767: unless ($ENV{'form.uname'}) {
1.29 www 1768: $aname=$ENV{'user.name'};
1769: $adom=$ENV{'user.domain'};
1.11 www 1770: } else {
1.29 www 1771: $aname=$ENV{'form.uname'};
1772: $adom=$ENV{'form.udom'};
1.10 www 1773: }
1.14 www 1774:
1.10 www 1775: # ------------------------------------------------------------------- Open page
1776:
1.7 www 1777: $r->content_type('text/html');
1.11 www 1778: $r->header_out('Cache-control','no-cache');
1779: $r->header_out('Pragma','no-cache');
1.7 www 1780: $r->send_http_header;
1.3 www 1781:
1.14 www 1782: # --------------------------------------------------------------- Screen output
1783:
1.10 www 1784: $r->print('<html><head><title>LON-CAPA Spreadsheet</title>');
1785: $r->print(<<ENDSCRIPT);
1786: <script language="JavaScript">
1787:
1788: function celledit(cn,cf) {
1789: var cnf=prompt(cn,cf);
1790: if (cnf!=null) {
1791: document.sheet.unewfield.value=cn;
1792: document.sheet.unewformula.value=cnf;
1793: document.sheet.submit();
1794: }
1795: }
1796:
1797: </script>
1798: ENDSCRIPT
1799: $r->print('</head><body bgcolor="#FFFFFF">'.
1.21 www 1800: '<img align=right src=/adm/lonIcons/lonlogos.gif>'.
1801: '<h1>LON-CAPA Spreadsheet</h1>'.
1.10 www 1802: '<form action="'.$r->uri.'" name=sheet method=post>'.
1803: &hiddenfield('uname',$ENV{'form.uname'}).
1804: &hiddenfield('udom',$ENV{'form.udom'}).
1805: &hiddenfield('usymb',$ENV{'form.usymb'}).
1806: &hiddenfield('unewfield','').
1807: &hiddenfield('unewformula',''));
1.29 www 1808:
1809: # ---------------------- Make sure that this gets out, even if user hits "stop"
1810:
1.24 www 1811: $r->rflush();
1.29 www 1812:
1.14 www 1813: # ---------------------------------------- Read new sheet or modified worksheet
1814:
1.19 www 1815: $r->uri=~/\/(\w+)$/;
1.14 www 1816:
1.29 www 1817: my $asheet=&makenewsheet($aname,$adom,$1,$ENV{'form.usymb'});
1818:
1.30 www 1819: # ------------------------ If a new formula had been entered, go from work copy
1820:
1821: if ($ENV{'form.unewfield'}) {
1822: $r->print('<h2>Modified Workcopy</h2>');
1823: $ENV{'form.unewformula'}=~s/\'/\"/g;
1824: $r->print('<p>New formula: '.$ENV{'form.unewfield'}.'='.
1825: $ENV{'form.unewformula'}.'<p>');
1826: &setfilename($asheet,$ENV{'form.ufn'});
1827: &tmpread($asheet,
1828: $ENV{'form.unewfield'},$ENV{'form.unewformula'});
1829:
1830: } elsif ($ENV{'form.saveas'}) {
1831: &setfilename($asheet,$ENV{'form.ufn'});
1832: &tmpread($asheet);
1833: } else {
1834: &readsheet($asheet,$ENV{'form.ufn'});
1835: }
1836:
1837: # -------------------------------------------------- Print out user information
1838:
1839: unless (&gettype($asheet) eq 'classcalc') {
1840: $r->print('<p><b>User:</b> '.&getuname($asheet).
1841: '<br><b>Domain:</b> '.&getudom($asheet));
1842: if (&getcsec($asheet) eq '-1') {
1843: $r->print('<h3><font color=red>'.
1844: 'Not a student in this course</font></h3>');
1845: } else {
1846: $r->print('<br><b>Section/Group:</b> '.&getcsec($asheet));
1847: }
1848: }
1849:
1850: # ---------------------------------------------------------------- Course title
1851:
1852: $r->print('<h1>'.
1853: $ENV{'course.'.$ENV{'request.course.id'}.'.description'}.'</h1>');
1854:
1855:
1.22 www 1856: # ---------------------------------------------------- See if something to save
1.30 www 1857:
1858: if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {
1859: my $fname='';
1860: if ($ENV{'form.saveas'} && ($fname=$ENV{'form.newfn'})) {
1861: $fname=~s/\W/\_/g;
1862: if ($fname eq 'default') { $fname='course_default'; }
1863: $fname.='_'.&gettype($asheet);
1864: &setfilename($asheet,$fname);
1865: $ENV{'form.ufn'}=$fname;
1866: $r->print('<p>Saving spreadsheet: '.
1867: &writesheet($asheet,$ENV{'form.makedefufn'}).'<p>');
1868: }
1869: }
1870:
1.14 www 1871: # ------------------------------------------------ Write the modified worksheet
1872:
1.30 www 1873: $r->print('<b>Current sheet:</b> '.&getfilename($asheet).'<p>');
1874:
1875: &tmpwrite($asheet);
1876:
1877: # ----------------------------------------------------------------- Save dialog
1878:
1879:
1880: if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) {
1881: my $fname=$ENV{'form.ufn'};
1882: $fname=~s/\_[^\_]+$//;
1883: if ($fname eq 'default') { $fname='course_default'; }
1884: $r->print('<input type=submit name=saveas value="Save as ...">'.
1885: '<input type=text size=20 name=newfn value="'.$fname.
1886: '"> (make default: <input type=checkbox name="makedefufn">)<p>');
1887: }
1888:
1889: $r->print(&hiddenfield('ufn',&getfilename($asheet)));
1890:
1.46 www 1891: # --------------------------------------------------------------- Cached sheets
1.48 www 1892:
1893: &expirationdates();
1.46 www 1894:
1895: undef %oldsheets;
1896: undef %loadedcaches;
1.44 www 1897:
1.46 www 1898: if (&gettype($asheet) eq 'classcalc') {
1899: $r->print("Loading previously calculated student sheets ...<br>\n");
1900: $r->rflush();
1901: &cachedcsheets();
1902: } elsif (&gettype($asheet) eq 'studentcalc') {
1903: $r->print("Loading previously calculated assessment sheets ...<br>\n");
1904: $r->rflush();
1905: &cachedssheets(&getuname($asheet),&getudom($asheet),
1906: &getuhome($asheet));
1907: }
1.30 www 1908:
1909: # ----------------------------------------------------- Update sheet, load rows
1.14 www 1910:
1.44 www 1911: $r->print("Loaded sheet(s), updating rows ...<br>\n");
1.36 www 1912: $r->rflush();
1913:
1.29 www 1914: &updatesheet($asheet);
1.36 www 1915:
1916: $r->print("Updated rows, loading row data ...<br>\n");
1917: $r->rflush();
1918:
1.37 www 1919: &loadrows($asheet,$r);
1.14 www 1920:
1.36 www 1921: $r->print("Loaded row data, calculating sheet ...<br>\n");
1922: $r->rflush();
1.14 www 1923:
1.29 www 1924: my $calcoutput=&calcsheet($asheet);
1925: $r->print('<h3><font color=red>'.$calcoutput.'</h3></font>');
1.8 www 1926:
1.29 www 1927: &outsheet($r,$asheet);
1.10 www 1928: $r->print('</form></body></html>');
1.29 www 1929:
1.14 www 1930: # ------------------------------------------------------------------------ Done
1.7 www 1931: } else {
1932: # ----------------------------- Not in a course, or not allowed to modify parms
1933: $ENV{'user.error.msg'}=
1934: $r->uri.":opa:0:0:Cannot modify spreadsheet";
1935: return HTTP_NOT_ACCEPTABLE;
1936: }
1.3 www 1937: return OK;
1.28 www 1938:
1.1 www 1939: }
1940:
1941: 1;
1942: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>