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