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