Annotation of loncom/interface/lonpdfupload.pm, revision 1.17

1.1       onken       1: # The LearningOnline Network with CAPA
1.12      bisitz      2: # PDF Form Upload Handler
1.1       onken       3: #
1.17    ! raeburn     4: # $Id: lonpdfupload.pm,v 1.16 2010/03/18 14:50:15 raeburn Exp $
1.1       onken       5: #
                      6: # Copyright Michigan State University Board of Trustees
                      7: #
                      8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA).
                      9: #
                     10: # LON-CAPA is free software; you can redistribute it and/or modify
                     11: # it under the terms of the GNU General Public License as published by
                     12: # the Free Software Foundation; either version 2 of the License, or
                     13: # (at your option) any later version.
                     14: #
                     15: # LON-CAPA is distributed in the hope that it will be useful,
                     16: # but WITHOUT ANY WARRANTY; without even the implied warranty of
                     17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
                     18: # GNU General Public License for more details.
                     19: #
                     20: # You should have received a copy of the GNU General Public License
                     21: # along with LON-CAPA; if not, write to the Free Software
                     22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
                     23: #
                     24: # /home/httpd/html/adm/gpl.txt
                     25: #
                     26: # http://www.lon-capa.org/
                     27: #
                     28: package Apache::lonpdfupload;
                     29: 
                     30: use lib '/home/httpd/lib/perl';
                     31: use Apache::Constants qw(:common :http);
1.15      raeburn    32: use Apache::lonnet;
1.1       onken      33: use Apache::lonhtmlcommon();
                     34: use Apache::loncommon();
                     35: use Apache::lonlocal;
1.16      raeburn    36: use File::MMagic;
1.2       onken      37: use CAM::PDF;
1.1       onken      38: 
                     39: use strict;
                     40: 
                     41: sub handler() {
1.2       onken      42:     my $r = shift;
1.15      raeburn    43:     &Apache::loncommon::content_type($r,'text/html');
                     44:     $r->send_http_header;
                     45:     return OK if $r->header_only;
1.13      bisitz     46: 
1.16      raeburn    47:     #  Needs to be in a course
                     48:     if (!$env{'request.course.fn'}) {
                     49:         # Not in a course
                     50:         $env{'user.error.msg'}="/adm/pdfupload:bre:0:0:Cannot upload PDF forms unless in a course";
                     51:         return HTTP_NOT_ACCEPTABLE;
                     52:     }
                     53: 
1.13      bisitz     54:     # Breadcrumbs
1.15      raeburn    55:     my $brcrum = [{'href' => '/adm/pdfupload',
1.13      bisitz     56:                    'text' => 'Upload PDF Form'}];
1.16      raeburn    57:     if ($env{'form.Uploaded'} && $env{'form.file'}) {
                     58:         push(@{$brcrum},{'href'  => '',
                     59:                          'text'  => 'PDF upload result'});
                     60:     }
1.13      bisitz     61: 
                     62:     $r->print(&Apache::loncommon::start_page('Upload PDF Form',
                     63:                                              undef,
                     64:                                              {'bread_crumbs' => $brcrum,})
                     65:     );
1.2       onken      66: 
1.16      raeburn    67:     if ($env{'request.course.id'}) {
                     68:         my $permission = $env{'course.'.$env{'request.course.id'}.'.canuse_pdfforms'};
                     69:         if ($permission eq '') {
                     70:             my %domdefs = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'});
                     71:             $permission = $domdefs{'canuse_pdfforms'};
                     72:         }
                     73:         unless ($permission) {
                     74:             $r->print('<p class="LC_warning">'.
                     75:                       &mt('Upload of PDF forms is not permitted for this course.').
                     76:                       '</p>'.
                     77:                       &Apache::loncommon::end_page());
                     78:             return OK;
                     79:         }
                     80:     } else {
                     81:         $r->print('<p class="LC_warning">'.
                     82:                   &mt('Could not determine identity of this course. you may need to [_1]re-select[_2] the course.','<a href="/adm/roles">','</a>').
                     83:                   '</p>'.
                     84:                   &Apache::loncommon::end_page());
                     85:         return OK;
                     86:     }
                     87: 
1.2       onken      88:     # if a file was upload
                     89:     if($env{'form.Uploaded'} && $env{'form.file'}) {
1.16      raeburn    90:         my $mm = new File::MMagic;
                     91:         my $mime_type = $mm->checktype_contents($env{'form.file'});
                     92:         if ($mime_type eq 'application/pdf') {
                     93:             $r->print(&processPDF);
                     94:         } else {
                     95:             $r->print('<p class="LC_error">'
                     96:                      .&mt("The uploaded file does not appear to be a PDF file.")
                     97:                      .'</p>');
                     98:         }
1.2       onken      99:     } else { 
                    100:         # print upload form
                    101:         $r->print(&get_javascripts);
                    102:         $r->print(&get_uploadform);
                    103:     }
                    104: 
                    105:     #link to course-content
1.6       bisitz    106:     $r->print('<hr />'
                    107:              .'<p>'."\n"
1.5       bisitz    108:              .'<a href="/adm/navmaps">'."\n"
1.14      raeburn   109:              .&mt('Course Contents')."\n"
1.5       bisitz    110:              .'</a>'."\n"
                    111:              .'</p>'."\n"
                    112:     );
1.1       onken     113: 
1.2       onken     114:     #&dumpenv($r); #debug -> prints the environment
1.7       onken     115:     $r->print(&Apache::loncommon::end_page());
1.1       onken     116:     return OK;
1.2       onken     117: }
1.1       onken     118: 
                    119: sub get_javascripts() {
1.2       onken     120:     
1.6       bisitz    121:     my $message = &mt('Please choose a PDF-File.');
1.1       onken     122: 
1.2       onken     123:     # simple test if the upload ends with ".pdf"
                    124:     # it's only for giving a message to the user
                    125:     my $result .= <<END
                    126:   <script type="text/javascript">
1.1       onken     127:     function checkFilename(form) {
                    128:         var fileExt = form.file.value;
                    129:         fileExt = fileExt.match(/[.]pdf\$/g);
                    130:         if(fileExt) {
                    131:             return true;
                    132:         }
1.2       onken     133:         alert("$message");
1.1       onken     134:         return false;
                    135:     }
1.2       onken     136:   </script>
1.1       onken     137: END
                    138: ;
                    139:     return $result; 
                    140: }
                    141: 
