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