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