1.2       onken     142: 
1.1       onken     143: sub get_uploadform() {
1.4       onken     144:     
                    145:     my %lt = &Apache::lonlocal::texthash(
1.6       bisitz    146:                  'title'  => 'Upload a PDF Form with filled Form Fields', 
                    147:                  'chFile' => 'File',
                    148:                  'submit' => 'Upload',
1.4       onken     149:              );
                    150: 
1.5       bisitz    151:     my $result = 
                    152:         '<br />'
                    153:        .'<form method="post" enctype="multipart/form-data" onsubmit="return checkFilename(this);">'
                    154:        .'<input type="hidden" name="type" value="upload" />'
                    155:        .&Apache::lonhtmlcommon::start_pick_box()
                    156:        .&Apache::lonhtmlcommon::row_headline()
                    157:        .'<h2>'.$lt{'title'}.'</h2>'
                    158:        .&Apache::lonhtmlcommon::row_closure()
                    159:        .&Apache::lonhtmlcommon::row_title($lt{'chFile'})
                    160:        .'<input type="file" name="file" id="filename" />'
                    161:        .&Apache::lonhtmlcommon::row_closure(1)
                    162:        .&Apache::lonhtmlcommon::end_pick_box()
1.13      bisitz    163:        .'<p>'
1.5       bisitz    164:        .'<input type="submit" name="Uploaded" value="'.$lt{'submit'}.'" />'
1.13      bisitz    165:        .'</p>'
1.5       bisitz    166:        .'</form>'
1.6       bisitz    167:        .'<br />';
1.5       bisitz    168: 
1.1       onken     169:   return $result;
                    170: }
                    171: 
                    172: sub processPDF {
1.2       onken     173:     my $result = ();  # message for Browser
                    174:     my @pdfdata = (); # answers from PDF-Forms
1.1       onken     175:     
1.2       onken     176:     @pdfdata = &get_pdf_data(); # get answers from PDF-Form
1.1       onken     177:     
                    178:     if (scalar @pdfdata) {    
1.2       onken     179:         &grade_pdf(@pdfdata);
1.1       onken     180:     } else {
1.6       bisitz    181:         $result .= '<p class="LC_error">'
                    182:                   .&mt("Can't find any valid PDF formfields.")
                    183:                   .'</p>';
1.1       onken     184:     }
                    185: }
                    186: 
                    187: sub get_pdf_data() {
                    188:     my @data = ();
1.2       onken     189:     my $pdf = CAM::PDF->new($env{'form.file'});
                    190: 
                    191:     my @formFields = $pdf->getFormFieldList(); #get names of formfields
1.1       onken     192:     
1.2       onken     193:     foreach my $field (@formFields) {
                    194: 	my $dict = $pdf->getFormFieldDict($pdf->getFormField($field)); # get formfield dictonary
1.1       onken     195: 
1.2       onken     196:         #
1.15      raeburn   197:         # this is necessary because CAM::PDF has a problem with formfieldnames which include a
1.2       onken     198:         # dot in fieldnames. So a fieldname like "i.am.aFormfield" will offer three fieldnames "i", "i.am" 
                    199:         # and "i.am.aFormfield". The fragmentary names keep no values and will be ignored.
                    200:         if($dict->{'V'}) {
                    201:             push(@data, $field."?". $dict->{'V'}{'value'}); #binding fieldname with value
                    202:         }
                    203:     } 
1.1       onken     204:     return @data;
                    205: }
                    206: 
                    207: sub grade_pdf {
                    208:     my $result = ();
                    209:     my @pdfdata = @_;
1.17    ! raeburn   210:     my ($result,$meta,%grades,%problems,$debug);   
1.1       onken     211:    
                    212:     $debug  .= "Found: ". scalar @pdfdata." Entries \n";
1.4       onken     213: 
1.1       onken     214:     foreach my $entry (sort(@pdfdata)) {
                    215:         if ($entry =~ /^meta.*/) {
1.2       onken     216:             $debug .= 'found: metadata -> '.$entry . "<br />";
                    217:             my ($label, $value) = ($entry =~ /^([^?]*)\?(.*)/);
1.1       onken     218:             my ($domain, $user) = split('&', $value);
1.4       onken     219:             $user =~ s/(.*)\n/$1/; #TODO is that equals to chomp?
1.1       onken     220:             
                    221:             if($user ne $env{'user.name'} or  $domain ne $env{'user.domain'}) {
1.12      bisitz    222:                 return '<p class="LC_error">'
                    223:                       .&mt('Wrong username ([_1]) found in PDF file. Expected username: [_2]'
                    224:                           ,$user.':'.$domain
                    225:                           ,$env{'user.domain'}.':'.$env{'user.name'})
                    226:                       .'</p>';
1.1       onken     227:             }
                    228: 
1.17    ! raeburn   229:         } elsif ($entry =~ /^upload.*/)  {
1.1       onken     230:             $debug .= 'found: a problem -> '.$entry;
1.2       onken     231:             my ($label, $value) = ($entry =~ /^([^?]*)\?(.*)/);
1.1       onken     232:             my ($symb, $part, $type, $HWVAL) = split('&', $label);
1.17    ! raeburn   233:             my ($map,$id,$resource)=&Apache::lonnet::decode_symb($symb);
        !           234:             next unless (&Apache::lonnet::is_on_map($resource));
1.1       onken     235:             $value =~ s/(.*)\n/$1/; 
                    236: 
1.6       bisitz    237:             #filter incorrect radiobuttons (Bug in CABAReT Stage)
1.17    ! raeburn   238:             if ($type eq 'radiobuttonresponse' && $value eq 'Off' ) {
1.1       onken     239:                 next;
                    240:             }
                    241:  
                    242:             my $submit = $part;
                    243:             $submit =~ s/part_(.*)/submit_$1/;
1.17    ! raeburn   244:             if ($problems{$symb.$part}) {
1.1       onken     245:                  $problems{$symb.$part}{$HWVAL} = $value;
                    246:             } else {
                    247:                  $problems{$symb.$part} =  { 'resource' => $resource,
1.11      onken     248:                                         'symb' => $symb,
1.1       onken     249:                                         'submitted' => $part,
                    250:                                         $submit => 'Answer',
                    251:                                         $HWVAL => $value};
                    252:             }
                    253:         } else {
                    254:             $debug .= 'found: -> '.$entry;
                    255:             next;
                    256:         }
                    257:     }
1.4       onken     258:     #$result .= $debug;
1.1       onken     259: 
1.12      bisitz    260:     $result .= '<h2>'.&mt('Results of PDF Form problems').'</h2>';
1.17    ! raeburn   261: 
        !           262:     if (keys(%problems) > 0) {
        !           263:         $result .= &Apache::loncommon::start_data_table()
        !           264:                   .&Apache::loncommon::start_data_table_header_row()
        !           265:                   .'<th>'.&mt('Problem Name').'</th>'
        !           266:                   .'<th>'.&mt('Grading').'</th>'
        !           267:                   .&Apache::loncommon::start_data_table_header_row()
        !           268:                   .&Apache::loncommon::end_data_table_header_row();
        !           269: 
        !           270:         foreach my $key (sort(keys(%problems))) {
        !           271:             my %problem = %{$problems{$key}};
        !           272:             my ($problemname, $grade) = &grade_problem(%problem);
        !           273: 
        !           274:             $result .= &Apache::loncommon::start_data_table_row();
        !           275:             $result .= "<td>$problemname</td><td class='";
        !           276:             if ($grade eq "EXACT_ANS" || $grade eq "APPROX_ANS") {
        !           277:                 $result .= "LC_answer_correct";
        !           278:             } else { 
        !           279:                 $result .= "LC_answer_charged_try";
        !           280:             }
        !           281:             $grade = &parse_grade_answer($grade);
        !           282:             $result .= "'>$grade</span></td>";
        !           283:             $result .= &Apache::loncommon::end_data_table_row();
1.1       onken     284:         }
1.17    ! raeburn   285:         $result .= &Apache::loncommon::end_data_table();
        !           286:     } else {
        !           287:         $result .= '<p class="LC_warning">'.
        !           288:                    &mt('As no gradable form items were found, no submissions have been recorded.').
        !           289:                    '</p>';
1.4       onken     290:     }
1.1       onken     291: 
                    292:     return $result;        
                    293: }
                    294: 
                    295: sub grade_problem {
                    296:     my %problem = @_;
1.7       onken     297:     my ($title, $part) = ();
1.1       onken     298: 
1.7       onken     299:     &Apache::loncommon::ssi_with_retries('/res/'.$problem{'resource'}, 5, %problem);
1.1       onken     300: 
1.7       onken     301:     $title = &Apache::lonnet::gettitle($problem{'symb'});    
                    302:     $part = $problem{submitted};
1.1       onken     303:     $part =~ s/part_(.*)/$1/;
1.7       onken     304:     unless($part eq '0') {
                    305:         #add information about part number
                    306:         $title .= " - Part $part";
                    307:     }
1.1       onken     308:  
                    309:     my %problemhash = &Apache::lonnet::restore($problem{'symb'});
                    310:     my $grade = $problemhash{"resource.$part.award"};
                    311: 
1.7       onken     312:     return ($title, $grade);    
1.1       onken     313: }
                    314: 
