# # $Id: studentcalc.pm,v 1.4 2003/05/22 21:16:35 matthew Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ # # The LearningOnline Network with CAPA # Spreadsheet/Grades Display Handler # # POD required stuff: =head1 NAME studentcalc =head1 SYNOPSIS =head1 DESCRIPTION =over 4 =cut ################################################### ### StudentSheet ### ################################################### package Apache::studentcalc; use strict; use Apache::Constants qw(:common :http); use Apache::lonnet; use Apache::loncommon(); use Apache::loncoursedata(); use Apache::lonnavmaps; use Apache::Spreadsheet(); use Apache::assesscalc(); use HTML::Entities(); use Spreadsheet::WriteExcel; use Time::HiRes; @Apache::studentcalc::ISA = ('Apache::Spreadsheet'); my @Sequences = (); my %Exportrows = (); my $current_course; sub initialize_package { $current_course = $ENV{'request.course.id'}; &initialize_sequence_cache(); &load_cached_export_rows(); } sub initialize_sequence_cache { # # Set up the sequences and assessments @Sequences = (); my ($top,$sequences,$assessments) = &Apache::loncoursedata::get_sequence_assessment_data(); if (! defined($top) || ! ref($top)) { # There has been an error, better report it &Apache::lonnet::logthis('top is undefined (studentcalc.pm)'); return; } @Sequences = @{$sequences} if (ref($sequences) eq 'ARRAY'); } sub clear_package { @Sequences = undef; %Exportrows = undef; } sub get_title { my $self = shift; my $title = ''; my %userenv = &Apache::loncoursedata::GetUserName($self->{'name'}, $self->{'domain'}); &Apache::lonnet::logthis('userenv = '.join(' ',%userenv)); my $name = join(' ',@userenv{'firstname','middlename','lastname','generation'}); $name =~ s/\s+$//; $title .= '

'.$name; if ($ENV{'user.name'} ne $self->{'name'} && $ENV{'user.domain'} ne $self->{'domain'}) { $title .= &Apache::loncommon::aboutmewrapper ($self->{'name'}.'@'.$self->{'domain'}, $self->{'name'},$self->{'domain'}); } $title .= "

\n"; $title .= '

'.$self->{'coursedesc'}."

\n"; $title .= '

'.localtime(time).'

'; return $title; } sub parent_link { my $self = shift; my $link .= '

'. 'Course level sheet

'."\n"; return $link; } sub outsheet_html { my $self = shift; my ($r) = @_; #################################### # Get the list of assessment files # #################################### my @AssessFileNames = $self->othersheets('assesscalc'); my $editing_is_allowed = &Apache::lonnet::allowed('mgr', $ENV{'request.course.id'}); #################################### # Determine table structure # #################################### my $num_uneditable = 26; my $num_left = 52-$num_uneditable; my $tableheader =<<"END";

