Annotation of loncom/interface/spreadsheet/studentcalc.pm, revision 1.1
1.1 ! matthew 1: #
! 2: # $Id$
! 3: #
! 4: # Copyright Michigan State University Board of Trustees
! 5: #
! 6: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
! 7: #
! 8: # LON-CAPA is free software; you can redistribute it and/or modify
! 9: # it under the terms of the GNU General Public License as published by
! 10: # the Free Software Foundation; either version 2 of the License, or
! 11: # (at your option) any later version.
! 12: #
! 13: # LON-CAPA is distributed in the hope that it will be useful,
! 14: # but WITHOUT ANY WARRANTY; without even the implied warranty of
! 15: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
! 16: # GNU General Public License for more details.
! 17: #
! 18: # You should have received a copy of the GNU General Public License
! 19: # along with LON-CAPA; if not, write to the Free Software
! 20: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
! 21: #
! 22: # /home/httpd/html/adm/gpl.txt
! 23: #
! 24: # http://www.lon-capa.org/
! 25: #
! 26: # The LearningOnline Network with CAPA
! 27: # Spreadsheet/Grades Display Handler
! 28: #
! 29: # POD required stuff:
! 30:
! 31: =head1 NAME
! 32:
! 33: studentcalc
! 34:
! 35: =head1 SYNOPSIS
! 36:
! 37: =head1 DESCRIPTION
! 38:
! 39: =over 4
! 40:
! 41: =cut
! 42:
! 43: ###################################################
! 44: ### StudentSheet ###
! 45: ###################################################
! 46: package Apache::studentcalc;
! 47:
! 48: use strict;
! 49: use Apache::Constants qw(:common :http);
! 50: use Apache::lonnet;
! 51: use Apache::lonnavmaps;
! 52: use Apache::Spreadsheet;
! 53: use Apache::assesscalc;
! 54: use HTML::Entities();
! 55: use Spreadsheet::WriteExcel;
! 56: use Time::HiRes;
! 57:
! 58: @Apache::studentcalc::ISA = ('Apache::Spreadsheet');
! 59:
! 60: my @Sequences = ();
! 61: my %Exportrows = ();
! 62:
! 63: my $current_course;
! 64:
! 65: sub initialize_package {
! 66: $current_course = $ENV{'request.course.id'};
! 67: &initialize_sequence_cache();
! 68: &load_cached_export_rows();
! 69: }
! 70:
! 71: sub initialize_sequence_cache {
! 72: #
! 73: # Set up the sequences and assessments
! 74: @Sequences = ();
! 75: my ($top,$sequences,$assessments) =
! 76: &Apache::loncoursedata::get_sequence_assessment_data();
! 77: if (! defined($top) || ! ref($top)) {
! 78: # There has been an error, better report it
! 79: &Apache::lonnet::logthis('top is undefined');
! 80: return;
! 81: }
! 82: @Sequences = @{$sequences} if (ref($sequences) eq 'ARRAY');
! 83: }
! 84:
! 85: sub clear_package {
! 86: @Sequences = undef;
! 87: %Exportrows = undef;
! 88: }
! 89:
! 90: sub get_title {
! 91: my $self = shift;
! 92: my $title = '';
! 93: $title .= '<h2>Spreadsheet for '.
! 94: $self->{'name'}.'@'.$self->{'domain'}.'</h2>';
! 95: return $title;
! 96: }
! 97:
! 98: sub parent_link {
! 99: my $self = shift;
! 100: my $link .= '<p><a href="/adm/classcalc?'.
! 101: 'sname='.$self->{'name'}.
! 102: '&sdomain='.$self->{'domain'}.'">'.
! 103: 'Course level sheet</a></p>'."\n";
! 104: return $link;
! 105: }
! 106:
! 107: sub outsheet_html {
! 108: my $self = shift;
! 109: my ($r) = @_;
! 110: ####################################
! 111: # Get the list of assessment files #
! 112: ####################################
! 113: my @AssessFileNames = $self->othersheets('assesscalc');
! 114: ####################################
! 115: # Determine table structure #
! 116: ####################################
! 117: my $num_uneditable = 26;
! 118: my $num_left = 52-$num_uneditable;
! 119: my $tableheader =<<"END";
! 120: <table border="2">
! 121: <tr>
! 122: <th colspan="2" rowspan="2"><font size="+2">Student</font></th>
! 123: <td bgcolor="#FFDDDD" colspan="$num_uneditable">
! 124: <b><font size="+1">Import</font></b></td>
! 125: <td colspan="$num_left">
! 126: <b><font size="+1">Calculations</font></b></td>
! 127: </tr><tr>
! 128: END
! 129: my $label_num = 0;
! 130: foreach (split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')){
! 131: if ($label_num<$num_uneditable) {
! 132: $tableheader .='<td bgcolor="#FFDDDD">';
! 133: } else {
! 134: $tableheader .='<td>';
! 135: }
! 136: $tableheader .="<b><font size=+1>$_</font></b></td>";
! 137: $label_num++;
! 138: }
! 139: $tableheader .="</tr>\n";
! 140: #
! 141: $r->print($tableheader);
! 142: #
! 143: # Print out template row
! 144: $r->print('<tr><td>Template</td><td> </td>'.
! 145: $self->html_template_row($num_uneditable)."</tr>\n");
! 146: #
! 147: # Print out summary/export row
! 148: $r->print('<tr><td>Export</td><td>0</td>'.
! 149: $self->html_export_row()."</tr>\n");
! 150: $r->print("</table>\n");
! 151: #
! 152: # Prepare to output rows
! 153: $tableheader =<<"END";
! 154: <table border="2">
! 155: <tr><th>Row</th><th>Assessment</th>
! 156: END
! 157: foreach (split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')){
! 158: if ($label_num<$num_uneditable) {
! 159: $tableheader.='<td bgcolor="#FFDDDD">';
! 160: } else {
! 161: $tableheader.='<td>';
! 162: }
! 163: $tableheader.="<b><font size=+1>$_</font></b></td>";
! 164: }
! 165: $tableheader.="\n";
! 166: #
! 167: my $num_output = 1;
! 168: if (scalar(@Sequences)< 1) {
! 169: &initialize_sequence_cache();
! 170: }
! 171: foreach my $Sequence (@Sequences) {
! 172: next if ($Sequence->{'num_assess'} < 1);
! 173: $r->print("<h3>".$Sequence->{'title'}."</h3>\n");
! 174: $r->print($tableheader);
! 175: foreach my $resource (@{$Sequence->{'contents'}}) {
! 176: next if ($resource->{'type'} ne 'assessment');
! 177: my $rownum = $self->get_row_number_from_key($resource->{'symb'});
! 178: my $assess_filename = $self->{'row_source'}->{$rownum};
! 179: my $row_output = '<tr><td>'.$rownum.'</td>';
! 180: $row_output .= '<td>'.
! 181: '<a href="/adm/assesscalc?sname='.$self->{'name'}.
! 182: '&sdomain='.$self->{'domain'}.
! 183: '&filename='.$assess_filename.
! 184: '&usymb='.$resource->{'symb'}.'">'.
! 185: $resource->{'title'}.'</a>';
! 186: $row_output .= &assess_file_selector($rownum,
! 187: $assess_filename,
! 188: \@AssessFileNames);
! 189: $row_output .= '</td>'.$self->html_row($num_uneditable,$rownum).
! 190: "</tr>\n";
! 191: $r->print($row_output);
! 192: }
! 193: $r->print("</table>\n");
! 194: }
! 195: return;
! 196: }
! 197:
! 198: ########################################################
! 199: ########################################################
! 200:
! 201: =pod
! 202:
! 203: =item &assess_file_selector()
! 204:
! 205: =cut
! 206:
! 207: ########################################################
! 208: ########################################################
! 209: sub assess_file_selector {
! 210: my ($row,$default,$AssessFiles)=@_;
! 211: return '' if (!defined($AssessFiles) || ! @$AssessFiles);
! 212: return '' if (! &Apache::lonnet::allowed('mgr',$ENV{'request.course.id'}));
! 213: my $element_name = 'FileSelect_'.$row;
! 214: my $load_dialog = '<select size="1" name="'.$element_name.'" '.
! 215: 'onchange="'.
! 216: "document.sheet.cell.value='source_$row';".
! 217: "document.sheet.newformula.value=document.sheet.$element_name\.value;".
! 218: 'document.sheet.submit()" '.'>'."\n";
! 219: foreach my $file (@{$AssessFiles}) {
! 220: $load_dialog .= ' <option name="'.$file.'"';
! 221: $load_dialog .= ' selected' if ($default eq $file);
! 222: $load_dialog .= '>'.$file."</option>\n";
! 223: }
! 224: $load_dialog .= "</select>\n";
! 225: return $load_dialog;
! 226: }
! 227:
! 228: sub modify_cell {
! 229: my $self = shift;
! 230: my ($cell,$formula) = @_;
! 231: if ($cell =~ /^source_(\d+)$/) {
! 232: # Need to make sure $formula is a valid filename....
! 233: my $row = $1;
! 234: $cell = 'A'.$row;
! 235: $self->{'row_source'}->{$row} = $formula;
! 236: my $original_source = $self->formula($cell);
! 237: if ($original_source =~ /__&&&__/) {
! 238: ($original_source,undef) = split('__&&&__',$original_source);
! 239: }
! 240: $formula = $original_source.'__&&&__'.$formula;
! 241: } elsif ($cell =~ /([A-z])\-/) {
! 242: $cell = 'template_'.$1;
! 243: } elsif ($cell !~ /^([A-z](\d+)|template_[A-z])$/) {
! 244: return;
! 245: }
! 246: $self->set_formula($cell,$formula);
! 247: $self->rebuild_stats();
! 248: return;
! 249: }
! 250:
! 251: sub outsheet_csv {
! 252: my $self = shift;
! 253: my ($r) = @_;
! 254: }
! 255: sub outsheet_excel {
! 256: my $self = shift;
! 257: my ($r) = @_;
! 258: }
! 259: sub outsheet_recursive_excel {
! 260: my $self = shift;
! 261: my ($r) = @_;
! 262: }
! 263:
! 264: sub display {
! 265: my $self = shift;
! 266: my ($r) = @_;
! 267: $self->compute();
! 268: $self->outsheet_html($r);
! 269: return;
! 270: }
! 271:
! 272: sub set_row_sources {
! 273: my $self = shift;
! 274: while (my ($cell,$value) = each(%{$self->{'formulas'}})) {
! 275: next if ($cell !~ /^A(\d+)/ && $1 > 0);
! 276: my $row = $1;
! 277: (undef,$value) = split('__&&&__',$value);
! 278: $value = 'Default' if (! defined($value));
! 279: $self->{'row_source'}->{$row} = $value;
! 280: }
! 281: return;
! 282: }
! 283:
! 284: sub compute {
! 285: my $self = shift;
! 286: $self->logthis('computing');
! 287: if (! defined($current_course) ||
! 288: $current_course ne $ENV{'request.course.id'}) {
! 289: $current_course = $ENV{'request.course.id'};
! 290: &clear_package();
! 291: &initialize_sequence_cache();
! 292: }
! 293: $self->initialize_safe_space();
! 294: my @sequences = @Sequences;
! 295: if (@sequences < 1) {
! 296: my ($top,$sequences,$assessments) =
! 297: &Apache::loncoursedata::get_sequence_assessment_data();
! 298: if (! defined($top) || ! ref($top)) {
! 299: &Apache::lonnet::logthis('top is undefined');
! 300: return;
! 301: }
! 302: @sequences = @{$sequences} if (ref($sequences) eq 'ARRAY');
! 303: }
! 304: &Apache::assesscalc::initialize_package($self->{'name'},$self->{'domain'});
! 305: my %f = $self->formulas();
! 306: #
! 307: # Process the formulas list -
! 308: # the formula for the A column of a row is symb__&&__filename
! 309: my %c = $self->constants();
! 310: foreach my $seq (@sequences) {
! 311: next if ($seq->{'num_assess'}<1);
! 312: foreach my $resource (@{$seq->{'contents'}}) {
! 313: next if ($resource->{'type'} ne 'assessment');
! 314: my $rownum = $self->get_row_number_from_key($resource->{'symb'});
! 315: my $cell = 'A'.$rownum;
! 316: my $assess_filename = 'Default';
! 317: if (exists($self->{'row_source'}->{$rownum})) {
! 318: $assess_filename = $self->{'row_source'}->{$rownum};
! 319: } else {
! 320: $self->{'row_source'}->{$rownum} = $assess_filename;
! 321: }
! 322: $f{$cell} = $resource->{'symb'}.'__&&&__'.$assess_filename;
! 323: my $assessSheet = Apache::assesscalc->new($self->{'name'},
! 324: $self->{'domain'},
! 325: $assess_filename,
! 326: $resource->{'symb'});
! 327: my @exportdata = $assessSheet->export_data();
! 328: #
! 329: # Be sure not to disturb the formulas in the 'A' column
! 330: my $data = shift(@exportdata);
! 331: $c{$cell} = $data if (defined($data));
! 332: #
! 333: # Deal with the remaining columns
! 334: my $i=0;
! 335: foreach (split(//,'BCDEFGHIJKLMNOPQRSTUVWXYZ')) {
! 336: my $cell = $_.$rownum;
! 337: my $data = shift(@exportdata);
! 338: if (defined($data)) {
! 339: $f{$cell} = 'import';
! 340: $c{$cell} = $data;
! 341: }
! 342: $i++;
! 343: }
! 344: }
! 345: }
! 346: $self->constants(\%c);
! 347: $self->formulas(\%f);
! 348: $self->calcsheet();
! 349: #
! 350: # Store export row in cache
! 351: my @exportarray=$self->exportrow();
! 352: my $student = $self->{'name'}.':'.$self->{'domain'};
! 353: $Exportrows{$student}->{'time'} = time;
! 354: $Exportrows{$student}->{'data'} = \@exportarray;
! 355: # save export row
! 356: $self->save_export_data();
! 357: return;
! 358: }
! 359:
! 360: sub set_row_numbers {
! 361: my $self = shift;
! 362: while (my ($cell,$formula) = each(%{$self->{'formulas'}})) {
! 363: next if ($cell !~ /^A(\d+)/);
! 364: my $row = $1;
! 365: next if ($row == 0);
! 366: my ($symb,undef) = split('__&&&__',$formula);
! 367: $self->{'row_numbers'}->{$symb} = $row;
! 368: }
! 369: }
! 370:
! 371: sub get_row_number_from_symb {
! 372: my $self = shift;
! 373: my ($key) = @_;
! 374: ($key,undef) = split('__&&&__',$key) if ($key =~ /__&&&__/);
! 375: return $self->get_row_number_from_key($key);
! 376: }
! 377:
! 378: #############################################
! 379: #############################################
! 380:
! 381: =pod
! 382:
! 383: =item &load_cached_export_rows
! 384:
! 385: Retrieves and parsers the export rows of the student spreadsheets.
! 386: These rows are saved in the courses directory in the format:
! 387:
! 388: sname:sdom:studentcalc:.time => time
! 389:
! 390: sname:sdom:studentcalc => ___=___Adata___;___Bdata___;___Cdata___;___ .....
! 391:
! 392: =cut
! 393:
! 394: #############################################
! 395: #############################################
! 396: sub load_cached_export_rows {
! 397: %Exportrows = undef;
! 398: my @tmp = &Apache::lonnet::dump('nohist_calculatedsheets',
! 399: $ENV{'course.'.$ENV{'request.course.id'}.'.domain'},
! 400: $ENV{'course.'.$ENV{'request.course.id'}.'.num'},undef);
! 401: my %Selected_Assess_Sheet;
! 402: if ($tmp[0] =~ /^error/) {
! 403: &Apache::lonnet::logthis('unable to read cached student export rows '.
! 404: 'for course '.$ENV{'request.course.id'});
! 405: return;
! 406: }
! 407: my %tmp = @tmp;
! 408: while (my ($key,$sheetdata) = each(%tmp)) {
! 409: my ($sname,$sdom,$sheettype,$remainder) = split(':',$key);
! 410: my $student = $sname.':'.$sdom;
! 411: if ($remainder =~ /\.time/) {
! 412: $Exportrows{$student}->{'time'} = $sheetdata;
! 413: } else {
! 414: $sheetdata =~ s/^___=___//;
! 415: my @Data = split('___;___',$sheetdata);
! 416: $Exportrows{$student}->{'data'} = \@Data;
! 417: }
! 418: }
! 419: }
! 420:
! 421: #############################################
! 422: #############################################
! 423:
! 424: =pod
! 425:
! 426: =item &save_export_data()
! 427:
! 428: Writes the export data for this student to the course cache.
! 429:
! 430: =cut
! 431:
! 432: #############################################
! 433: #############################################
! 434: sub save_export_data {
! 435: my $self = shift;
! 436: my $student = $self->{'name'}.':'.$self->{'domain'};
! 437: return if (! exists($Exportrows{$student}));
! 438: return if (! $self->is_default());
! 439: my $key = join(':',($self->{'name'},$self->{'domain'},'studentcalc')).':';
! 440: my $timekey = $key.'.time';
! 441: my $newstore = join('___;___',
! 442: @{$Exportrows{$student}->{'data'}});
! 443: $newstore = '___=___'.$newstore;
! 444: &Apache::lonnet::put('nohist_calculatedsheets',
! 445: { $key => $newstore,
! 446: $timekey => $Exportrows{$student}->{'time'} },
! 447: $self->{'cdom'},
! 448: $self->{'cnum'});
! 449: return;
! 450: }
! 451:
! 452: #############################################
! 453: #############################################
! 454:
! 455: =pod
! 456:
! 457: =item &export_data()
! 458:
! 459: Returns the export data associated with the spreadsheet. Computes the
! 460: spreadsheet only if necessary.
! 461:
! 462: =cut
! 463:
! 464: #############################################
! 465: #############################################
! 466: sub export_data {
! 467: my $self = shift;
! 468: my $student = $self->{'name'}.':'.$self->{'domain'};
! 469: if (! exists($Exportrows{$student}) ||
! 470: ! $self->check_expiration_time($Exportrows{$student}->{'time'})) {
! 471: $self->compute();
! 472: }
! 473: my @Data = @{$Exportrows{$student}->{'data'}};
! 474: for (my $i=0; $i<=$#Data;$i++) {
! 475: $Data[$i]="'".$Data[$i]."'" if ($Data[$i]=~/\D/ && defined($Data[$i]));
! 476: }
! 477: return @Data;
! 478: }
! 479:
! 480: 1;
! 481:
! 482: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>