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