1.8       onken     315: sub parse_grade_answer {
                    316:     my ($shortcut) = @_;
                    317:      my %answerhash = ('EXACT_ANS' => &mt('You are correct.'),
1.10      onken     318:                        'APPROX_ANS' => &mt('You are correct.'),
1.8       onken     319:                        'INCORRECT' => &mt('You are incorrect'),
                    320:      );
                    321: 
                    322:     foreach my $key (keys %answerhash) {
                    323:         if($shortcut eq $key) {
                    324:             return $answerhash{$shortcut};
                    325:         }  
                    326:     }
                    327:     return &mt('See course contents for further information.');
                    328: 
                    329: }
                    330: 
                    331: 
1.1       onken     332: sub dumpenv  {
                    333:     my $r = shift;
                    334: 
                    335:     $r->print ("<br />-------------------<br />");
                    336:     foreach my $key (sort (keys %env)) {
                    337:         $r->print ("<br />$key -> $env{$key}");
                    338:     }
                    339:     $r->print ("<br />-------------------<br />");
                    340:     $r->print ("<br />-------------------<br />");
                    341:     foreach my $key (sort (keys %ENV)) {
                    342:         $r->print ("<br />$key -> $ENV{$key}");
                    343:     }
                    344:     $r->print ("<br />-------------------<br />");
                    345:     
                    346: }	
                    347: 
                    348: 1;
                    349: __END__
                    350: 

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>