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