Annotation of loncom/interface/spreadsheet/studentcalc.pm, revision 1.45
1.1 matthew 1: #
1.45 ! raeburn 2: # $Id: studentcalc.pm,v 1.44 2009/06/09 14:15:42 bisitz Exp $
1.1 matthew 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:
1.17 matthew 48: use warnings FATAL=>'all';
49: no warnings 'uninitialized';
50:
1.1 matthew 51: use strict;
52: use Apache::Constants qw(:common :http);
53: use Apache::lonnet;
1.3 matthew 54: use Apache::loncommon();
55: use Apache::loncoursedata();
1.1 matthew 56: use Apache::lonnavmaps;
1.3 matthew 57: use Apache::Spreadsheet();
58: use Apache::assesscalc();
1.1 matthew 59: use HTML::Entities();
60: use Time::HiRes;
1.23 www 61: use Apache::lonlocal;
1.40 www 62: use LONCAPA;
63:
1.1 matthew 64:
65: @Apache::studentcalc::ISA = ('Apache::Spreadsheet');
66:
67: my @Sequences = ();
1.28 matthew 68: my $navmap;
1.1 matthew 69: my %Exportrows = ();
70:
71: my $current_course;
72:
1.8 matthew 73: sub initialize {
74: &initialize_sequence_cache();
1.28 matthew 75: &Apache::assesscalc::initialize($navmap);
1.8 matthew 76: }
77:
1.1 matthew 78: sub initialize_package {
1.30 albertel 79: $current_course = $env{'request.course.id'};
1.1 matthew 80: &initialize_sequence_cache();
81: &load_cached_export_rows();
1.8 matthew 82: }
83:
84: sub ensure_correct_sequence_data {
1.30 albertel 85: if ($current_course ne $env{'request.course.id'}) {
1.8 matthew 86: &initialize_sequence_cache();
1.30 albertel 87: $current_course = $env{'request.course.id'};
1.8 matthew 88: }
89: return;
1.1 matthew 90: }
91:
92: sub initialize_sequence_cache {
93: #
94: # Set up the sequences and assessments
1.22 matthew 95: undef(@Sequences);
1.28 matthew 96: undef($navmap);
97: $navmap = Apache::lonnavmaps::navmap->new();
98: if (!defined($navmap)) {
99: &Apache::lonnet::logthis('student spreadsheet:Can not open Coursemap');
100: }
101: my @all_sequences = $navmap->retrieveResources(undef,
102: sub { shift->is_map(); },1,0,1);
1.45 ! raeburn 103: my $toplevelseq = $navmap->getById('0.0');
! 104: if (!grep(/^\Q$toplevelseq\E$/,@all_sequences)) {
! 105: unshift(@all_sequences,$toplevelseq);
! 106: }
! 107: foreach my $sequence (@all_sequences) {
! 108: if ($navmap->hasResource($sequence,sub { shift->is_problem(); }, 0,1)){
! 109: push(@Sequences,$sequence);
! 110: &get_resources($sequence);
! 111: }
1.1 matthew 112: }
1.28 matthew 113: }
114:
1.31 albertel 115: my %res_memoize;
1.28 matthew 116: sub get_resources {
117: my ($seq) = @_;
1.31 albertel 118: if (exists($res_memoize{$seq->symb()})) {
119: return @{$res_memoize{$seq->symb()}};
120: }
1.28 matthew 121: return () if (! defined($navmap) || ! ref($navmap));
122: my @resources = $navmap->retrieveResources($seq,
123: sub { shift->is_problem(); },
1.43 albertel 124: 0,0,1);
1.31 albertel 125: $res_memoize{$seq->symb()}=\@resources;
1.28 matthew 126: return @resources;
1.1 matthew 127: }
128:
129: sub clear_package {
1.17 matthew 130: undef(@Sequences);
131: undef(%Exportrows);
1.31 albertel 132: undef(%res_memoize);
1.33 albertel 133: undef($navmap);
1.18 matthew 134: &Apache::assesscalc::clear_package();
1.1 matthew 135: }
136:
137: sub get_title {
138: my $self = shift;
1.6 matthew 139: my @title = ();
140: #
141: # Determine the students name
1.26 albertel 142: my $name = &Apache::loncommon::plainname($self->{'name'},
143: $self->{'domain'});
1.6 matthew 144: push (@title,$name);
145: push (@title,$self->{'coursedesc'});
1.23 www 146: push (@title,&Apache::lonlocal::locallocaltime(time));
1.6 matthew 147: return @title;
148: }
149:
150: sub get_html_title {
151: my $self = shift;
152: my ($name,$desc,$time) = $self->get_title();
153: my $title = '<h1>'.$name;
1.30 albertel 154: if ($env{'user.name'} ne $self->{'name'} &&
155: $env{'user.domain'} ne $self->{'domain'}) {
1.19 matthew 156: $title .= ' '.&Apache::loncommon::aboutmewrapper
1.3 matthew 157: ($self->{'name'}.'@'.$self->{'domain'},
158: $self->{'name'},$self->{'domain'});
159: }
160: $title .= "</h1>\n";
1.6 matthew 161: $title .= '<h2>'.$desc."</h2>\n";
162: $title .= '<h3>'.$time.'</h3>';
1.1 matthew 163: return $title;
164: }
165:
166: sub parent_link {
167: my $self = shift;
1.23 www 168: return '<p><a href="/adm/classcalc">'.&mt('Course level sheet').'</a></p>'."\n";
1.1 matthew 169: }
170:
1.14 matthew 171: sub convenience_links {
172: my $self = shift;
173: my ($resource) = @_;
1.28 matthew 174: my $result=&Apache::loncommon::submlink('<img src="/adm/lonMisc/subm_button.gif" border="0" />',$self->{'name'},$self->{'domain'},$resource->symb,'LONcatInfo');
175: $result .= &Apache::loncommon::pgrdlink('<img src="/adm/lonMisc/pgrd_button.gif" border="0" />',$self->{'name'},$self->{'domain'},$resource->symb,'LONcatInfo');
176: $result .= &Apache::loncommon::pprmlink('<img src="/adm/lonMisc/pprm_button.gif" border="0" />',$self->{'name'},$self->{'domain'},$resource->symb,'LONcatInfo');
1.14 matthew 177: return $result;
178: }
179:
1.1 matthew 180: sub outsheet_html {
181: my $self = shift;
182: my ($r) = @_;
1.13 matthew 183: my $importcolor = '#FFFFAA';
1.12 matthew 184: my $exportcolor = '#88FF88';
1.1 matthew 185: ####################################
186: # Get the list of assessment files #
187: ####################################
188: my @AssessFileNames = $self->othersheets('assesscalc');
1.2 matthew 189: my $editing_is_allowed = &Apache::lonnet::allowed('mgr',
1.30 albertel 190: $env{'request.course.id'});
1.1 matthew 191: ####################################
1.24 matthew 192: # Report any calculation errors #
193: ####################################
194: $r->print($self->html_report_error());
195: ####################################
1.1 matthew 196: # Determine table structure #
197: ####################################
198: my $num_uneditable = 26;
199: my $num_left = 52-$num_uneditable;
1.23 www 200: my %lt=&Apache::lonlocal::texthash(
201: 'st' => 'Student',
202: 'im' => 'Import',
203: 'ca' => 'Calculations',
204: 'as' => 'Assessment',
205: 'ro' => 'Row',
206: );
1.1 matthew 207: my $tableheader =<<"END";
1.2 matthew 208: <p>
1.1 matthew 209: <table border="2">
210: <tr>
1.23 www 211: <th colspan="2" rowspan="2"><font size="+2">$lt{'st'}</font></th>
1.12 matthew 212: <td bgcolor="$importcolor" colspan="$num_uneditable">
1.23 www 213: <b><font size="+1">$lt{'im'}</font></b></td>
1.1 matthew 214: <td colspan="$num_left">
1.23 www 215: <b><font size="+1">$lt{'ca'}</font></b></td>
1.1 matthew 216: </tr><tr>
217: END
218: my $label_num = 0;
219: foreach (split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')){
220: if ($label_num<$num_uneditable) {
1.12 matthew 221: $tableheader .='<td bgcolor="'.$importcolor.'">';
1.1 matthew 222: } else {
223: $tableheader .='<td>';
224: }
225: $tableheader .="<b><font size=+1>$_</font></b></td>";
226: $label_num++;
227: }
228: $tableheader .="</tr>\n";
1.4 matthew 229: if ($self->blackout()) {
1.44 bisitz 230: $r->print('<p class="LC_warning">'.
1.23 www 231: &mt('Some computations are not available at this time.').'<br />'.
232: &mt('There are problems whose status you are not allowed to view.').
1.44 bisitz 233: '</p>'."\n");
1.4 matthew 234: } else {
235: $r->print($tableheader);
236: #
237: # Print out template row
1.30 albertel 238: if (exists($env{'request.role.adv'}) && $env{'request.role.adv'}) {
1.44 bisitz 239: $r->print('<tr><td>'.&mt('Template').'</td><td> </td>'.
1.12 matthew 240: $self->html_template_row($num_uneditable,
241: $importcolor)."</tr>\n");
1.4 matthew 242: }
243: #
244: # Print out summary/export row
1.23 www 245: $r->print('<tr><td>'.&mt('Summary').'</td><td>0</td>'.
1.12 matthew 246: $self->html_export_row($exportcolor)."</tr>\n");
1.4 matthew 247: }
1.1 matthew 248: $r->print("</table>\n");
249: #
250: # Prepare to output rows
1.30 albertel 251: if (exists($env{'request.role.adv'}) && $env{'request.role.adv'}) {
1.4 matthew 252: $tableheader =<<"END";
1.2 matthew 253: </p><p>
1.1 matthew 254: <table border="2">
1.23 www 255: <tr><th>$lt{'ro'}</th><th> </th><th>$lt{'as'}</th>
1.1 matthew 256: END
1.4 matthew 257: } else {
258: $tableheader =<<"END";
259: </p><p>
260: <table border="2">
1.23 www 261: <tr><th> </th><th>$lt{'as'}</th>
1.4 matthew 262: END
263: }
1.1 matthew 264: foreach (split(//,'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz')){
265: if ($label_num<$num_uneditable) {
266: $tableheader.='<td bgcolor="#FFDDDD">';
267: } else {
268: $tableheader.='<td>';
269: }
270: $tableheader.="<b><font size=+1>$_</font></b></td>";
271: }
272: $tableheader.="\n";
273: #
274: my $num_output = 1;
275: if (scalar(@Sequences)< 1) {
276: &initialize_sequence_cache();
277: }
278: foreach my $Sequence (@Sequences) {
1.28 matthew 279: $r->print("<h3>".$Sequence->compTitle."</h3>\n");
1.36 bowersj2 280:
281: my @resources = &get_resources($Sequence);
282: my $first_rownum =
283: $self->get_row_number_from_key($resources[0]->symb);
284: my $last_rownum =
285: $self->get_row_number_from_key($resources[-1]->symb);
286: $r->print(&assess_file_selector([$first_rownum, $last_rownum],
287: undef, \@AssessFileNames));
288:
1.1 matthew 289: $r->print($tableheader);
1.36 bowersj2 290: foreach my $resource (@resources) {
1.28 matthew 291: my $rownum = $self->get_row_number_from_key($resource->symb);
1.1 matthew 292: my $assess_filename = $self->{'row_source'}->{$rownum};
1.2 matthew 293: my $row_output = '<tr>';
294: if ($editing_is_allowed) {
295: $row_output .= '<td>'.$rownum.'</td>';
1.14 matthew 296: $row_output .= '<td>'.$self->convenience_links($resource).'</td>';
1.2 matthew 297: $row_output .= '<td>'.
298: '<a href="/adm/assesscalc?sname='.$self->{'name'}.
299: '&sdomain='.$self->{'domain'}.
300: '&filename='.$assess_filename.
1.40 www 301: '&usymb='.&escape($resource->symb).
1.28 matthew 302: '">'.$resource->compTitle.'</a><br />';
1.2 matthew 303: $row_output .= &assess_file_selector($rownum,
304: $assess_filename,
305: \@AssessFileNames).
306: '</td>';
307: } else {
1.28 matthew 308: $row_output .= '<td><a href="'.$resource->src.'?symb='.
1.40 www 309: &escape($resource->symb).
1.4 matthew 310: '">Go To</a>';
1.28 matthew 311: $row_output .= '</td><td>'.$resource->compTitle.'</td>';
1.2 matthew 312: }
1.4 matthew 313: if ($self->blackout() && $self->{'blackout_rows'}->{$rownum}>0) {
314: $row_output .=
1.23 www 315: '<td colspan="52">'.&mt('Unavailable at this time').'</td></tr>'."\n";
1.4 matthew 316: } else {
1.12 matthew 317: $row_output .= $self->html_row($num_uneditable,$rownum,
318: $exportcolor,$importcolor).
1.4 matthew 319: "</tr>\n";
320: }
1.1 matthew 321: $r->print($row_output);
322: }
323: $r->print("</table>\n");
324: }
1.2 matthew 325: $r->print("</p>\n");
1.1 matthew 326: return;
327: }
328:
329: ########################################################
330: ########################################################
331:
332: =pod
333:
334: =item &assess_file_selector()
335:
336: =cut
337:
338: ########################################################
339: ########################################################
340: sub assess_file_selector {
341: my ($row,$default,$AssessFiles)=@_;
1.2 matthew 342: if (!defined($AssessFiles) || ! @$AssessFiles) {
343: return '';
344: }
1.30 albertel 345: return '' if (! &Apache::lonnet::allowed('mgr',$env{'request.course.id'}));
1.36 bowersj2 346: my $element_name;
347: my $source_row = $row;
348: if (ref($row)) {
349: my ($first_rownum, $last_rownum) = @$row;
350: $element_name = "FileSelect_${first_rownum}_${last_rownum}";
351: $source_row = "${first_rownum}:${last_rownum}";
352: } else {
353: $element_name = 'FileSelect_'.$row;
354: }
1.1 matthew 355: my $load_dialog = '<select size="1" name="'.$element_name.'" '.
356: 'onchange="'.
1.36 bowersj2 357: "document.sheet.cell.value='source_${source_row}';".
1.1 matthew 358: "document.sheet.newformula.value=document.sheet.$element_name\.value;".
359: 'document.sheet.submit()" '.'>'."\n";
1.36 bowersj2 360: if (ref($row)) {
361: $load_dialog .= ' <option name="" value="">' .
362: &mt("Select spreadsheet for entire sequence")
363: . "</option>\n";
364: }
1.1 matthew 365: foreach my $file (@{$AssessFiles}) {
366: $load_dialog .= ' <option name="'.$file.'"';
367: $load_dialog .= ' selected' if ($default eq $file);
368: $load_dialog .= '>'.$file."</option>\n";
369: }
370: $load_dialog .= "</select>\n";
371: return $load_dialog;
372: }
373:
374: sub modify_cell {
375: my $self = shift;
376: my ($cell,$formula) = @_;
1.36 bowersj2 377:
378: my $set_row = sub {
379: my $row = shift;
380: my $formula = shift;
381: my $cell = 'A' . $row;
1.1 matthew 382: $self->{'row_source'}->{$row} = $formula;
383: my $original_source = $self->formula($cell);
384: if ($original_source =~ /__&&&__/) {
385: ($original_source,undef) = split('__&&&__',$original_source);
386: }
387: $formula = $original_source.'__&&&__'.$formula;
1.36 bowersj2 388: $self->set_formula($cell,$formula);
389: };
390:
391: if ($cell =~ /^source_(\d+):(\d+)$/) {
392: my $first_row = $1;
393: my $last_row = $2;
394: for my $row ($first_row..$last_row) {
395: $set_row->($row, $formula);
396: }
397: } elsif ($cell =~ /^source_(\d+)$/) {
398: # Need to make sure $formula is a valid filename....
399: my $row = $1;
400: $set_row->($row, $formula);
1.1 matthew 401: } elsif ($cell =~ /([A-z])\-/) {
402: $cell = 'template_'.$1;
1.36 bowersj2 403: $self->set_formula($cell,$formula);
1.1 matthew 404: } elsif ($cell !~ /^([A-z](\d+)|template_[A-z])$/) {
405: return;
1.41 raeburn 406: } else {
407: $self->set_formula($cell,$formula);
1.1 matthew 408: }
409: $self->rebuild_stats();
410: return;
411: }
412:
1.7 matthew 413: sub csv_rows {
414: # writes the meat of the spreadsheet to an excel worksheet. Called
415: # by Spreadsheet::outsheet_excel;
1.1 matthew 416: my $self = shift;
1.25 matthew 417: my ($connection,$filehandle) = @_;
1.7 matthew 418: #
419: # Write a header row
420: $self->csv_output_row($filehandle,undef,
1.23 www 421: (&mt('Sequence or Folder'),&mt('Assessment title')));
1.7 matthew 422: #
423: # Write each assessments row
424: if (scalar(@Sequences)< 1) {
425: &initialize_sequence_cache();
426: }
427: foreach my $Sequence (@Sequences) {
1.28 matthew 428: foreach my $resource (&get_resources($Sequence)) {
429: my $rownum = $self->get_row_number_from_key($resource->symb);
430: my @assessdata = ($Sequence->compTitle,
431: $resource->compTitle);
1.7 matthew 432: $self->csv_output_row($filehandle,$rownum,@assessdata);
433: }
434: }
435: return;
1.1 matthew 436: }
1.6 matthew 437:
438: sub excel_rows {
439: # writes the meat of the spreadsheet to an excel worksheet. Called
440: # by Spreadsheet::outsheet_excel;
1.1 matthew 441: my $self = shift;
1.29 matthew 442: my ($connection,$worksheet,$cols_output,$rows_output,$format) = @_;
1.6 matthew 443: #
444: # Write a header row
445: $cols_output = 0;
446: foreach my $value ('Container','Assessment title') {
1.29 matthew 447: $worksheet->write($rows_output,$cols_output++,&mt($value),$format->{'h4'});
1.6 matthew 448: }
449: $rows_output++;
450: #
451: # Write each assessments row
452: if (scalar(@Sequences)< 1) {
453: &initialize_sequence_cache();
454: }
455: foreach my $Sequence (@Sequences) {
1.28 matthew 456: foreach my $resource (&get_resources($Sequence)) {
457: my $rownum = $self->get_row_number_from_key($resource->symb);
458: my @assessdata = ($Sequence->compTitle,
459: $resource->compTitle);
1.6 matthew 460: $self->excel_output_row($worksheet,$rownum,$rows_output++,
461: @assessdata);
462: }
463: }
464: return;
1.1 matthew 465: }
1.6 matthew 466:
1.1 matthew 467: sub outsheet_recursive_excel {
468: my $self = shift;
469: my ($r) = @_;
470: }
471:
1.22 matthew 472: ##
473: ## Routines to deal with sequences in the safe space
474: ##
475: sub get_rows_in_sequence {
476: my $self = shift();
477: my ($sequence) = @_;
478: my @Rows;
1.28 matthew 479: my @resources = &get_resources($sequence);
480: foreach my $resource (@resources) {
481: my $rownum = $self->get_row_number_from_key($resource->symb);
482: push (@Rows,$rownum);
1.22 matthew 483: }
484: return @Rows;
485: }
486:
487: sub remove_sequence_data_from_safe_space {
488: my $self = shift();
489: my $command = 'undef(%Sequence_Rows);';
490: $self->{'safe'}->reval($command);
491: }
492:
493: sub put_sequence_data_in_safe_space {
494: my $self = shift();
495: my $data = 'undef(%Sequence_Rows);';
496: # Build up the %Sequence_Rows hash - each sequence title is associated with
497: # an array pointer, which holds the rows in the sequence.
498: foreach my $seq (@Sequences) {
499: my @Rows = $self->get_rows_in_sequence($seq);
500: #
501: # Potential problems with sequence titles:
502: # 1. duplicate titles - they get the total for the titles
503: # 2. control characters in titles - use q{} around the string to
504: # deal with it.
1.34 albertel 505: my $title = &HTML::Entities::decode($seq->title());
1.22 matthew 506: $title =~ s/&\#058;/:/g;
507: if (@Rows) {
508: $data .= 'push(@{$Sequence_Rows{"'.quotemeta($title).'"}},'.
509: '('.join(',',@Rows).'));'."\n";;
510: }
511: }
512: my $new_code = $data.<<'END';
513: sub SUMSEQ {
514: my ($col,@titles) = @_;
515: return 'bad column: '.$col if ($col !~ /^[A-z]$/);
516: my $sum = 0;
517: foreach my $title (@titles) {
518: while (my ($seq_title,$rows) = each(%Sequence_Rows)) {
519: my $regexp;
520: if ($title =~ /^regexp:(.*)$/) {
521: $regexp = $1;
522: } elsif (lc($title) eq 'all') {
523: $regexp = '.';
524: }
525: if (defined($regexp)) {
526: next if ($seq_title !~ /$regexp/);
527: } else {
528: next if ($seq_title ne $title);
529: }
530: foreach my $rownum (@{$rows}) {
531: my $cell = $col.$rownum;
532: if (exists($sheet_values{$cell})) {
533: $sum += $sheet_values{$cell};
534: }
535: }
536: }
537: }
538: return $sum;
539: }
540: END
541: $self->{'safe'}->reval($new_code);
542: return;
543: }
544:
545: ##
546: ## Main computation method
547: ##
1.1 matthew 548: sub compute {
549: my $self = shift;
1.18 matthew 550: my ($r) = @_;
1.1 matthew 551: if (! defined($current_course) ||
1.30 albertel 552: $current_course ne $env{'request.course.id'} ||
1.22 matthew 553: ! @Sequences ) {
1.30 albertel 554: $current_course = $env{'request.course.id'};
1.1 matthew 555: &clear_package();
556: &initialize_sequence_cache();
557: }
558: $self->initialize_safe_space();
1.28 matthew 559: &Apache::assesscalc::initialize_package($self->{'name'},$self->{'domain'},
1.42 albertel 560: $navmap,$self);
1.1 matthew 561: my %f = $self->formulas();
562: #
563: # Process the formulas list -
564: # the formula for the A column of a row is symb__&&__filename
565: my %c = $self->constants();
1.22 matthew 566: foreach my $seq (@Sequences) {
1.28 matthew 567: foreach my $resource (&get_resources($seq)) {
568: my $rownum = $self->get_row_number_from_key($resource->symb);
1.1 matthew 569: my $cell = 'A'.$rownum;
570: my $assess_filename = 'Default';
571: if (exists($self->{'row_source'}->{$rownum})) {
572: $assess_filename = $self->{'row_source'}->{$rownum};
573: } else {
574: $self->{'row_source'}->{$rownum} = $assess_filename;
575: }
1.28 matthew 576: $f{$cell} = $resource->symb.'__&&&__'.$assess_filename;
1.24 matthew 577: my $assessSheet;
578: $assessSheet = Apache::assesscalc->new($self->{'name'},
579: $self->{'domain'},
580: $assess_filename,
1.37 albertel 581: $resource->symb,
1.38 raeburn 582: $self->{'section'},
1.39 raeburn 583: $self->{'groups'});
1.18 matthew 584: my @exportdata = $assessSheet->export_data($r);
1.24 matthew 585: #
586: if ($assessSheet->badcalc()) {
587: $self->set_calcerror(
588: &mt('Error computing row for assessment "[_1]" (row [_2]):[_3]',
589: $assessSheet->get_title(),$rownum,$assessSheet->calcerror()));
590: }
591: #
1.4 matthew 592: if ($assessSheet->blackout()) {
593: $self->blackout(1);
594: $self->{'blackout_rows'}->{$rownum} = 1;
595: }
1.1 matthew 596: #
597: # Be sure not to disturb the formulas in the 'A' column
598: my $data = shift(@exportdata);
599: $c{$cell} = $data if (defined($data));
600: #
601: # Deal with the remaining columns
602: my $i=0;
603: foreach (split(//,'BCDEFGHIJKLMNOPQRSTUVWXYZ')) {
604: my $cell = $_.$rownum;
605: my $data = shift(@exportdata);
606: if (defined($data)) {
607: $f{$cell} = 'import';
608: $c{$cell} = $data;
609: }
610: $i++;
611: }
612: }
613: }
614: $self->constants(\%c);
615: $self->formulas(\%f);
1.22 matthew 616: $self->put_sequence_data_in_safe_space();
1.1 matthew 617: $self->calcsheet();
1.22 matthew 618: $self->remove_sequence_data_from_safe_space();
1.1 matthew 619: #
620: # Store export row in cache
621: my @exportarray=$self->exportrow();
622: my $student = $self->{'name'}.':'.$self->{'domain'};
623: $Exportrows{$student}->{'time'} = time;
624: $Exportrows{$student}->{'data'} = \@exportarray;
625: # save export row
626: $self->save_export_data();
1.9 matthew 627: #
628: $self->save() if ($self->need_to_save());
629: return;
630: }
631:
632: sub set_row_sources {
633: my $self = shift;
1.35 albertel 634: $self->check_formulas_loaded();
1.9 matthew 635: while (my ($cell,$value) = each(%{$self->{'formulas'}})) {
1.17 matthew 636: next if ($cell !~ /^A(\d+)$/ || $1 < 1);
1.9 matthew 637: my $row = $1;
638: (undef,$value) = split('__&&&__',$value);
639: $value = 'Default' if (! defined($value));
640: $self->{'row_source'}->{$row} = $value;
641: }
1.1 matthew 642: return;
643: }
644:
645: sub set_row_numbers {
646: my $self = shift;
1.35 albertel 647: $self->check_formulas_loaded();
1.1 matthew 648: while (my ($cell,$formula) = each(%{$self->{'formulas'}})) {
649: next if ($cell !~ /^A(\d+)/);
650: my $row = $1;
651: next if ($row == 0);
652: my ($symb,undef) = split('__&&&__',$formula);
653: $self->{'row_numbers'}->{$symb} = $row;
1.17 matthew 654: $self->{'maxrow'} = $row if ($row > $self->{'maxrow'});
1.1 matthew 655: }
656: }
657:
658: sub get_row_number_from_symb {
659: my $self = shift;
660: my ($key) = @_;
661: ($key,undef) = split('__&&&__',$key) if ($key =~ /__&&&__/);
662: return $self->get_row_number_from_key($key);
663: }
664:
665: #############################################
666: #############################################
667:
668: =pod
669:
670: =item &load_cached_export_rows
671:
672: Retrieves and parsers the export rows of the student spreadsheets.
673: These rows are saved in the courses directory in the format:
674:
675: sname:sdom:studentcalc:.time => time
676:
677: sname:sdom:studentcalc => ___=___Adata___;___Bdata___;___Cdata___;___ .....
678:
679: =cut
680:
681: #############################################
682: #############################################
683: sub load_cached_export_rows {
1.17 matthew 684: undef(%Exportrows);
1.1 matthew 685: my @tmp = &Apache::lonnet::dump('nohist_calculatedsheets',
1.30 albertel 686: $env{'course.'.$env{'request.course.id'}.'.domain'},
687: $env{'course.'.$env{'request.course.id'}.'.num'},undef);
1.1 matthew 688: my %Selected_Assess_Sheet;
689: if ($tmp[0] =~ /^error/) {
690: &Apache::lonnet::logthis('unable to read cached student export rows '.
1.30 albertel 691: 'for course '.$env{'request.course.id'});
1.1 matthew 692: return;
693: }
694: my %tmp = @tmp;
695: while (my ($key,$sheetdata) = each(%tmp)) {
696: my ($sname,$sdom,$sheettype,$remainder) = split(':',$key);
697: my $student = $sname.':'.$sdom;
698: if ($remainder =~ /\.time/) {
699: $Exportrows{$student}->{'time'} = $sheetdata;
700: } else {
701: $sheetdata =~ s/^___=___//;
702: my @Data = split('___;___',$sheetdata);
703: $Exportrows{$student}->{'data'} = \@Data;
704: }
705: }
706: }
707:
708: #############################################
709: #############################################
710:
711: =pod
712:
713: =item &save_export_data()
714:
715: Writes the export data for this student to the course cache.
716:
717: =cut
718:
719: #############################################
720: #############################################
721: sub save_export_data {
722: my $self = shift;
1.24 matthew 723: my $student = $self->{'name'}.':'.$self->{'domain'};
724: return if ($self->temporary());
725: if ($self->badcalc()){
726: # do not save data away when calculations have not been done properly.
727: delete($Exportrows{$student});
728: return;
729: }
730: return if (! exists($Exportrows{$student}));
1.20 matthew 731: &Apache::assesscalc::save_cached_export_rows($self->{'name'},
732: $self->{'domain'});
1.1 matthew 733: return if (! $self->is_default());
734: my $key = join(':',($self->{'name'},$self->{'domain'},'studentcalc')).':';
735: my $timekey = $key.'.time';
736: my $newstore = join('___;___',
737: @{$Exportrows{$student}->{'data'}});
738: $newstore = '___=___'.$newstore;
1.16 matthew 739: my $result= &Apache::lonnet::put('nohist_calculatedsheets',
1.1 matthew 740: { $key => $newstore,
741: $timekey => $Exportrows{$student}->{'time'} },
742: $self->{'cdom'},
743: $self->{'cnum'});
744: return;
745: }
746:
747: #############################################
748: #############################################
749:
750: =pod
751:
752: =item &export_data()
753:
754: Returns the export data associated with the spreadsheet. Computes the
755: spreadsheet only if necessary.
756:
757: =cut
758:
759: #############################################
760: #############################################
761: sub export_data {
762: my $self = shift;
1.18 matthew 763: my ($r) = @_;
764: my $connection = $r->connection();
1.1 matthew 765: my $student = $self->{'name'}.':'.$self->{'domain'};
766: if (! exists($Exportrows{$student}) ||
1.15 matthew 767: ! defined($Exportrows{$student}) ||
1.16 matthew 768: ! exists($Exportrows{$student}->{'data'}) ||
1.15 matthew 769: ! defined($Exportrows{$student}->{'data'}) ||
1.16 matthew 770: ! exists($Exportrows{$student}->{'time'}) ||
771: ! defined($Exportrows{$student}->{'time'}) ||
1.1 matthew 772: ! $self->check_expiration_time($Exportrows{$student}->{'time'})) {
1.18 matthew 773: $self->compute($r);
1.1 matthew 774: }
1.18 matthew 775: if ($connection->aborted()) { $self->cleanup(); return; }
1.24 matthew 776: my @Data;
777: if ($self->badcalc()) {
778: @Data = ();
779: } else {
780: @Data = @{$Exportrows{$student}->{'data'}};
781: for (my $i=0; $i<=$#Data;$i++) {
782: if ($Data[$i]=~/\D/ && defined($Data[$i])) {
783: $Data[$i]="'".$Data[$i]."'";
784: }
785: }
1.1 matthew 786: }
787: return @Data;
788: }
789:
790: 1;
791:
792: __END__
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>