END my $label_num = 0; foreach (split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')){ if ($label_num<$num_uneditable) { $tableheader .='"; $label_num++; } $tableheader .="\n"; if ($self->blackout()) { $r->print('

'. 'Some computations are not available at this time.
'. 'There are problems whose status you are allowed to view.'. '

'."\n"); } else { $r->print($tableheader); # # Print out template row if (exists($ENV{'request.role.adv'}) && $ENV{'request.role.adv'}) { $r->print(''. $self->html_template_row($num_uneditable)."\n"); } # # Print out summary/export row $r->print(''. $self->html_export_row()."\n"); } $r->print("
Student Import Calculations
'; } else { $tableheader .=''; } $tableheader .="$_
Template 
Summary0
\n"); # # Prepare to output rows if (exists($ENV{'request.role.adv'}) && $ENV{'request.role.adv'}) { $tableheader =<<"END";

END } else { $tableheader =<<"END";

RowAssessment
END } foreach (split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')){ if ($label_num<$num_uneditable) { $tableheader.='"; } $tableheader.="\n"; # my $num_output = 1; if (scalar(@Sequences)< 1) { &initialize_sequence_cache(); } foreach my $Sequence (@Sequences) { next if ($Sequence->{'num_assess'} < 1); $r->print("

".$Sequence->{'title'}."

\n"); $r->print($tableheader); foreach my $resource (@{$Sequence->{'contents'}}) { next if ($resource->{'type'} ne 'assessment'); my $rownum = $self->get_row_number_from_key($resource->{'symb'}); my $assess_filename = $self->{'row_source'}->{$rownum}; my $row_output = ''; if ($editing_is_allowed) { $row_output .= ''; $row_output .= ''; } else { $row_output .= ''; } if ($self->blackout() && $self->{'blackout_rows'}->{$rownum}>0) { $row_output .= ''."\n"; } else { $row_output .= $self->html_row($num_uneditable,$rownum). "\n"; } $r->print($row_output); } $r->print("
 Assessment'; } else { $tableheader.=''; } $tableheader.="$_
'.$rownum.''. ''.$resource->{'title'}.'
'; $row_output .= &assess_file_selector($rownum, $assess_filename, \@AssessFileNames). '
Go To'; $row_output .= ''.$resource->{'title'}.'Unavailable at this time
\n"); } $r->print("

\n"); return; } ######################################################## ######################################################## =pod =item &assess_file_selector() =cut ######################################################## ######################################################## sub assess_file_selector { my ($row,$default,$AssessFiles)=@_; if (!defined($AssessFiles) || ! @$AssessFiles) { return ''; } return '' if (! &Apache::lonnet::allowed('mgr',$ENV{'request.course.id'})); my $element_name = 'FileSelect_'.$row; my $load_dialog = '\n"; return $load_dialog; } sub modify_cell { my $self = shift; my ($cell,$formula) = @_; if ($cell =~ /^source_(\d+)$/) { # Need to make sure $formula is a valid filename.... my $row = $1; $cell = 'A'.$row; $self->{'row_source'}->{$row} = $formula; my $original_source = $self->formula($cell); if ($original_source =~ /__&&&__/) { ($original_source,undef) = split('__&&&__',$original_source); } $formula = $original_source.'__&&&__'.$formula; } elsif ($cell =~ /([A-z])\-/) { $cell = 'template_'.$1; } elsif ($cell !~ /^([A-z](\d+)|template_[A-z])$/) { return; } $self->set_formula($cell,$formula); $self->rebuild_stats(); return; } sub outsheet_csv { my $self = shift; my ($r) = @_; } sub outsheet_excel { my $self = shift; my ($r) = @_; } sub outsheet_recursive_excel { my $self = shift; my ($r) = @_; } sub display { my $self = shift; my ($r) = @_; $self->compute(); $self->outsheet_html($r); return; } sub set_row_sources { my $self = shift; while (my ($cell,$value) = each(%{$self->{'formulas'}})) { next if ($cell !~ /^A(\d+)/ && $1 > 0); my $row = $1; (undef,$value) = split('__&&&__',$value); $value = 'Default' if (! defined($value)); $self->{'row_source'}->{$row} = $value; } return; } sub compute { my $self = shift; $self->logthis('computing'); if (! defined($current_course) || $current_course ne $ENV{'request.course.id'}) { $current_course = $ENV{'request.course.id'}; &clear_package(); &initialize_sequence_cache(); } $self->initialize_safe_space(); my @sequences = @Sequences; if (@sequences < 1) { my ($top,$sequences,$assessments) = &Apache::loncoursedata::get_sequence_assessment_data(); if (! defined($top) || ! ref($top)) { &Apache::lonnet::logthis('top is undefined'); return; } @sequences = @{$sequences} if (ref($sequences) eq 'ARRAY'); } &Apache::assesscalc::initialize_package($self->{'name'},$self->{'domain'}); my %f = $self->formulas(); # # Process the formulas list - # the formula for the A column of a row is symb__&&__filename my %c = $self->constants(); foreach my $seq (@sequences) { next if ($seq->{'num_assess'}<1); foreach my $resource (@{$seq->{'contents'}}) { next if ($resource->{'type'} ne 'assessment'); my $rownum = $self->get_row_number_from_key($resource->{'symb'}); my $cell = 'A'.$rownum; my $assess_filename = 'Default'; if (exists($self->{'row_source'}->{$rownum})) { $assess_filename = $self->{'row_source'}->{$rownum}; } else { $self->{'row_source'}->{$rownum} = $assess_filename; } $f{$cell} = $resource->{'symb'}.'__&&&__'.$assess_filename; my $assessSheet = Apache::assesscalc->new($self->{'name'}, $self->{'domain'}, $assess_filename, $resource->{'symb'}); my @exportdata = $assessSheet->export_data(); if ($assessSheet->blackout()) { $self->blackout(1); $self->{'blackout_rows'}->{$rownum} = 1; } # # Be sure not to disturb the formulas in the 'A' column my $data = shift(@exportdata); $c{$cell} = $data if (defined($data)); # # Deal with the remaining columns my $i=0; foreach (split(//,'BCDEFGHIJKLMNOPQRSTUVWXYZ')) { my $cell = $_.$rownum; my $data = shift(@exportdata); if (defined($data)) { $f{$cell} = 'import'; $c{$cell} = $data; } $i++; } } } $self->constants(\%c); $self->formulas(\%f); $self->calcsheet(); # # Store export row in cache my @exportarray=$self->exportrow(); my $student = $self->{'name'}.':'.$self->{'domain'}; $Exportrows{$student}->{'time'} = time; $Exportrows{$student}->{'data'} = \@exportarray; # save export row $self->save_export_data(); return; } sub set_row_numbers { my $self = shift; while (my ($cell,$formula) = each(%{$self->{'formulas'}})) { next if ($cell !~ /^A(\d+)/); my $row = $1; next if ($row == 0); my ($symb,undef) = split('__&&&__',$formula); $self->{'row_numbers'}->{$symb} = $row; } } sub get_row_number_from_symb { my $self = shift; my ($key) = @_; ($key,undef) = split('__&&&__',$key) if ($key =~ /__&&&__/); return $self->get_row_number_from_key($key); } ############################################# ############################################# =pod =item &load_cached_export_rows Retrieves and parsers the export rows of the student spreadsheets. These rows are saved in the courses directory in the format: sname:sdom:studentcalc:.time => time sname:sdom:studentcalc => ___=___Adata___;___Bdata___;___Cdata___;___ ..... =cut ############################################# ############################################# sub load_cached_export_rows { %Exportrows = undef; my @tmp = &Apache::lonnet::dump('nohist_calculatedsheets', $ENV{'course.'.$ENV{'request.course.id'}.'.domain'}, $ENV{'course.'.$ENV{'request.course.id'}.'.num'},undef); my %Selected_Assess_Sheet; if ($tmp[0] =~ /^error/) { &Apache::lonnet::logthis('unable to read cached student export rows '. 'for course '.$ENV{'request.course.id'}); return; } my %tmp = @tmp; while (my ($key,$sheetdata) = each(%tmp)) { my ($sname,$sdom,$sheettype,$remainder) = split(':',$key); my $student = $sname.':'.$sdom; if ($remainder =~ /\.time/) { $Exportrows{$student}->{'time'} = $sheetdata; } else { $sheetdata =~ s/^___=___//; my @Data = split('___;___',$sheetdata); $Exportrows{$student}->{'data'} = \@Data; } } } ############################################# ############################################# =pod =item &save_export_data() Writes the export data for this student to the course cache. =cut ############################################# ############################################# sub save_export_data { my $self = shift; my $student = $self->{'name'}.':'.$self->{'domain'}; return if (! exists($Exportrows{$student})); return if (! $self->is_default()); my $key = join(':',($self->{'name'},$self->{'domain'},'studentcalc')).':'; my $timekey = $key.'.time'; my $newstore = join('___;___', @{$Exportrows{$student}->{'data'}}); $newstore = '___=___'.$newstore; &Apache::lonnet::put('nohist_calculatedsheets', { $key => $newstore, $timekey => $Exportrows{$student}->{'time'} }, $self->{'cdom'}, $self->{'cnum'}); return; } ############################################# ############################################# =pod =item &export_data() Returns the export data associated with the spreadsheet. Computes the spreadsheet only if necessary. =cut ############################################# ############################################# sub export_data { my $self = shift; my $student = $self->{'name'}.':'.$self->{'domain'}; if (! exists($Exportrows{$student}) || ! $self->check_expiration_time($Exportrows{$student}->{'time'})) { $self->compute(); } my @Data = @{$Exportrows{$student}->{'data'}}; for (my $i=0; $i<=$#Data;$i++) { $Data[$i]="'".$Data[$i]."'" if ($Data[$i]=~/\D/ && defined($Data[$i])); } return @Data; } 1; __END__