version 1.26, 2001/01/01 20:52:10
|
version 1.33, 2001/01/03 14:27:30
|
Line 3
|
Line 3
|
# |
# |
# 11/11,11/15,11/27,12/04,12/05,12/06,12/07, |
# 11/11,11/15,11/27,12/04,12/05,12/06,12/07, |
# 12/08,12/09,12/11,12/12,12/15,12/16,12/18,12/19,12/30, |
# 12/08,12/09,12/11,12/12,12/15,12/16,12/18,12/19,12/30, |
# 01/01/01 Gerd Kortemeyer |
# 01/01/01,02/01,03/01 Gerd Kortemeyer |
|
|
package Apache::lonspreadsheet; |
package Apache::lonspreadsheet; |
|
|
Line 18 use HTML::TokeParser;
|
Line 18 use HTML::TokeParser;
|
|
|
# |
# |
# These cache hashes need to be independent of user, resource and course |
# These cache hashes need to be independent of user, resource and course |
# (user and course are in the keys) |
# (user and course can/should be in the keys) |
# |
# |
use vars qw(%spreadsheets %courserdatas %userrdatas); |
|
|
my %spreadsheets; |
|
my %courserdatas; |
|
my %userrdatas; |
|
my %defaultsheets; |
|
|
# |
# |
# These global hashes are dependent on user, course and resource, |
# These global hashes are dependent on user, course and resource, |
# and need to be initialized every time when a sheet is calculated |
# and need to be initialized every time when a sheet is calculated |
Line 28 use vars qw(%spreadsheets %courserdatas
|
Line 33 use vars qw(%spreadsheets %courserdatas
|
my %courseopt; |
my %courseopt; |
my %useropt; |
my %useropt; |
my %parmhash; |
my %parmhash; |
my $csec; |
|
my $uname; |
# Stuff that only the screen handler can know |
my $udom; |
|
|
my $includedir; |
|
my $tmpdir; |
|
|
# ============================================================================= |
# ============================================================================= |
# ===================================== Implements an instance of a spreadsheet |
# ===================================== Implements an instance of a spreadsheet |
Line 61 sub initsheet {
|
Line 68 sub initsheet {
|
|
|
$maxrow=0; |
$maxrow=0; |
$sheettype=''; |
$sheettype=''; |
|
|
|
# filename/reference of the sheet |
|
|
$filename=''; |
$filename=''; |
|
|
|
# user data |
|
$uname=''; |
|
$uhome=''; |
|
$udom=''; |
|
|
|
# course data |
|
|
|
$csec=''; |
|
$chome=''; |
|
$cnum=''; |
|
$cdom=''; |
|
$cid=''; |
|
$cfn=''; |
|
|
|
# symb |
|
|
|
$usymb=''; |
|
|
sub mask { |
sub mask { |
my ($lower,$upper)=@_; |
my ($lower,$upper)=@_; |
|
|
Line 382 sub outrow {
|
Line 410 sub outrow {
|
} |
} |
|
|
sub exportrowa { |
sub exportrowa { |
my $rowa=''; |
my @exportarray=(); |
map { |
map { |
$rowa.=$v{$_.'0'}."___;___"; |
$exportarray[$#exportarray+1]=$v{$_.'0'}; |
} ('A','B','C','D','E','F','G','H','I','J','K','L','M', |
} ('A','B','C','D','E','F','G','H','I','J','K','L','M', |
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z'); |
'N','O','P','Q','R','S','T','U','V','W','X','Y','Z'); |
$rowa=~s/\_\_\_\;\_\_\_$//; |
return @exportarray; |
return $rowa; |
|
} |
} |
|
|
# ------------------------------------------- End of "Inside of the safe space" |
# ------------------------------------------- End of "Inside of the safe space" |
Line 439 sub getformulas {
|
Line 466 sub getformulas {
|
return $safeeval->reval('%f'); |
return $safeeval->reval('%f'); |
} |
} |
|
|
# -------------------------------------------------------------------- Set type |
|
|
|
sub settype { |
|
my ($safeeval,$type)=@_; |
|
$safeeval->reval('$sheettype="'.$type.'";'); |
|
} |
|
|
|
# -------------------------------------------------------------------- Get type |
# -------------------------------------------------------------------- Get type |
|
|
sub gettype { |
sub gettype { |
my $safeeval=shift; |
my $safeeval=shift; |
return $safeeval->reval('$sheettype'); |
return $safeeval->reval('$sheettype'); |
} |
} |
|
|
# ------------------------------------------------------------------ Set maxrow |
# ------------------------------------------------------------------ Set maxrow |
|
|
sub setmaxrow { |
sub setmaxrow { |
Line 480 sub getfilename {
|
Line 501 sub getfilename {
|
return $safeeval->reval('$filename'); |
return $safeeval->reval('$filename'); |
} |
} |
|
|
|
# --------------------------------------------------------------- Get course ID |
|
|
|
sub getcid { |
|
my $safeeval=shift; |
|
return $safeeval->reval('$cid'); |
|
} |
|
|
|
# --------------------------------------------------------- Get course filename |
|
|
|
sub getcfn { |
|
my $safeeval=shift; |
|
return $safeeval->reval('$cfn'); |
|
} |
|
|
|
# ----------------------------------------------------------- Get course number |
|
|
|
sub getcnum { |
|
my $safeeval=shift; |
|
return $safeeval->reval('$cnum'); |
|
} |
|
|
|
# ------------------------------------------------------------- Get course home |
|
|
|
sub getchome { |
|
my $safeeval=shift; |
|
return $safeeval->reval('$chome'); |
|
} |
|
|
|
# ----------------------------------------------------------- Get course domain |
|
|
|
sub getcdom { |
|
my $safeeval=shift; |
|
return $safeeval->reval('$cdom'); |
|
} |
|
|
|
# ---------------------------------------------------------- Get course section |
|
|
|
sub getcsec { |
|
my $safeeval=shift; |
|
return $safeeval->reval('$csec'); |
|
} |
|
|
|
# --------------------------------------------------------------- Get user name |
|
|
|
sub getuname { |
|
my $safeeval=shift; |
|
return $safeeval->reval('$uname'); |
|
} |
|
|
|
# ------------------------------------------------------------- Get user domain |
|
|
|
sub getudom { |
|
my $safeeval=shift; |
|
return $safeeval->reval('$udom'); |
|
} |
|
|
|
# --------------------------------------------------------------- Get user home |
|
|
|
sub getuhome { |
|
my $safeeval=shift; |
|
return $safeeval->reval('$uhome'); |
|
} |
|
|
|
# -------------------------------------------------------------------- Get symb |
|
|
|
sub getusymb { |
|
my $safeeval=shift; |
|
return $safeeval->reval('$usymb'); |
|
} |
|
|
# ------------------------------------------------------------- Export of A-row |
# ------------------------------------------------------------- Export of A-row |
|
|
sub exportrow { |
sub exportdata { |
my $safeeval=shift; |
my $safeeval=shift; |
return $safeeval->reval('&exportrowa()'); |
return $safeeval->reval('&exportrowa()'); |
} |
} |
Line 490 sub exportrow {
|
Line 581 sub exportrow {
|
# ========================================================== End of Spreadsheet |
# ========================================================== End of Spreadsheet |
# ============================================================================= |
# ============================================================================= |
|
|
|
# |
|
# Procedures for screen output |
|
# |
# --------------------------------------------- Produce output row n from sheet |
# --------------------------------------------- Produce output row n from sheet |
|
|
sub rown { |
sub rown { |
Line 600 sub outsheet {
|
Line 693 sub outsheet {
|
$r->print('</table>'); |
$r->print('</table>'); |
} |
} |
|
|
|
# |
|
# -------------------------------------- Read spreadsheet formulas for a course |
|
# |
|
|
|
sub readsheet { |
|
my ($safeeval,$fn)=@_; |
|
my $stype=&gettype($safeeval); |
|
my $cnum=&getcnum($safeeval); |
|
my $cdom=&getcdom($safeeval); |
|
my $chome=&getchome($safeeval); |
|
|
|
# --------- There is no filename. Look for defaults in course and global, cache |
|
|
|
unless($fn) { |
|
unless ($fn=$defaultsheets{$cnum.'_'.$cdom.'_'.$stype}) { |
|
$fn=&Apache::lonnet::reply('get:'.$cdom.':'.$cnum. |
|
':environment:spreadsheet_default_'.$stype, |
|
$chome); |
|
unless (($fn) && ($fn!~/^error\:/)) { |
|
$fn='default_'.$stype; |
|
} |
|
$defaultsheets{$cnum.'_'.$cdom.'_'.$stype}=$fn; |
|
} |
|
} |
|
|
|
# ---------------------------------------------------------- fn now has a value |
|
|
|
&setfilename($safeeval,$fn); |
|
|
# ------------------------------------- Read spreadsheet formulas from a course |
# ------------------------------------------------------ see if sheet is cached |
|
my $fstring=''; |
|
if ($fstring=$spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}) { |
|
&setformulas($safeeval,split(/\_\_\_\;\_\_\_/,$fstring)); |
|
} else { |
|
|
sub readsheet { |
# ---------------------------------------------------- Not cached, need to read |
my ($safeeval,$fn,$r)=@_; |
|
my %f=(); |
my %f=(); |
if (($fn eq '') || ($fn=~/^default\_/)) { |
|
|
if ($fn=~/^default\_/) { |
my $sheetxml=''; |
my $sheetxml=''; |
{ |
{ |
my $fh; |
my $fh; |
if ($fh=Apache::File->new($r->dir_config('lonIncludes'). |
if ($fh=Apache::File->new($includedir. |
'/default.'.&gettype($safeeval))) { |
'/default.'.&gettype($safeeval))) { |
$sheetxml=join('',<$fh>); |
$sheetxml=join('',<$fh>); |
} |
} |
} |
} |
my $parser=HTML::TokeParser->new(\$sheetxml); |
my $parser=HTML::TokeParser->new(\$sheetxml); |
my $token; |
my $token; |
while ($token=$parser->get_token) { |
while ($token=$parser->get_token) { |
if ($token->[0] eq 'S') { |
if ($token->[0] eq 'S') { |
if ($token->[1] eq 'field') { |
if ($token->[1] eq 'field') { |
$f{$token->[2]->{'col'}.$token->[2]->{'row'}}= |
$f{$token->[2]->{'col'}.$token->[2]->{'row'}}= |
Line 629 sub readsheet {
|
Line 754 sub readsheet {
|
$parser->get_text('/template'); |
$parser->get_text('/template'); |
} |
} |
} |
} |
} |
} |
&setfilename($safeeval,'default_'.&gettype($safeeval)); |
} else { |
} else { |
my $sheet=''; |
&setfilename($safeeval,$fn); |
my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum.':'.$fn, |
unless ($spreadsheets{$fn}) { |
$chome); |
$spreadsheets{$fn}=''; |
|
my $reply=&Apache::lonnet::reply('dump:'. |
|
$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'. |
|
$ENV{'course.'.$ENV{'request.course.id'}.'.num'}.':'.$fn, |
|
$ENV{'course.'.$ENV{'request.course.id'}.'.home'}); |
|
unless ($reply=~/^error\:/) { |
unless ($reply=~/^error\:/) { |
$spreadsheets{$fn}=$reply; |
$sheet=$reply; |
} |
} |
|
map { |
|
my ($name,$value)=split(/\=/,$_); |
|
$f{&Apache::lonnet::unescape($name)}= |
|
&Apache::lonnet::unescape($value); |
|
} split(/\&/,$sheet); |
} |
} |
map { |
# --------------------------------------------------------------- Cache and set |
my ($name,$value)=split(/\=/,$_); |
$spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f); |
$f{&Apache::lonnet::unescape($name)}= |
&setformulas($safeeval,%f); |
&Apache::lonnet::unescape($value); |
|
} split(/\&/,$spreadsheets{$fn}); |
|
} |
} |
|
} |
|
|
&setformulas($safeeval,%f); |
# -------------------------------------------------------- Make new spreadsheet |
|
|
|
sub makenewsheet { |
|
my ($uname,$udom,$stype,$usymb)=@_; |
|
my $safeeval=initsheet(); |
|
$safeeval->reval( |
|
'$uname="'.$uname. |
|
'";$udom="'.$udom. |
|
'";$uhome="'.&Apache::lonnet::homeserver($uname,$udom). |
|
'";$sheettype="'.$stype. |
|
'";$usymb="'.$usymb. |
|
'";$csec="'.&Apache::lonnet::usection($udom,$uname, |
|
$ENV{'request.course.id'}). |
|
'";$cid="'.$ENV{'request.course.id'}. |
|
'";$cfn="'.$ENV{'request.course.fn'}. |
|
'";$cnum="'.$ENV{'course.'.$ENV{'request.course.id'}.'.num'}. |
|
'";$cdom="'.$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}. |
|
'";$chome="'.$ENV{'course.'.$ENV{'request.course.id'}.'.home'}.'";'); |
|
return $safeeval; |
} |
} |
|
|
# ------------------------------------------------------------ Save spreadsheet |
# ------------------------------------------------------------ Save spreadsheet |
|
|
sub writesheet { |
sub writesheet { |
my $safeeval=shift; |
my ($safeeval,$makedef)=@_; |
if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) { |
my $cid=&getcid($safeeval); |
|
if (&Apache::lonnet::allowed('opa',$cid)) { |
my %f=&getformulas($safeeval); |
my %f=&getformulas($safeeval); |
|
my $stype=&gettype($safeeval); |
|
my $cnum=&getcnum($safeeval); |
|
my $cdom=&getcdom($safeeval); |
|
my $chome=&getchome($safeeval); |
|
my $fn=&getfilename($safeeval); |
|
|
|
# ------------------------------------------------------------- Cache new sheet |
|
$spreadsheets{$cnum.'_'.$cdom.'_'.$stype.'_'.$fn}=join('___;___',%f); |
|
# ----------------------------------------------------------------- Write sheet |
my $sheetdata=''; |
my $sheetdata=''; |
map { |
map { |
$sheetdata.=&Apache::lonnet::escape($_).'='. |
$sheetdata.=&Apache::lonnet::escape($_).'='. |
&Apache::lonnet::escape($f{$_}).'&'; |
&Apache::lonnet::escape($f{$_}).'&'; |
} keys %f; |
} keys %f; |
$sheetdata=~s/\&$//; |
$sheetdata=~s/\&$//; |
my $reply=&Apache::lonnet::reply('put:'. |
my $reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'.$fn.':'. |
$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'. |
$sheetdata,$chome); |
$ENV{'course.'.$ENV{'request.course.id'}.'.num'}.':'. |
|
&getfilename($safeeval).':'. |
|
$sheetdata, |
|
$ENV{'course.'.$ENV{'request.course.id'}.'.home'}); |
|
if ($reply eq 'ok') { |
if ($reply eq 'ok') { |
return &Apache::lonnet::reply('put:'. |
$reply=&Apache::lonnet::reply('put:'.$cdom.':'.$cnum.':'. |
$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'. |
$stype.'_spreadsheets:'. |
$ENV{'course.'.$ENV{'request.course.id'}.'.num'}.':'. |
&Apache::lonnet::escape($fn).'='.$ENV{'user.name'}, |
&gettype($safeeval).'_spreadsheets:'. |
$chome); |
&Apache::lonnet::escape(&getfilename($safeeval)).'='. |
if ($reply eq 'ok') { |
$ENV{'user.name'}, |
if ($makedef) { |
$ENV{'course.'.$ENV{'request.course.id'}.'.home'}); |
return &Apache::lonnet::reply('put:'.$cdom.':'.$cnum. |
|
':environment:spreadsheet_default_'.$stype.'='. |
|
&Apache::lonnet::escape($fn), |
|
$chome); |
|
} else { |
|
return $reply; |
|
} |
|
} else { |
|
return $reply; |
|
} |
} else { |
} else { |
return $reply; |
return $reply; |
} |
} |
Line 687 sub writesheet {
|
Line 844 sub writesheet {
|
} |
} |
|
|
# ----------------------------------------------- Make a temp copy of the sheet |
# ----------------------------------------------- Make a temp copy of the sheet |
|
# "Modified workcopy" - interactive only |
|
# |
|
|
sub tmpwrite { |
sub tmpwrite { |
my ($safeeval,$tmpdir,$symb)=@_; |
my $safeeval=shift; |
my $fn=$uname.'_'.$udom.'_spreadsheet_'.$symb.'_'.&getfilename($safeeval); |
my $fn=$ENV{'user.name'}.'_'. |
|
$ENV{'user.domain'}.'_spreadsheet_'.&getusymb($safeeval).'_'. |
|
&getfilename($safeeval); |
$fn=~s/\W/\_/g; |
$fn=~s/\W/\_/g; |
$fn=$tmpdir.$fn.'.tmp'; |
$fn=$tmpdir.$fn.'.tmp'; |
my $fh; |
my $fh; |
Line 702 sub tmpwrite {
|
Line 863 sub tmpwrite {
|
# ---------------------------------------------------------- Read the temp copy |
# ---------------------------------------------------------- Read the temp copy |
|
|
sub tmpread { |
sub tmpread { |
my ($safeeval,$tmpdir,$symb,$nfield,$nform)=@_; |
my ($safeeval,$nfield,$nform)=@_; |
my $fn=$uname.'_'.$udom.'_spreadsheet_'.$symb.'_'.&getfilename($safeeval); |
my $fn=$ENV{'user.name'}.'_'. |
|
$ENV{'user.domain'}.'_spreadsheet_'.&getusymb($safeeval).'_'. |
|
&getfilename($safeeval); |
$fn=~s/\W/\_/g; |
$fn=~s/\W/\_/g; |
$fn=$tmpdir.$fn.'.tmp'; |
$fn=$tmpdir.$fn.'.tmp'; |
my $fh; |
my $fh; |
Line 721 sub tmpread {
|
Line 884 sub tmpread {
|
&setformulas($safeeval,%fo); |
&setformulas($safeeval,%fo); |
} |
} |
|
|
# --------------------------------------------------------------- Read metadata |
|
|
|
sub readmeta { |
|
my $fn=shift; |
|
unless ($fn=~/\.meta$/) { $fn.='meta'; } |
|
my $content; |
|
my %returnhash=(); |
|
{ |
|
my $fh=Apache::File->new($fn); |
|
$content=join('',<$fh>); |
|
} |
|
my $parser=HTML::TokeParser->new(\$content); |
|
my $token; |
|
while ($token=$parser->get_token) { |
|
if ($token->[0] eq 'S') { |
|
my $entry=$token->[1]; |
|
if (($entry eq 'stores') || ($entry eq 'parameter')) { |
|
my $unikey=$entry; |
|
$unikey.='_'.$token->[2]->{'part'}; |
|
$unikey.='_'.$token->[2]->{'name'}; |
|
$returnhash{$unikey}=$token->[2]->{'display'}; |
|
} |
|
} |
|
} |
|
return %returnhash; |
|
} |
|
|
|
# ================================================================== Parameters |
# ================================================================== Parameters |
# -------------------------------------------- Figure out a cascading parameter |
# -------------------------------------------- Figure out a cascading parameter |
|
# |
|
# For this function to work |
|
# |
|
# * parmhash needs to be tied |
|
# * courseopt and useropt need to be initialized for this user and course |
|
# |
|
|
sub parmval { |
sub parmval { |
my ($what,$symb)=@_; |
my ($what,$safeeval)=@_; |
|
my $cid=&getcid($safeeval); |
|
my $csec=&getcsec($safeeval); |
|
my $uname=&getuname($safeeval); |
|
my $udom=&getudom($safeeval); |
|
my $symb=&getusymb($safeeval); |
|
|
unless ($symb) { return ''; } |
unless ($symb) { return ''; } |
my $result=''; |
my $result=''; |
Line 765 sub parmval {
|
Line 912 sub parmval {
|
|
|
my $symbparm=$symb.'.'.$what; |
my $symbparm=$symb.'.'.$what; |
my $mapparm=$mapname.'___(all).'.$what; |
my $mapparm=$mapname.'___(all).'.$what; |
|
my $usercourseprefix=$uname.'_'.$udom.'_'.$cid; |
|
|
my $seclevel= |
my $seclevel= |
$ENV{'request.course.id'}.'.['. |
$usercourseprefix.'.['. |
$csec.'].'.$what; |
$csec.'].'.$what; |
my $seclevelr= |
my $seclevelr= |
$ENV{'request.course.id'}.'.['. |
$usercourseprefix.'.['. |
$csec.'].'.$symbparm; |
$csec.'].'.$symbparm; |
my $seclevelm= |
my $seclevelm= |
$ENV{'request.course.id'}.'.['. |
$usercourseprefix.'.['. |
$csec.'].'.$mapparm; |
$csec.'].'.$mapparm; |
|
|
my $courselevel= |
my $courselevel= |
$ENV{'request.course.id'}.'.'.$what; |
$usercourseprefix.'.'.$what; |
my $courselevelr= |
my $courselevelr= |
$ENV{'request.course.id'}.'.'.$symbparm; |
$usercourseprefix.'.'.$symbparm; |
my $courselevelm= |
my $courselevelm= |
$ENV{'request.course.id'}.'.'.$mapparm; |
$usercourseprefix.'.'.$mapparm; |
|
|
# ---------------------------------------------------------- fourth, check user |
# ---------------------------------------------------------- fourth, check user |
|
|
Line 826 sub parmval {
|
Line 974 sub parmval {
|
|
|
# ---------------------------------------------- Update rows for course listing |
# ---------------------------------------------- Update rows for course listing |
|
|
sub updatestudentrows { |
sub updateclasssheet { |
my $safeeval=shift; |
my $safeeval=shift; |
my $cid=$ENV{'request.course.id'}; |
my $cnum=&getcnum($safeeval); |
|
my $cdom=&getcdom($safeeval); |
|
my $cid=&getcid($safeeval); |
|
my $chome=&getchome($safeeval); |
|
|
|
# ---------------------------------------------- Read class list and row labels |
|
|
my $classlst=&Apache::lonnet::reply |
my $classlst=&Apache::lonnet::reply |
('dump:'.$ENV{'course.'.$cid.'.domain'}.':'. |
('dump:'.$cdom.':'.$cnum.':classlist',$chome); |
$ENV{'course.'.$cid.'.num'}.':classlist', |
|
$ENV{'course.'.$cid.'.home'}); |
|
my %currentlist=(); |
my %currentlist=(); |
my $now=time; |
my $now=time; |
unless ($classlst=~/^error\:/) { |
unless ($classlst=~/^error\:/) { |
Line 844 sub updatestudentrows {
|
Line 996 sub updatestudentrows {
|
if ($active) { |
if ($active) { |
my $rowlabel=''; |
my $rowlabel=''; |
$name=&Apache::lonnet::unescape($name); |
$name=&Apache::lonnet::unescape($name); |
my ($cname,$cdom)=split(/\:/,$name); |
my ($sname,$sdom)=split(/\:/,$name); |
my $csec= |
my $ssec=&Apache::lonnet::usection($sdom,$sname,$cid); |
&Apache::lonnet::usection($cdom,$cname,$ENV{'request.course.id'}); |
if ($ssec==-1) { |
if ($csec==-1) { |
|
$rowlabel='<font color=red>Data not available: '.$name. |
$rowlabel='<font color=red>Data not available: '.$name. |
'</font>'; |
'</font>'; |
} else { |
} else { |
my %reply=&Apache::lonnet::idrget($cdom,$cname); |
my %reply=&Apache::lonnet::idrget($sdom,$sname); |
my $reply=&Apache::lonnet::reply('get:'.$cdom.':'.$cname. |
my $reply=&Apache::lonnet::reply('get:'.$sdom.':'.$sname. |
':environment:firstname&middlename&lastname&generation', |
':environment:firstname&middlename&lastname&generation', |
&Apache::lonnet::homeserver($cname,$cdom)); |
&Apache::lonnet::homeserver($sname,$sdom)); |
$rowlabel=$csec.' '.$reply{$cname}.'<br>'; |
$rowlabel=$ssec.' '.$reply{$sname}.'<br>'; |
map { |
map { |
$rowlabel.=&Apache::lonnet::unescape($_).' '; |
$rowlabel.=&Apache::lonnet::unescape($_).' '; |
} split(/\&/,$reply); |
} split(/\&/,$reply); |
} |
} |
|
|
$currentlist{&Apache::lonnet::unescape($name)}=$rowlabel; |
$currentlist{&Apache::lonnet::unescape($name)}=$rowlabel; |
} |
} |
} split(/\&/,$classlst); |
} split(/\&/,$classlst); |
Line 904 sub updatestudentrows {
|
Line 1054 sub updatestudentrows {
|
return 'Could not access course data'; |
return 'Could not access course data'; |
} |
} |
} |
} |
# ----------------------------------------------------------------- Update rows |
|
|
|
sub updaterows { |
# ----------------------------------- Update rows for student and assess sheets |
|
|
|
sub updatestudentassesssheet { |
my $safeeval=shift; |
my $safeeval=shift; |
my %bighash; |
my %bighash; |
# -------------------------------------------------------------------- Tie hash |
# -------------------------------------------------------------------- Tie hash |
Line 1004 sub updaterows {
|
Line 1155 sub updaterows {
|
|
|
# ------------------------------------------------ Load data for one assessment |
# ------------------------------------------------ Load data for one assessment |
|
|
sub rowazstudent { |
sub loadstudent { |
my $safeeval=shift; |
my $safeeval=shift; |
my %c=(); |
my %c=(); |
my %f=&getformulas($safeeval); |
my %f=&getformulas($safeeval); |
Line 1012 sub rowazstudent {
|
Line 1163 sub rowazstudent {
|
if ($_=~/^A(\d+)/) { |
if ($_=~/^A(\d+)/) { |
my $row=$1; |
my $row=$1; |
unless ($f{$_}=~/^\!/) { |
unless ($f{$_}=~/^\!/) { |
my @assessdata=split(/\_\_\_\;\_\_\_/, |
my @assessdata=&exportsheet(&getuname($safeeval), |
&Apache::lonnet::ssi( |
&getudom($safeeval), |
'/adm/assesscalc',('utarget' => 'export', |
'assesscalc',$f{$_}); |
'uname' => $uname, |
|
'udom' => $udom, |
|
'usymb' => $f{$_}))); |
|
my $index=0; |
my $index=0; |
map { |
map { |
if ($assessdata[$index]) { |
if ($assessdata[$index]) { |
Line 1038 sub rowazstudent {
|
Line 1186 sub rowazstudent {
|
|
|
# --------------------------------------------------- Load data for one student |
# --------------------------------------------------- Load data for one student |
|
|
sub rowazclass { |
sub loadcourse { |
my $safeeval=shift; |
my $safeeval=shift; |
my %c=(); |
my %c=(); |
my %f=&getformulas($safeeval); |
my %f=&getformulas($safeeval); |
map { |
map { |
if ($_=~/^A(\d+)/) { |
if ($_=~/^A(\d+)/) { |
my $row=$1; |
my $row=$1; |
unless ($f{$_}=~/^\!/) { |
unless (($f{$_}=~/^\!/) |
my ($tname,$tdom)=split(/\:/,$_); |
|
my @assessdata=split(/\_\_\_\;\_\_\_/, |
|| ($row>200)) |
&Apache::lonnet::ssi( |
|
'/adm/studentcalc',('utarget' => 'export', |
{ |
'uname' => $tname, |
my @studentdata=&exportsheet(&getuname($safeeval), |
'udom' => $tdom))); |
&getudom($safeeval), |
|
'studentcalc'); |
|
undef %userrdatas; |
my $index=0; |
my $index=0; |
map { |
map { |
if ($assessdata[$index]) { |
if ($studentdata[$index]) { |
$c{$_.$row}=$assessdata[$index]; |
$c{$_.$row}=$studentdata[$index]; |
unless ($_ eq 'A') { |
unless ($_ eq 'A') { |
$f{$_.$row}='import'; |
$f{$_.$row}='import'; |
} |
} |
Line 1072 sub rowazclass {
|
Line 1222 sub rowazclass {
|
|
|
# ------------------------------------------------ Load data for one assessment |
# ------------------------------------------------ Load data for one assessment |
|
|
sub rowaassess { |
sub loadassessment { |
my ($safeeval,$symb)=@_; |
my $safeeval=shift; |
my $uhome=&Apache::lonnet::homeserver($uname,$udom); |
|
|
my $uhome=&getuhome($safeeval); |
|
my $uname=&getuname($safeeval); |
|
my $udom=&getudom($safeeval); |
|
my $symb=&getusymb($safeeval); |
|
my $cid=&getcid($safeeval); |
|
my $cnum=&getcnum($safeeval); |
|
my $cdom=&getcdom($safeeval); |
|
my $chome=&getchome($safeeval); |
|
|
my $namespace; |
my $namespace; |
unless ($namespace=$ENV{'request.course.id'}) { return ''; } |
unless ($namespace=$cid) { return ''; } |
|
|
# ----------------------------------------------------------- Get stored values |
# ----------------------------------------------------------- Get stored values |
my $answer=&Apache::lonnet::reply( |
my $answer=&Apache::lonnet::reply( |
Line 1098 sub rowaassess {
|
Line 1257 sub rowaassess {
|
# ----------------------------- returnhash now has all stores for this resource |
# ----------------------------- returnhash now has all stores for this resource |
|
|
# ---------------------------- initialize coursedata and userdata for this user |
# ---------------------------- initialize coursedata and userdata for this user |
%courseopt=(); |
undef %courseopt; |
%useropt=(); |
undef %useropt; |
my $uhome=&Apache::lonnet::homeserver($uname,$udom); |
|
|
my $userprefix=$uname.'_'.$udom.'_'; |
|
|
unless ($uhome eq 'no_host') { |
unless ($uhome eq 'no_host') { |
# -------------------------------------------------------------- Get coursedata |
# -------------------------------------------------------------- Get coursedata |
unless |
unless |
((time-$courserdatas{$ENV{'request.course.id'}.'.last_cache'})<120) { |
((time-$courserdatas{$cid.'.last_cache'})<240) { |
my $reply=&Apache::lonnet::reply('dump:'. |
my $reply=&Apache::lonnet::reply('dump:'.$cdom.':'.$cnum. |
$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'. |
':resourcedata',$chome); |
$ENV{'course.'.$ENV{'request.course.id'}.'.num'}.':resourcedata', |
|
$ENV{'course.'.$ENV{'request.course.id'}.'.home'}); |
|
if ($reply!~/^error\:/) { |
if ($reply!~/^error\:/) { |
$courserdatas{$ENV{'request.course.id'}}=$reply; |
$courserdatas{$cid}=$reply; |
$courserdatas{$ENV{'request.course.id'}.'.last_cache'}=time; |
$courserdatas{$cid.'.last_cache'}=time; |
} |
} |
} |
} |
map { |
map { |
my ($name,$value)=split(/\=/,$_); |
my ($name,$value)=split(/\=/,$_); |
$courseopt{&Apache::lonnet::unescape($name)}= |
$courseopt{$userprefix.&Apache::lonnet::unescape($name)}= |
&Apache::lonnet::unescape($value); |
&Apache::lonnet::unescape($value); |
} split(/\&/,$courserdatas{$ENV{'request.course.id'}}); |
} split(/\&/,$courserdatas{$ENV{'request.course.id'}}); |
# --------------------------------------------------- Get userdata (if present) |
# --------------------------------------------------- Get userdata (if present) |
unless |
unless |
((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<120) { |
((time-$userrdatas{$uname.'___'.$udom.'.last_cache'})<240) { |
my $reply= |
my $reply= |
&Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome); |
&Apache::lonnet::reply('dump:'.$udom.':'.$uname.':resourcedata',$uhome); |
if ($reply!~/^error\:/) { |
if ($reply!~/^error\:/) { |
Line 1131 sub rowaassess {
|
Line 1290 sub rowaassess {
|
} |
} |
map { |
map { |
my ($name,$value)=split(/\=/,$_); |
my ($name,$value)=split(/\=/,$_); |
$useropt{&Apache::lonnet::unescape($name)}= |
$useropt{$userprefix.&Apache::lonnet::unescape($name)}= |
&Apache::lonnet::unescape($value); |
&Apache::lonnet::unescape($value); |
} split(/\&/,$userrdatas{$uname.'___'.$udom}); |
} split(/\&/,$userrdatas{$uname.'___'.$udom}); |
} |
} |
# -- now courseopt, useropt initialized for this user and course (used parmval) |
|
|
|
my %c=(); |
# ----------------- now courseopt, useropt initialized for this user and course |
|
# (used by parmval) |
|
|
|
my %c=(); |
|
|
|
if (tie(%parmhash,'GDBM_File', |
|
&getcfn($safeeval).'_parms.db',&GDBM_READER,0640)) { |
my %f=&getformulas($safeeval); |
my %f=&getformulas($safeeval); |
map { |
map { |
if ($_=~/^A/) { |
if ($_=~/^A/) { |
unless ($f{$_}=~/^\!/) { |
unless ($f{$_}=~/^\!/) { |
if ($f{$_}=~/^parameter/) { |
if ($f{$_}=~/^parameter/) { |
$c{$_}=&parmval($f{$_},$symb); |
$c{$_}=&parmval($f{$_},$safeeval); |
} else { |
} else { |
my $key=$f{$_}; |
my $key=$f{$_}; |
$key=~s/^stores\_/resource\./; |
$key=~s/^stores\_/resource\./; |
Line 1153 sub rowaassess {
|
Line 1317 sub rowaassess {
|
} |
} |
} |
} |
} keys %f; |
} keys %f; |
|
untie(%parmhash); |
&setconstants($safeeval,%c); |
} |
|
&setconstants($safeeval,%c); |
} |
} |
|
|
# --------------------------------------------------------- Various form fields |
# --------------------------------------------------------- Various form fields |
Line 1181 sub selectbox {
|
Line 1346 sub selectbox {
|
return $selout.'</select>'; |
return $selout.'</select>'; |
} |
} |
|
|
|
# =============================================== Update information in a sheet |
|
# |
|
# Add new users or assessments, etc. |
|
# |
|
|
|
sub updatesheet { |
|
my $safeeval=shift; |
|
my $stype=&gettype($safeeval); |
|
if ($stype eq 'classcalc') { |
|
return &updateclasssheet($safeeval); |
|
} else { |
|
return &updatestudentassesssheet($safeeval); |
|
} |
|
} |
|
|
|
# =================================================== Load the rows for a sheet |
|
# |
|
# Import the data for rows |
|
# |
|
|
|
sub loadrows() { |
|
my $safeeval=shift; |
|
my $stype=&gettype($safeeval); |
|
if ($stype eq 'classcalc') { |
|
&loadcourse($safeeval); |
|
} elsif ($stype eq 'studentcalc') { |
|
&loadstudent($safeeval); |
|
} else { |
|
&loadassessment($safeeval); |
|
} |
|
} |
|
|
|
# ============================================================== Export handler |
|
# |
|
# Non-interactive call from with program |
|
# |
|
|
|
sub exportsheet { |
|
my ($uname,$udom,$stype,$usymb,$fn)=@_; |
|
my $thissheet=&makenewsheet($uname,$udom,$stype,$usymb); |
|
&readsheet($thissheet,$fn); |
|
&updatesheet($thissheet); |
|
&loadrows($thissheet); |
|
&calcsheet($thissheet); |
|
my @returnthis=&exportdata($thissheet); |
|
undef $thissheet; |
|
return @returnthis; |
|
} |
|
|
# ================================================================ Main handler |
# ================================================================ Main handler |
|
# |
|
# Interactive call to screen |
|
# |
|
# |
|
|
|
|
sub handler { |
sub handler { |
my $r=shift; |
my $r=shift; |
|
|
$uname=''; |
if ($r->header_only) { |
$udom=''; |
|
$csec=''; |
|
|
|
if ($r->header_only) { |
|
$r->content_type('text/html'); |
$r->content_type('text/html'); |
$r->send_http_header; |
$r->send_http_header; |
return OK; |
return OK; |
} |
} |
|
|
|
# ---------------------------------------------------- Global directory configs |
|
|
|
$includedir=$r->dir_config('lonIncludes'); |
|
$tmpdir=$r->dir_config('lonDaemons').'/tmp/'; |
|
|
# ----------------------------------------------------- Needs to be in a course |
# ----------------------------------------------------- Needs to be in a course |
|
|
if (($ENV{'request.course.fn'}) || |
if ($ENV{'request.course.fn'}) { |
($ENV{'request.state'} eq 'construct')) { |
|
|
|
# --------------------------- Get query string for limited number of parameters |
# --------------------------- Get query string for limited number of parameters |
|
|
Line 1216 sub handler {
|
Line 1435 sub handler {
|
} (split(/&/,$ENV{'QUERY_STRING'})); |
} (split(/&/,$ENV{'QUERY_STRING'})); |
|
|
# ------------------------------------------- Nothing there? Must be login user |
# ------------------------------------------- Nothing there? Must be login user |
|
|
|
my $aname; |
|
my $adom; |
|
|
unless ($ENV{'form.uname'}) { |
unless ($ENV{'form.uname'}) { |
$uname=$ENV{'user.name'}; |
$aname=$ENV{'user.name'}; |
$udom=$ENV{'user.domain'}; |
$adom=$ENV{'user.domain'}; |
} else { |
} else { |
$uname=$ENV{'form.uname'}; |
$aname=$ENV{'form.uname'}; |
$udom=$ENV{'form.udom'}; |
$adom=$ENV{'form.udom'}; |
} |
} |
# ----------------------------------------------------------- Change of target? |
|
|
|
my $reroute=($ENV{'form.utarget'} eq 'export'); |
|
|
|
# ------------------------------------------------------------------- Open page |
# ------------------------------------------------------------------- Open page |
|
|
Line 1236 sub handler {
|
Line 1456 sub handler {
|
|
|
# --------------------------------------------------------------- Screen output |
# --------------------------------------------------------------- Screen output |
|
|
unless ($reroute) { |
|
$r->print('<html><head><title>LON-CAPA Spreadsheet</title>'); |
$r->print('<html><head><title>LON-CAPA Spreadsheet</title>'); |
$r->print(<<ENDSCRIPT); |
$r->print(<<ENDSCRIPT); |
<script language="JavaScript"> |
<script language="JavaScript"> |
Line 1261 ENDSCRIPT
|
Line 1480 ENDSCRIPT
|
&hiddenfield('usymb',$ENV{'form.usymb'}). |
&hiddenfield('usymb',$ENV{'form.usymb'}). |
&hiddenfield('unewfield',''). |
&hiddenfield('unewfield',''). |
&hiddenfield('unewformula','')); |
&hiddenfield('unewformula','')); |
} |
|
|
# ---------------------- Make sure that this gets out, even if user hits "stop" |
|
|
$r->rflush(); |
$r->rflush(); |
|
|
# ---------------------------------------- Read new sheet or modified worksheet |
# ---------------------------------------- Read new sheet or modified worksheet |
|
|
my $sheetone=initsheet(); |
|
$r->uri=~/\/(\w+)$/; |
$r->uri=~/\/(\w+)$/; |
&settype($sheetone,$1); |
|
|
my $asheet=&makenewsheet($aname,$adom,$1,$ENV{'form.usymb'}); |
|
|
|
# ------------------------ If a new formula had been entered, go from work copy |
|
|
if ($ENV{'form.unewfield'}) { |
if ($ENV{'form.unewfield'}) { |
$r->print('<h2>Modified Workcopy</h2>'); |
$r->print('<h2>Modified Workcopy</h2>'); |
$ENV{'form.unewformula'}=~s/\'/\"/g; |
$ENV{'form.unewformula'}=~s/\'/\"/g; |
$r->print('<p>New formula: '.$ENV{'form.unewfield'}.'='. |
$r->print('<p>New formula: '.$ENV{'form.unewfield'}.'='. |
$ENV{'form.unewformula'}.'<p>'); |
$ENV{'form.unewformula'}.'<p>'); |
&setfilename($sheetone,$ENV{'form.ufn'}); |
&setfilename($asheet,$ENV{'form.ufn'}); |
&tmpread($sheetone,$r->dir_config('lonDaemons').'/tmp/', |
&tmpread($asheet, |
$ENV{'form.usymb'}, |
|
$ENV{'form.unewfield'},$ENV{'form.unewformula'}); |
$ENV{'form.unewfield'},$ENV{'form.unewformula'}); |
} elsif ($ENV{'form.saveas'}) { |
|
&setfilename($sheetone,$ENV{'form.ufn'}); |
} elsif ($ENV{'form.saveas'}) { |
&tmpread($sheetone,$r->dir_config('lonDaemons').'/tmp/', |
&setfilename($asheet,$ENV{'form.ufn'}); |
$ENV{'form.usymb'}); |
&tmpread($asheet); |
} else { |
} else { |
unless ($ENV{'form.ufn'}) { |
&readsheet($asheet,$ENV{'form.ufn'}); |
my $reply=&Apache::lonnet::reply('get:'. |
} |
$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'. |
|
$ENV{'course.'.$ENV{'request.course.id'}.'.num'}. |
# -------------------------------------------------- Print out user information |
':environment:spreadsheet_default_'. |
|
&gettype($sheetone), |
unless (&gettype($asheet) eq 'classcalc') { |
$ENV{'course.'.$ENV{'request.course.id'}.'.home'}); |
$r->print('<p><b>User:</b> '.&getuname($asheet). |
unless (($reply=~/^error\:/) || ($reply eq '')) { |
'<br><b>Domain:</b> '.&getudom($asheet)); |
$ENV{'form.ufn'}=$reply; |
if (&getcsec($asheet) eq '-1') { |
unless ($reroute) { |
$r->print('<h3><font color=red>'. |
$r->print('<p>Using customized default spreadsheet<p>'); |
'Not a student in this course</font></h3>'); |
} |
} else { |
} |
$r->print('<br><b>Section/Group:</b> '.&getcsec($asheet)); |
} |
} |
&readsheet($sheetone,$ENV{'form.ufn'},$r); |
|
$ENV{'form.ufn'}=&getfilename($sheetone); |
|
} |
} |
|
|
if (&gettype($sheetone) eq 'classcalc') { |
# ---------------------------------------------------------------- Course title |
# ---------------------------------- For course view: get courselist and update |
|
&updatestudentrows($sheetone); |
$r->print('<h1>'. |
} else { |
$ENV{'course.'.$ENV{'request.course.id'}.'.description'}.'</h1>'); |
# ----------------- For assessment and student: See if all import rows uptodate |
|
|
|
if (tie(%parmhash,'GDBM_File', |
|
$ENV{'request.course.fn'}.'_parms.db',&GDBM_READER,0640)) { |
|
$csec=&Apache::lonnet::usection($udom,$uname,$ENV{'request.course.id'}); |
|
if ($csec eq '-1') { |
|
$r->print('<h3><font color=red>'. |
|
"User '$uname' at domain '$udom' not a student in this course</font></h3>"); |
|
} |
|
&updaterows($sheetone); |
|
untie(%parmhash); |
|
} else { |
|
$r->print('<h3><font color=red>'. |
|
'Could not initialize import fields (not in a course)</font></h3>'); |
|
} |
|
} |
|
# ---------------------------------------------------- See if something to save |
# ---------------------------------------------------- See if something to save |
|
|
if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) { |
if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) { |
my $fname=''; |
my $fname=''; |
if ($ENV{'form.saveas'} && ($fname=$ENV{'form.newfn'})) { |
if ($ENV{'form.saveas'} && ($fname=$ENV{'form.newfn'})) { |
$fname=~s/\W/\_/g; |
$fname=~s/\W/\_/g; |
if ($fname eq 'default') { $fname='course_default'; } |
if ($fname eq 'default') { $fname='course_default'; } |
$fname.='_'.&gettype($sheetone); |
$fname.='_'.&gettype($asheet); |
&setfilename($sheetone,$fname); |
&setfilename($asheet,$fname); |
$ENV{'form.ufn'}=$fname; |
$ENV{'form.ufn'}=$fname; |
my $reply=&writesheet($sheetone); |
$r->print('<p>Saving spreadsheet: '. |
unless ($reroute) { |
&writesheet($asheet,$ENV{'form.makedefufn'}).'<p>'); |
$r->print('<p>Saving spreadsheet: '.$reply.'<p>'); |
} |
} |
|
if ($ENV{'form.makedefufn'}) { |
|
my $reply=&Apache::lonnet::reply('put:'. |
|
$ENV{'course.'.$ENV{'request.course.id'}.'.domain'}.':'. |
|
$ENV{'course.'.$ENV{'request.course.id'}.'.num'}. |
|
':environment:spreadsheet_default_'. |
|
&gettype($sheetone).'='. |
|
&Apache::lonnet::escape($fname), |
|
$ENV{'course.'.$ENV{'request.course.id'}.'.home'}); |
|
unless ($reroute) { |
|
$r->print('<p>Making default spreadsheet: '.$reply.'<p>'); |
|
} |
|
} |
|
} |
|
} |
} |
|
|
# ------------------------------------------------ Write the modified worksheet |
# ------------------------------------------------ Write the modified worksheet |
|
|
&tmpwrite($sheetone,$r->dir_config('lonDaemons').'/tmp/', |
$r->print('<b>Current sheet:</b> '.&getfilename($asheet).'<p>'); |
$ENV{'form.usymb'}); |
|
|
&tmpwrite($asheet); |
|
|
|
# ----------------------------------------------------------------- Save dialog |
|
|
|
|
# ----------------------------------------------------- Print user, course, etc |
|
unless ($reroute) { |
|
if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) { |
if (&Apache::lonnet::allowed('opa',$ENV{'request.course.id'})) { |
my $fname=$ENV{'form.ufn'}; |
my $fname=$ENV{'form.ufn'}; |
$fname=~s/\_[^\_]+$//; |
$fname=~s/\_[^\_]+$//; |
Line 1362 ENDSCRIPT
|
Line 1560 ENDSCRIPT
|
'<input type=text size=20 name=newfn value="'.$fname. |
'<input type=text size=20 name=newfn value="'.$fname. |
'"> (make default: <input type=checkbox name="makedefufn">)<p>'); |
'"> (make default: <input type=checkbox name="makedefufn">)<p>'); |
} |
} |
$r->print(&hiddenfield('ufn',$ENV{'form.ufn'})); |
|
unless (&gettype($sheetone) eq 'classcalc') { |
|
$r->print('<br><b>User:</b> '.$uname.'<br><b>Domain:</b> '.$udom); |
|
} |
|
$r->print('<h1>'. |
|
$ENV{'course.'.$ENV{'request.course.id'}.'.description'}.'</h1>'); |
|
if ($csec) { |
|
$r->print('<h3>Group/Section: '.$csec.'</h3>'); |
|
} |
|
} |
|
# -------------------------------------------------------- Import and calculate |
|
|
|
if (&gettype($sheetone) eq 'assesscalc') { |
$r->print(&hiddenfield('ufn',&getfilename($asheet))); |
&rowaassess($sheetone,$ENV{'form.usymb'}); |
|
} elsif (&gettype($sheetone) eq 'studentcalc') { |
|
&rowazstudent($sheetone); |
# ----------------------------------------------------- Update sheet, load rows |
} else { |
|
&rowazclass($sheetone); |
&updatesheet($asheet); |
} |
&loadrows($asheet); |
my $calcoutput=&calcsheet($sheetone); |
|
unless ($reroute) { |
|
$r->print('<h3><font color=red>'.$calcoutput.'</h3></font>'); |
|
} |
|
|
|
# ------------------------------------------------------- Print or export sheet |
my $calcoutput=&calcsheet($asheet); |
unless ($reroute) { |
$r->print('<h3><font color=red>'.$calcoutput.'</h3></font>'); |
&outsheet($r,$sheetone); |
|
|
|
|
&outsheet($r,$asheet); |
$r->print('</form></body></html>'); |
$r->print('</form></body></html>'); |
} else { |
|
$r->print(&exportrow($sheetone)); |
# --------------------------------- We know this leaks, so terminate this child |
} |
|
|
$r->child_terminate(); |
|
|
# ------------------------------------------------------------------------ Done |
# ------------------------------------------------------------------------ Done |
} else { |
} else { |
# ----------------------------- Not in a course, or not allowed to modify parms |
# ----------------------------- Not in a course, or not allowed to modify parms |
Line 1402 ENDSCRIPT
|
Line 1588 ENDSCRIPT
|
return HTTP_NOT_ACCEPTABLE; |
return HTTP_NOT_ACCEPTABLE; |
} |
} |
return OK; |
return OK; |
|
|
} |
} |
|
|
1; |
1; |