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