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