# The LearningOnline Network with CAPA # input definitons # # $Id: inputtags.pm,v 1.355 2021/09/07 20:37:33 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # # This file is part of the LearningOnline Network with CAPA (LON-CAPA). # # LON-CAPA is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # LON-CAPA is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with LON-CAPA; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # /home/httpd/html/adm/gpl.txt # # http://www.lon-capa.org/ =pod =head1 NAME Apache::inputtags =head1 SYNOPSIS This is part of the LearningOnline Network with CAPA project described at http://www.lon-capa.org. =head1 NOTABLE SUBROUTINES =over =item =back =cut package Apache::inputtags; use HTML::Entities(); use strict; use Apache::loncommon; use Apache::lonhtmlcommon; use Apache::lonlocal; use Apache::lonnet; use LONCAPA; BEGIN { &Apache::lonxml::register('Apache::inputtags',('hiddensubmission','hiddenline','textfield','textline')); } =pod =item initialize_inputtags() Initializes a set of global variables used during the parse of the problem. @Apache::inputtags::input - List of current input ids. @Apache::inputtags::inputlist - List of all input ids seen this problem. @Apache::inputtags::response - List of all current resopnse ids. @Apache::inputtags::responselist - List of all response ids seen this problem. @Apache::inputtags::hint - List of all hint ids. @Apache::inputtags::hintlist - List of all hint ids seen this problem. @Apache::inputtags::previous - List describing if specific responseds have been used @Apache::inputtags::previous_version - Submission responses were used in. $Apache::inputtags::part - Current part id (valid only in ) 0 if not in a part. @Apache::inputtags::partlist - List of part ids seen in the current @Apache::inputtags::status - List of problem statuses. First element is the status of the the remainder are for individual s. %Apache::inputtags::params - Hash of defined parameters for the current response. @Apache::inputtags::import - List of all ids for thes get join()ed and prepended. @Apache::inputtags::importlist - List of all import ids seen. $Apache::inputtags::response_with_no_part - Flag set true if we have seen a response that is not inside a %Apache::inputtags::answertxt - <*response> tags store correct answer strings for display by in this hash. %Apache::inputtags::submission_display - <*response> tags store improved display of submission strings for display by part end. =cut sub initialize_inputtags { @Apache::inputtags::input=(); @Apache::inputtags::inputlist=(); @Apache::inputtags::response=(); @Apache::inputtags::responselist=(); @Apache::inputtags::hint=(); @Apache::inputtags::hintlist=(); @Apache::inputtags::previous=(); @Apache::inputtags::previous_version=(); $Apache::inputtags::part=''; @Apache::inputtags::partlist=(); @Apache::inputtags::status=(); %Apache::inputtags::params=(); @Apache::inputtags::import=(); @Apache::inputtags::importlist=(); $Apache::inputtags::response_with_no_part=0; %Apache::inputtags::answertxt=(); %Apache::inputtags::submission_display=(); } # # provides the onblur binding for spellchecking. This could be an # empty string if spellchecking was not enabled. # Jquery selector binding is done rather than setting an onblur # attribute because we'll need to set the element's spellcheck language # option dynamically so we need $(this) to be defined. # # @param id - The element id to bind. # @param lang - Language in which spellchecking is desired. # if undef, nothing is generated. # @return string - onblur specification to do the requested spellchecking. # sub spellcheck_onblur { my ($id, $lang) = @_; my $result = ''; if ($lang) { $result = < \$('\#$id').blur(function() { doSpellcheck('\#$id', '$lang'); }); JAVASCRIPT } return $result; } sub check_for_duplicate_ids { my %check; foreach my $id (@Apache::inputtags::partlist, @Apache::inputtags::responselist, @Apache::inputtags::hintlist, @Apache::inputtags::importlist) { $check{$id}++; } my @duplicates; foreach my $id (sort(keys(%check))) { if ($check{$id} > 1) { push(@duplicates,$id); } } if (@duplicates) { &Apache::lonxml::error("Duplicated ids found, problem will operate incorrectly. Duplicated ids seen: ",join(', ',@duplicates)); } } sub start_input { my ($parstack,$safeeval)=@_; my $id = &Apache::lonxml::get_id($parstack,$safeeval); push (@Apache::inputtags::input,$id); push (@Apache::inputtags::inputlist,$id); return $id; } sub end_input { pop @Apache::inputtags::input; return ''; } sub addchars { my ($fieldid,$addchars)=@_; my $output=''; foreach (split(/\,/,$addchars)) { $output.=''.$_.' '; } return $output; } sub start_textfield { my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; my $result = ""; my $id = &start_input($parstack,$safeeval); my $resid=$Apache::inputtags::response[-1]; if ($target eq 'web') { $Apache::lonxml::evaluate--; my $partid=$Apache::inputtags::part; my ($oldresponse,$newvariation); if ((($Apache::lonhomework::history{"resource.$partid.type"} eq 'randomizetry') || ($Apache::lonhomework::type eq 'randomizetry')) && ($Apache::inputtags::status[-1] eq 'CAN_ANSWER')) { if ($env{'form.'.$partid.'.rndseed'} ne $Apache::lonhomework::history{"resource.$partid.rndseed"}) { $newvariation = 1; } } unless ($newvariation) { if ((($env{'form.grade_username'} eq '') && ($env{'form.grade_domain'} eq '')) || (($env{'form.grade_username'} eq $env{'user.name'}) && ($env{'form.grade_domain'} eq $env{'user.domain'}))) { $oldresponse = $Apache::lonhomework::history{"resource.$partid.$resid.submission"}; } elsif (($Apache::lonhomework::history{"resource.$partid.type"} eq 'anonsurvey') || ($Apache::lonhomework::history{"resource.$partid.type"} eq 'anonsurveycred')) { $oldresponse = '* '.&mt('(only shown to submitter)').' *'; } else { $oldresponse = $Apache::lonhomework::history{"resource.$partid.$resid.submission"}; } } if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') { my $cols = &Apache::lonxml::get_param('cols',$parstack,$safeeval); if ( $cols eq '') { $cols = 80; } my $rows = &Apache::lonxml::get_param('rows',$parstack,$safeeval); if ( $rows eq '') { $rows = 16; } my $addchars=&Apache::lonxml::get_param('addchars',$parstack,$safeeval); $result=''; my $tagident = 'HWVAL_' . $resid; my $itemid = 'HWVAL_'.$partid.'_'.$resid; if ($addchars) { $result.=&addchars($tagident, $addchars); } my $textareaclass; unless (&Apache::londefdef::is_inside_of($tagstack, 'externalresponse')) { $textareaclass = 'class="LC_richDetectHtml spellchecked"'; } $result.= '"; $result .= &spellcheck_onblur($itemid, $spellcheck); return $result; } } elsif ($target eq 'edit') { $result=&Apache::edit::end_table(); } &end_input; return $result; } sub exam_score_line { my ($target) = @_; my $result; if ($target eq 'tex') { my $repetition = &Apache::response::repetition(); $result.='\begin{enumerate}'; if ($env{'request.state'} eq "construct" ) {$result.='\item[\strut]';} foreach my $i (0..$repetition-1) { $result.='\item[\textbf{'. ($Apache::lonxml::counter+$i). '}.]\textit{Leave blank on scoring form}\vskip 0 mm'; } $result.= '\end{enumerate}'; } return $result; } sub exam_box { my ($target) = @_; my $result; if ($target eq 'tex') { $result .= '\fbox{\fbox{\parbox{\textwidth-5mm}{\strut\\\\\strut\\\\\strut\\\\\strut\\\\}}}'; $result .= &exam_score_line($target); } elsif ($target eq 'web') { my $id=$Apache::inputtags::response[-1]; $result.= '



'; } return $result; } sub needs_exam_box { my ($tagstack) = @_; my @tags = ('formularesponse', 'stringresponse', 'reactionresponse', 'organicresponse', ); foreach my $tag (@tags) { if (grep(/\Q$tag\E/,@$tagstack)) { return 1; } } return 0; } sub start_textline { my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_; my $result = ""; my $input_id = &start_input($parstack,$safeeval); # The spellcheck attribute # 1. enables spellchecking. # 2. Provides the language code in which the spellchecking will be performed. my $spellcheck = &Apache::lonxml::get_param('spellcheck', $parstack, $safeeval); if ($target eq 'web') { $Apache::lonxml::evaluate--; my $partid=$Apache::inputtags::part; my $id=$Apache::inputtags::response[-1]; if (!&Apache::response::show_answer()) { my $size = &Apache::lonxml::get_param('size',$parstack,$safeeval); my $maxlength; if ($size eq '') { $size=20; } else { if ($size < 20) { $maxlength = ' maxlength="'.$size.'"'; } } my ($oldresponse,$newvariation); if ((($Apache::lonhomework::history{"resource.$partid.type"} eq 'randomizetry') || ($Apache::lonhomework::type eq 'randomizetry')) && ($Apache::inputtags::status[-1] eq 'CAN_ANSWER')) { if ($env{'form.'.$partid.'.rndseed'} ne $Apache::lonhomework::history{"resource.$partid.rndseed"}) { $newvariation = 1; } } unless ($newvariation) { if ((($env{'form.grade_username'} eq '') && ($env{'form.grade_domain'} eq '')) || (($env{'form.grade_username'} eq $env{'user.name'}) && ($env{'form.grade_domain'} eq $env{'user.domain'}))) { $oldresponse = $Apache::lonhomework::history{"resource.$partid.$id.submission"}; } elsif (($Apache::lonhomework::history{"resource.$partid.type"} eq 'anonsurvey') || ($Apache::lonhomework::history{"resource.$partid.type"} eq 'anonsurveycred') || ($Apache::lonhomework::type eq 'anonsurvey') || ($Apache::lonhomework::type eq 'anonsurveycred')) { $oldresponse = '* '.&mt('(only shown to submitter)').' *'; } else { $oldresponse = $Apache::lonhomework::history{"resource.$partid.$id.submission"}; } &Apache::lonxml::debug("oldresponse $oldresponse is ".ref($oldresponse)); if (ref($oldresponse) eq 'ARRAY') { $oldresponse = $oldresponse->[$#Apache::inputtags::inputlist]; } $oldresponse = &HTML::Entities::encode($oldresponse,'<>&"'); $oldresponse =~ s/^\s+//; $oldresponse =~ s/\s+$//; $oldresponse =~ s/\s+/ /g; } if ($Apache::lonhomework::type ne 'exam') { my $addchars=&Apache::lonxml::get_param('addchars',$parstack,$safeeval); $result=''; if ($addchars) { $result.=&addchars('HWVAL_'.$id,$addchars); } my $readonly=&Apache::lonxml::get_param('readonly',$parstack, $safeeval); if (lc($readonly) eq 'yes' || $Apache::inputtags::status[-1] eq 'CANNOT_ANSWER') { $readonly=' readonly="readonly" '; } else { $readonly=''; } my $name = 'HWVAL_'.$id; my $itemid = 'HWVAL_'.$partid.'_'.$id; # NOTE: the input id should match the one given by defaut_homework input_id(). my $input_tag_id = $itemid.'_'.$input_id; if ($Apache::inputtags::status[-1] eq 'CANNOT_ANSWER') { $name = "none"; } $result.= ''; $result .= &spellcheck_onblur($itemid, $spellcheck); if (($Apache::inputtags::status['-1'] eq 'CAN_ANSWER') && (($tagstack->[-2] eq 'formularesponse') || ($tagstack->[-2] eq 'mathresponse')) && (&Apache::lonnet::EXT('resource.'.$partid.'_'.$id.'.turnoffeditor') ne 'yes')) { $result.=&edit_mathresponse_button($input_tag_id); } } if ($Apache::lonhomework::type eq 'exam' && &needs_exam_box($tagstack)) { $result.=&exam_box($target); } } else { #right or wrong don't show what was last typed in. my $count = scalar(@Apache::inputtags::inputlist)-1; $result=''.$Apache::inputtags::answertxt{$id}[$count].''; #$result=''; } } elsif ($target eq 'edit') { $result=&Apache::edit::tag_start($target,$token); $result.=&Apache::edit::text_arg('Size:','size',$token,'5'). &Apache::edit::text_arg('Click-On Texts (comma sep):', 'addchars',$token,10); $result.=&Apache::edit::select_arg('Readonly:','readonly', ['no','yes'],$token); my $spell_langs = &spelling_languages(); $result.=&Apache::edit::select_arg('Spellcheck for:', 'spellcheck', $spell_langs, $token); $result.=&Apache::edit::end_row(); $result.=&Apache::edit::end_table(); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args($token,$parstack, $safeeval,'size', 'addchars','readonly', 'spellcheck'); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); } } elsif ($target eq 'tex' && $Apache::lonhomework::type ne 'exam') { my $size = &Apache::lonxml::get_param('size',$parstack,$safeeval); if ($size != 0) {$size=$size*2; $size.=' mm';} else {$size='40 mm';} if ($env{'form.pdfFormFields'} eq 'yes' && $Apache::inputtags::status[-1] eq 'CAN_ANSWER') { my $fieldname = $env{'request.symb'}. '&part_'. $Apache::inputtags::part. '&textresponse'. '&HWVAL_' . $Apache::inputtags::response['-1']; $result='\textField{'.$fieldname.'}{'.$size.'}{12 bp}'; } else { $result='\framebox['.$size.'][s]{\tiny\strut}'; } } elsif ($target eq 'tex' && $Apache::lonhomework::type eq 'exam' && &needs_exam_box($tagstack)) { $result.=&exam_box($target); } return $result; } sub end_textline { my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_; if ($target eq 'web') { $Apache::lonxml::evaluate++; } elsif ($target eq 'edit') { return ('','no'); } &end_input(); return ""; } sub start_hiddenline { my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_; my $result = ""; my $input_id = &start_input($parstack,$safeeval); if ($target eq 'web') { $Apache::lonxml::evaluate--; if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') { my $partid=$Apache::inputtags::part; my $id=$Apache::inputtags::response[-1]; my $oldresponse = $Apache::lonhomework::history{"resource.$partid.$id.submission"}; if (ref($oldresponse) eq 'ARRAY') { $oldresponse = $oldresponse->[$#Apache::inputtags::inputlist]; } $oldresponse = &HTML::Entities::encode($oldresponse,'<>&"'); if ($Apache::lonhomework::type ne 'exam') { $result= ''; } } } elsif ($target eq 'edit') { $result=&Apache::edit::tag_start($target,$token); $result.=&Apache::edit::end_table; } if ( ($target eq 'web' || $target eq 'tex') && $Apache::lonhomework::type eq 'exam' && &needs_exam_box($tagstack)) { $result.=&exam_box($target); } return $result; } sub end_hiddenline { my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_; if ($target eq 'web') { $Apache::lonxml::evaluate++; } elsif ($target eq 'edit') { return ('','no'); } &end_input(); return ""; } sub start_hiddensubmission { my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_; my $result = ""; my $input_id = &start_input($parstack,$safeeval); if ($target eq 'web') { $Apache::lonxml::evaluate--; if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') { my $partid=$Apache::inputtags::part; my $id=$Apache::inputtags::response[-1]; if ($Apache::lonhomework::type ne 'exam') { my $value = &Apache::lonxml::get_param('value',$parstack,$safeeval); $value = &HTML::Entities::encode($value,'<>&"'); $result= ''; } } } elsif ($target eq 'edit') { $result=&Apache::edit::tag_start($target,$token); $result.=&Apache::edit::text_arg('Value:','value',$token,'15'); $result.=&Apache::edit::end_row(); $result.=&Apache::edit::end_table(); } elsif ($target eq 'modified') { my $constructtag=&Apache::edit::get_new_args($token,$parstack, $safeeval,'value'); if ($constructtag) { $result = &Apache::edit::rebuild_tag($token); } } if ( ($target eq 'web' || $target eq 'tex') && $Apache::lonhomework::type eq 'exam' && &needs_exam_box($tagstack)) { $result.=&exam_box($target); } return $result; } sub end_hiddensubmission { my ($target,$token,$tagstack,$parstack,$parser,$safeeval)=@_; if ($target eq 'web') { $Apache::lonxml::evaluate++; } elsif ($target eq 'edit') { return ('','no'); } &end_input(); return ""; } =pod =item file_selector() $part -> partid $id -> responseid $uploadefiletypes -> comma seperated list of extensions allowed or * for any $which -> 'uploadonly' -> only newly uploaded files 'portfolioonly' -> only allow files from portfolio 'both' -> allow files from either location $extratext -> additional text to go between the link and the input box $maxfilesize -> maximum cumulative filesize for submitted files (in MB). returns a table row =cut sub file_selector { my ($part,$id,$uploadedfiletypes,$which,$extratext,$maxfilesize)=@_; if (!$uploadedfiletypes) { return ''; } my $jspart=$part; $jspart=~s/\./_/g; my $result; my $current_files_display = ¤t_file_submissions($part,$id); my $addfiles; if ($current_files_display) { $result .= &Apache::lonhtmlcommon::row_title(&mt('Files currently selected for submission')). $current_files_display. &Apache::lonhtmlcommon::row_closure(); $addfiles = &mt('Submit other file(s)'); } else { $addfiles = &mt('Choose file(s) to submit'); } $result .= &Apache::lonhtmlcommon::row_title($addfiles); my $constraints; if ($uploadedfiletypes ne '*') { $constraints = &mt('Allowed filetypes: [_1]',''.$uploadedfiletypes.'').'
'; } if ($maxfilesize) { $constraints .= &mt('Combined size of all files not to exceed: [_1] MB.', ''.$maxfilesize.'').'
'; } if ($constraints) { $result .= $constraints.'
'; } if ($which eq 'uploadonly' || $which eq 'both') { my $free_space = $maxfilesize * 1048576; $result .= &mt('Submit a file: (only one file per submission)'). '
'. '
'; } if ( $which eq 'both') { $result.='
'.''.&mt('OR:').'
'; } if ($which eq 'portfolioonly' || $which eq 'both') { my $symb = $env{'request.symb'}; (undef,undef,my $res)=&Apache::lonnet::decode_symb($symb); my $showsymb; # If resource is a .task and URL is unencrypted, include symb in query string # for url opened in portfolio file selection window. Can be used to override # blocking of portfolio access resulting from an exam event in a different course. if ($res =~ /\.task$/i) { my $encsymb = &Apache::lonenc::check_encrypt($symb); if ($symb eq $encsymb) { $showsymb = $symb; } } $result.=$extratext.''. &mt('Select Portfolio Files: (one or more files per submission)').'
'. ''. '
'; } $result.=&Apache::lonhtmlcommon::row_closure(1); return $result; } sub current_file_submissions { my ($part,$id) = @_; my $jspart=$part; $jspart=~s/\./_/g; my $uploadedfile=$Apache::lonhomework::history{"resource.$part.$id.uploadedfile"}; my $portfiles=$Apache::lonhomework::history{"resource.$part.$id.portfiles"}; return if (($uploadedfile eq '') && ($portfiles !~/[^\s]/)); my @unversioned; foreach my $file (split(/\s*,\s*/,&unescape($portfiles))) { my ($path,$name) = ($file =~ m{^(.*/)([^/]+)$}); my ($origname,$version,$ext) = &Apache::lonnet::file_name_version_ext($name); unless ($version) { push(@unversioned,$file); } } return if (!@unversioned); my $header = &portpath_popup_js(). &Apache::loncommon::start_data_table(). &Apache::loncommon::start_data_table_header_row(); if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') { $header .= ''.&mt('Delete?').''; } $header .= ''.&mt('File').''. ''.&mt('Size (MB)').''. ''.&mt('Last Modified').''. &Apache::loncommon::end_data_table_header_row(); my ($symb,$crsid,$udom,$uname)=&Apache::lonnet::whichuser(); my ($cdom,$cnum) = ($crsid =~ /^($LONCAPA::match_domain)_($LONCAPA::match_courseid)$/); my ($result,$header_shown,%okfiles,%rows,%legacy,@bad_file_list); if ($uploadedfile) { my $url=$Apache::lonhomework::history{"resource.$part.$id.uploadedurl"}; my $link = &HTML::Entities::encode($url,'<>&"'); my ($path,$name) = ($url =~ m{^(/uploaded/\Q$udom\E/\Q$uname\E/essayresponse.*/)([^/]+)$}); my ($status,$hashref,$error) = ¤t_file_info($url,$link,$name,$path); if ($status eq 'ok') { push(@{$okfiles{$name}},$url); $rows{$url} = $hashref; $legacy{$url} = 1; &Apache::lonxml::extlink($url); &Apache::lonnet::allowuploaded('/adm/essayresponse',$url); } else { push(@bad_file_list,$error); } } if (@unversioned > 0) { my $prefix = "/uploaded/$udom/$uname/portfolio"; foreach my $file (@unversioned) { my ($path,$name) = ($file =~ m{^(.*/)([^/]+)$}); my $url = $prefix.$path.$name; my $uploadedfile = &HTML::Entities::encode($url,'<>&"'); my ($status,$hashref,$error) = ¤t_file_info($url,$uploadedfile,$name,$path); if ($status eq 'ok') { push(@{$okfiles{$name}},$url); $rows{$url} = $hashref; } else { push(@bad_file_list,$error); } } } my $num = 0; foreach my $name (sort(keys(%okfiles))) { if (ref($okfiles{$name}) eq 'ARRAY') { foreach my $url (@{$okfiles{$name}}) { if (ref($rows{$url}) eq 'HASH') { my $link = $rows{$url}{link}; my $portfile = $rows{$url}{path}.$rows{$url}{name}; $portfile = &HTML::Entities::encode($portfile,'<>&"'); if ($link) { my $icon=&Apache::loncommon::icon($url); unless ($header_shown) { $result .= $header; $header_shown = 1; } $result.= &Apache::loncommon::start_data_table_row()."\n"; if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') { $result .= ''."\n"; $num ++; } my $pathid = 'HWFILE'.$jspart.'_'.$id.'_'.$num.'_path'; my $pathidtext = $pathid.'text'; my ($showname,$showpath); if ($legacy{$url}) { $showname = $name.' '.&mt('not in portfolio'); } else { $showname = $name; $showpath = '
'. ''. ''. &mt('(Show path)').''. '
'.$rows{$url}{path}.$name. '
'; } $result .= ''.$showname.''.$showpath.''."\n". ''.$rows{$url}{size}.''."\n". ''.$rows{$url}{lastmodified}.''."\n". &Apache::loncommon::end_data_table_row(); } } } } } if ($header_shown) { $result .= &Apache::loncommon::end_data_table(); if ($Apache::inputtags::status[-1] eq 'CAN_ANSWER') { $result .= '
'. &mt('Exclude existing file(s) from grading by checking the "Delete?" checkbox(es) and clicking "Submit Answer"').''; } } if (@bad_file_list) { my $bad_files = ''. join(', ',@bad_file_list). ''; $result.='

'. &mt("These file(s) don't exist: [_1]",$bad_files). '

'; } return $result; } sub current_file_info { my ($url,$uploadedfile,$name,$path) = @_; my ($status,$error,%info); my @stat = &Apache::lonnet::stat_file($url); if ((@stat) && ($stat[0] ne 'no_such_dir')) { my ($lastmod,$size); if ($stat[9] =~ /^\d+$/) { $lastmod = &Apache::lonlocal::locallocaltime($stat[9]); } $size = $stat[7]/(1024*1024); $size = sprintf("%.3f",$size); %info = ( link => $uploadedfile, name => $name, path => $path, size => $size, lastmodified => $lastmod, ); $status = 'ok'; } else { &Apache::lonnet::logthis("bad file is $url"); my $icon=&Apache::loncommon::icon($url); $error = ''.$uploadedfile.''; } return ($status,\%info,$error); } sub portpath_popup_js { my %lt = &Apache::lonlocal::texthash( show => '(Show path)', hide => '(Hide)', ); return <<"END"; END } sub valid_award { my ($award) =@_; foreach my $possibleaward ('EXTRA_ANSWER','MISSING_ANSWER', 'ERROR', 'NO_RESPONSE','WRONG_NUMBOXESCHECKED', 'TOO_LONG', 'UNIT_INVALID_INSTRUCTOR', 'UNIT_INVALID_STUDENT', 'UNIT_IRRECONCIBLE', 'UNIT_FAIL', 'NO_UNIT', 'UNIT_NOTNEEDED', 'WANTED_NUMERIC', 'BAD_FORMULA', 'NOT_FUNCTION', 'WRONG_FORMAT', 'INTERNAL_ERROR', 'SIG_FAIL', 'INCORRECT', 'MISORDERED_RANK', 'INVALID_FILETYPE', 'EXCESS_FILESIZE', 'FILENAME_INUSE', 'DRAFT', 'SUBMITTED', 'SUBMITTED_CREDIT', 'ANONYMOUS', 'ANONYMOUS_CREDIT', 'ASSIGNED_SCORE', 'APPROX_ANS', 'EXACT_ANS','COMMA_FAIL') { if ($award eq $possibleaward) { return 1; } } return 0; } { my @awards = ('EXTRA_ANSWER', 'MISSING_ANSWER', 'ERROR', 'NO_RESPONSE', 'WRONG_NUMBOXESCHECKED','TOO_LONG', 'UNIT_INVALID_INSTRUCTOR', 'UNIT_INVALID_STUDENT', 'UNIT_IRRECONCIBLE', 'UNIT_FAIL', 'NO_UNIT', 'UNIT_NOTNEEDED', 'WANTED_NUMERIC', 'BAD_FORMULA', 'NOT_FUNCTION', 'WRONG_FORMAT', 'INTERNAL_ERROR', 'COMMA_FAIL', 'SIG_FAIL', 'INCORRECT', 'MISORDERED_RANK', 'INVALID_FILETYPE', 'EXCESS_FILESIZE', 'FILENAME_INUSE', 'DRAFT', 'SUBMITTED', 'SUBMITTED_CREDIT', 'ANONYMOUS', 'ANONYMOUS_CREDIT', 'ASSIGNED_SCORE', 'APPROX_ANS', 'EXACT_ANS'); my $i=0; my %fwd_awards = map { ($_,$i++) } @awards; my $max=scalar(@awards); @awards=reverse(@awards); $i=0; my %rev_awards = map { ($_,$i++) } @awards; sub awarddetail_to_awarded { my ($awarddetail) = @_; if ($awarddetail eq 'EXACT_ANS' || $awarddetail eq 'APPROX_ANS') { return 1; } return 0; } sub hide_award { my ($award) = @_; if (&Apache::lonhomework::show_no_problem_status()) { return 1; } if ($award =~ /^(?:EXACT_ANS|APPROX_ANS|SUBMITTED|SUBMITTED_CREDIT|ANONYMOUS|ANONYMOUS_CREDIT|ASSIGNED_SCORE|INCORRECT)/) { return 1; } return 0; } sub finalizeawards { my ($awardref,$msgref,$nameref,$reverse,$final_scantron)=@_; my $result; if ($#$awardref == -1) { $result = "NO_RESPONSE"; } if ($result eq '' ) { my $blankcount; foreach my $award (@$awardref) { if ($award eq '') { $result='MISSING_ANSWER'; $blankcount++; } } if ($blankcount == ($#$awardref + 1)) { return ('NO_RESPONSE'); } } if ($Apache::lonxml::internal_error) { $result='INTERNAL_ERROR'; } if (!$final_scantron && defined($result)) { return ($result); } # if in scantron mode, if the award for any response is # assigned score, then the part gets an assigned score if ($final_scantron && grep {$_ eq 'ASSIGNED_SCORE'} (@$awardref)) { return ('ASSIGNED_SCORE'); } # if in scantron mode, if the award for any response is # correct and there are non-correct responses, # then the part gets an assigned score if ($final_scantron && (grep { $_ eq 'EXACT_ANS' || $_ eq 'APPROX_ANS' } (@$awardref)) && (grep { $_ ne 'EXACT_ANS' && $_ ne 'APPROX_ANS' } (@$awardref))) { return ('ASSIGNED_SCORE'); } # these awards are ordered from most important error through best correct my $awards = (!$reverse) ? \%fwd_awards : \%rev_awards ; my $best = $max; my $j=0; my $which; foreach my $award (@$awardref) { if ($awards->{$award} < $best) { $best = $awards->{$award}; $which = $j; } $j++; } # if at least one response item is set to include lenient grading # and that item is partially correct then overall award reflects # that, unless an award for one of the other response items does # not fall within the basic awards for correct or incorrect. if ($Apache::inputtags::leniency) { if (($$awardref[$which] eq 'INCORRECT') && (grep { $_ eq 'EXACT_ANS' || $_ eq 'APPROX_ANS' || $_ eq 'ASSIGNED_SCORE' } (@$awardref)) && !((grep { $_ ne 'INCORRECT' && $_ ne 'EXACT_ANS' && $_ ne 'APPROX_ANS' && $_ ne 'ASSIGNED_SCORE' } (@$awardref)))) { return ('ASSIGNED_SCORE'); } } if (defined($which)) { if (ref($nameref)) { return ($$awardref[$which],$$msgref[$which],$$nameref[$which]); } else { return ($$awardref[$which],$$msgref[$which]); } } return ('ERROR',undef); } } sub decideoutput { my ($award,$awarded,$awardmsg,$solved,$previous,$target,$nocorrect,$tdclass)=@_; my $message=''; my $button=0; my $previousmsg; my $css_class='orange'; my $added_computer_text=0; my %possible_class = ( 'correct' => 'LC_answer_correct', 'charged_try' => 'LC_answer_charged_try', 'not_charged_try' => 'LC_answer_not_charged_try', 'no_grade' => 'LC_answer_no_grade', 'no_message' => 'LC_no_message', 'no_charge_warn' => 'LC_answer_warning', ); my $part = $Apache::inputtags::part; my $tohandgrade = &Apache::lonnet::EXT("resource.$part.handgrade"); my $handgrade = ('yes' eq lc($tohandgrade)); # # Should "Computer's Answer" be displayed? # Should not be displayed if still answerable, # if the problem is handgraded, # or if the problem does not give a correct answer # my $computer = ($handgrade || $nocorrect)? '' : &mt("Computer's answer now shown above."); &Apache::lonxml::debug("handgrade has :$handgrade:"); if ($previous) { $previousmsg=&mt('You have entered that answer before'); } if ($solved =~ /^correct/) { $css_class=$possible_class{'correct'}; $message=&mt('You are correct.'); if ($awarded < 1 && $awarded > 0) { $message=&mt('You are partially correct.'); $css_class=$possible_class{'not_charged_try'}; } elsif ($awarded < 1) { $message=&mt('Incorrect.'); $css_class=$possible_class{'charged_try'}; } if ($handgrade || ($env{'request.filename'}=~/\/res\/lib\/templates\/(examupload|DropBox).problem$/)) { $message = &mt("A score has been assigned."); $added_computer_text=1; } else { if ($target eq 'tex') { $message = '\textbf{'.$message.'}'; } else { $message = "".$message.""; if ($computer) { $message = "$computer $message"; } } $added_computer_text=1; if ($awarded > 0) { my ($symb) = &Apache::lonnet::whichuser(); if (($symb ne '') && ($env{'course.'.$env{'request.course.id'}. '.disable_receipt_display'} ne 'yes') && ($Apache::lonhomework::type ne 'practice')) { $message.=(($target eq 'web')?'
':' '). &mt('Your receipt no. is [_1]', (&Apache::lonnet::receipt($Apache::inputtags::part). (($target eq 'web')?&Apache::loncommon::help_open_topic('Receipt'):''))); } } } if ($awarded >= 1) { $button=0; } elsif (&Apache::lonnet::EXT("resource.$part.retrypartial") !~/^1|on|yes$/i) { $button=0; } else { $button=1; } $previousmsg=''; } elsif ($solved =~ /^excused/) { if ($target eq 'tex') { $message = ' \textbf{'.&mt('You are excused from the problem.').'} '; } else { $message = "".&mt('You are excused from the problem.').""; } $css_class=$possible_class{'charged_try'}; $button=0; $previousmsg=''; } elsif ($award eq 'EXACT_ANS' || $award eq 'APPROX_ANS' ) { if ($solved =~ /^incorrect/ || $solved eq '') { $message = &mt("Incorrect")."."; $css_class=$possible_class{'charged_try'}; $button=1; } else { if ($target eq 'tex') { $message = '\textbf{'.&mt('You are correct.').'}'; } else { $message = "".&mt('You are correct.').""; if ($computer) { $message = "$computer $message"; } } $added_computer_text=1; if ($awarded > 0 && $env{'course.'. $env{'request.course.id'}. '.disable_receipt_display'} ne 'yes') { $message.=(($target eq 'web')?'
':' '). &mt('Your receipt is [_1]', (&Apache::lonnet::receipt($Apache::inputtags::part). (($target eq 'web')?&Apache::loncommon::help_open_topic('Receipt'):''))); } $css_class=$possible_class{'correct'}; $button=0; $previousmsg=''; } } elsif ($award eq 'NO_RESPONSE') { $message = ''; $css_class=$possible_class{'no_feedback'}; $button=1; } elsif ($award eq 'EXTRA_ANSWER') { $message = &mt('Some extra items were submitted.'); $css_class=$possible_class{'not_charged_try'}; $button = 1; } elsif ($award eq 'MISSING_ANSWER') { $message = &mt('Some items were not submitted.'); if ($target ne 'tex') { $message .= &Apache::loncommon::help_open_topic('Some_Items_Were_Not_Submitted'); } if (&Apache::lonhomework::show_some_problem_status()) { $css_class=$possible_class{'no_charge_warn'}; } else { $css_class=$possible_class{'not_charged_try'}; } $button = 1; } elsif ($award eq 'WRONG_NUMBOXESCHECKED') { $message = &mt('Number of boxes checked outside permissible range (either too few or too many).'); if ($target ne 'tex') { $message .= &Apache::loncommon::help_open_topic('Wrong_Num_Boxes_Checked'); } $css_class=$possible_class{'not_charged_try'}; $button = 1; } elsif ($award eq 'ERROR') { $message = &mt('An error occurred while grading your answer.'); $css_class=$possible_class{'not_charged_try'}; $button = 1; } elsif ($award eq 'TOO_LONG') { $message = &mt("The submitted answer was too long."); $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'WANTED_NUMERIC') { $message = &mt("This question expects a numeric answer."); $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'MISORDERED_RANK') { $message = &mt('You have provided an invalid ranking.'); if ($target ne 'tex') { $message.=' '.&mt('Please refer to [_1]',&Apache::loncommon::help_open_topic('Ranking_Problems',&mt('help on ranking problems'))); } $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'EXCESS_FILESIZE') { $message = &mt("Submission won't be graded. The combined size of submitted files exceeded the amount allowed."); $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'FILENAME_INUSE') { $message = &mt('You have already uploaded a file with that filename.'); if ($target eq 'tex') { $message.= "\\\\\n"; } else { $message .= '
'; } $message .= &mt('Please use a different filename.'); $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'INVALID_FILETYPE') { $message = &mt("Submission won't be graded. The type of file submitted is not allowed."); $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'SIG_FAIL') { my ($used,$min,$max)=split(':',$awardmsg); my $word = ($used < $min) ? 'more' : 'fewer'; $message = &mt("Submission not graded. Use $word significant figures."); if (&Apache::lonhomework::show_some_problem_status()) { $css_class=$possible_class{'no_charge_warn'}; } else { $css_class=$possible_class{'not_charged_try'}; } $button=1; } elsif ($award eq 'UNIT_INVALID_INSTRUCTOR') { $message = &mt('Error in instructor specifed unit. This error has been reported to the instructor.', $awardmsg); if ($target ne 'tex') {$message.=&Apache::loncommon::help_open_topic('Physical_Units');} $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'UNIT_INVALID_STUDENT') { $message = &mt('Unable to interpret units. Computer reads units as "[_1]".',&markup_unit($awardmsg,$target)); if ($target ne 'tex') {$message.=&Apache::loncommon::help_open_topic('Physical_Units');} if (&Apache::lonhomework::show_some_problem_status()) { $css_class=$possible_class{'no_charge_warn'}; } else { $css_class=$possible_class{'not_charged_try'}; } $button=1; } elsif ($award eq 'UNIT_FAIL' || $award eq 'UNIT_IRRECONCIBLE') { $message = &mt('Incompatible units. No conversion found between "[_1]" and the required units.',&markup_unit($awardmsg,$target)); if ($target ne 'tex') {$message.=&Apache::loncommon::help_open_topic('Physical_Units');} if (&Apache::lonhomework::show_some_problem_status()) { $css_class=$possible_class{'no_charge_warn'}; } else { $css_class=$possible_class{'not_charged_try'}; } $button=1; } elsif ($award eq 'UNIT_NOTNEEDED') { $message = &mt('Only a number required. Computer reads units of "[_1]".',&markup_unit($awardmsg,$target)); if (&Apache::lonhomework::show_some_problem_status()) { $css_class=$possible_class{'no_charge_warn'}; } else { $css_class=$possible_class{'not_charged_try'}; } $button=1; } elsif ($award eq 'NO_UNIT') { $message = &mt("Units required").'.'; if ($target ne 'tex') {$message.=&Apache::loncommon::help_open_topic('Physical_Units')}; if (&Apache::lonhomework::show_some_problem_status()) { $css_class=$possible_class{'no_charge_warn'}; } else { $css_class=$possible_class{'not_charged_try'}; } $button=1; } elsif ($award eq 'COMMA_FAIL') { $message = &mt("Proper comma separation is required").'.'; $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'BAD_FORMULA') { $message = &mt("Unable to understand formula").'.'; if ($target ne 'tex') {$message.=&Apache::loncommon::help_open_topic('Formula_Answers')}; $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'NOT_FUNCTION') { $message = &mt("Not a function").'.'; $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'WRONG_FORMAT') { $message = &mt("Wrong format").'.'; $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'INTERNAL_ERROR') { $message = &mt("An internal error occurred while processing your answer. Please try again later."); $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'INCORRECT') { $message = &mt("Incorrect").'.'; $css_class=$possible_class{'charged_try'}; $button=1; } elsif ($award eq 'SUBMITTED') { $message = &mt("Your submission has been recorded."); $css_class=$possible_class{'no_grade'}; $button=1; } elsif ($award eq 'SUBMITTED_CREDIT') { $message = &mt("Your submission has been recorded, and credit awarded."); $css_class=$possible_class{'correct'}; $button=1; } elsif ($award eq 'ANONYMOUS') { $message = &mt("Your anonymous submission has been recorded."); $css_class=$possible_class{'no_grade'}; $button=1; } elsif ($award eq 'ANONYMOUS_CREDIT') { $message = &mt("Your anonymous submission has been recorded, and credit awarded."); $css_class=$possible_class{'correct'}; $button=1; } elsif ($award eq 'DRAFT') { $message = &mt("Copy saved but not submitted."); $css_class=$possible_class{'not_charged_try'}; $button=1; } elsif ($award eq 'ASSIGNED_SCORE') { $message = &mt("A score has been assigned."); $css_class=$possible_class{'correct'}; $button=0; } elsif ($award eq '') { if ($handgrade && $Apache::inputtags::status[-1] eq 'SHOW_ANSWER') { $message = &mt("Nothing submitted."); $css_class=$possible_class{'charged_try'}; } else { $css_class=$possible_class{'not_charged_try'}; } $button=1; } else { $message = &mt("Unknown message").": $award"; $button=1; } my (undef,undef,$domain,$user)=&Apache::lonnet::whichuser(); foreach my $resid(@Apache::inputtags::response){ if ($Apache::lonhomework::history{"resource.$part.$resid.handback"}) { if ($target eq 'tex') { $message.= "\\\\\n"; } else { $message.='
'; } my @files = split(/\s*,\s*/, $Apache::lonhomework::history{"resource.$part.$resid.handback"}); my $file_msg; foreach my $file (@files) { if ($target eq 'tex') { $file_msg.= "\\\\\n".$file; } else { $file_msg.= '
'.$file.''; } } $message .= &mt('Returned file(s): [_1]',$file_msg); if ($target eq 'tex') { $message.= "\\\\\n"; } else { $message.='
'; } } } if (&Apache::lonhomework::hide_problem_status() && $Apache::inputtags::status[-1] ne 'SHOW_ANSWER' && &hide_award($award)) { $message = &mt("Answer Submitted: Your final submission will be graded after the due date."); my @interval= &Apache::lonnet::EXT("resource.$part.interval"); if ($interval[0] =~ /\d+/) { my $first_access=&Apache::lonnet::get_first_access($interval[1]); if (defined($first_access)) { my $due_date= &Apache::lonnet::EXT("resource.$part.duedate"); my ($timelimit) = ($interval[0] =~ /^(\d+)/); unless (($due_date) && ($due_date < $first_access + $timelimit)) { $message = &mt("Answer Submitted: Your final submission will be graded when the time limit is reached."); } } } $css_class=$possible_class{'no_grade'}; $button=1; if ($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Placement') { if ($Apache::inputtags::status[-1] eq 'CANNOT_ANSWER') { $message = 'Answer Submitted'; } else { undef($message); } } } if ($Apache::inputtags::status[-1] eq 'SHOW_ANSWER' && !$added_computer_text && $target ne 'tex') { if ($computer) { $message = "$computer $message"; } $added_computer_text=1; } if ($Apache::lonhomework::type eq 'practice') { if ($target eq 'web') { $message .= '
'; } else { $message .= ' '; } $message.=&mt('Submissions to practice problems are not permanently recorded.'); } return ($button,$css_class,$message,$previousmsg); } sub markup_unit { my ($unit,$target)=@_; if ($target eq 'tex') { return '\texttt{'.&Apache::lonxml::latex_special_symbols($unit).'}'; } else { return "".$unit.""; } } sub removealldata { my ($id)=@_; foreach my $key (keys(%Apache::lonhomework::results)) { if (($key =~ /^resource\.\Q$id\E\./) && ($key !~ /\.collaborators$/)) { &Apache::lonxml::debug("Removing $key"); delete($Apache::lonhomework::results{$key}); } } } sub hidealldata { my ($id)=@_; foreach my $key (keys(%Apache::lonhomework::results)) { if (($key =~ /^resource\.\Q$id\E\./) && ($key !~ /\.collaborators$/)) { &Apache::lonxml::debug("Hidding $key"); my $newkey=$key; $newkey=~s/^(resource\.\Q$id\E\.[^\.]+\.)(.*)$/${1}hidden${2}/; $Apache::lonhomework::results{$newkey}= $Apache::lonhomework::results{$key}; delete($Apache::lonhomework::results{$key}); } } } sub setgradedata { my ($award,$msg,$id,$previously_used) = @_; if ($Apache::lonhomework::scantronmode && &Apache::lonnet::validCODE($env{'form.CODE'})) { $Apache::lonhomework::results{"resource.CODE"}=$env{'form.CODE'}; } elsif ($Apache::lonhomework::scantronmode && $env{'form.CODE'} eq '' && $Apache::lonhomework::history{"resource.CODE"} ne '') { $Apache::lonhomework::results{"resource.CODE"}=''; } if (!$Apache::lonhomework::scantronmode && $Apache::inputtags::status['-1'] ne 'CAN_ANSWER' && $Apache::inputtags::status['-1'] ne 'CANNOT_ANSWER') { $Apache::lonhomework::results{"resource.$id.afterduedate"}=$award; return ''; } elsif ( $Apache::lonhomework::history{"resource.$id.awarded"} < 1 || $Apache::lonhomework::scantronmode || &Apache::lonhomework::hide_problem_status() ) { # the student doesn't already have it correct, # or we are in a mode (scantron orno problem status) where a correct # can become incorrect # handle assignment of tries and solved status my $solvemsg; if ($Apache::lonhomework::scantronmode) { $solvemsg='correct_by_scantron'; } else { $solvemsg='correct_by_student'; } if ($Apache::lonhomework::history{"resource.$id.afterduedate"}) { $Apache::lonhomework::results{"resource.$id.afterduedate"}=''; } if ( $award eq 'ASSIGNED_SCORE') { $Apache::lonhomework::results{"resource.$id.tries"} = $Apache::lonhomework::history{"resource.$id.tries"} + 1; $Apache::lonhomework::results{"resource.$id.solved"} = $solvemsg; my $numawards=scalar(@Apache::inputtags::response); $Apache::lonhomework::results{"resource.$id.awarded"} = 0; foreach my $res (@Apache::inputtags::response) { if (defined($Apache::lonhomework::results{"resource.$id.$res.awarded"})) { $Apache::lonhomework::results{"resource.$id.awarded"}+= $Apache::lonhomework::results{"resource.$id.$res.awarded"}; } else { $Apache::lonhomework::results{"resource.$id.awarded"}+= &awarddetail_to_awarded($Apache::lonhomework::results{"resource.$id.$res.awarddetail"}); } } if ($numawards > 0) { $Apache::lonhomework::results{"resource.$id.awarded"}/= $numawards; } } elsif ( $award eq 'APPROX_ANS' || $award eq 'EXACT_ANS' ) { $Apache::lonhomework::results{"resource.$id.tries"} = $Apache::lonhomework::history{"resource.$id.tries"} + 1; $Apache::lonhomework::results{"resource.$id.solved"} = $solvemsg; $Apache::lonhomework::results{"resource.$id.awarded"} = '1'; } elsif ( $award eq 'SUBMITTED_CREDIT' ) { $Apache::lonhomework::results{"resource.$id.tries"} = $Apache::lonhomework::history{"resource.$id.tries"} + 1; $Apache::lonhomework::results{"resource.$id.solved"} = 'credit_attempted'; $Apache::lonhomework::results{"resource.$id.awarded"} = '1'; } elsif ( $award eq 'ANONYMOUS_CREDIT' ) { $Apache::lonhomework::results{"resource.$id.tries"} = $Apache::lonhomework::history{"resource.$id.tries"} + 1; $Apache::lonhomework::results{"resource.$id.solved"} = 'credit_attempted'; $Apache::lonhomework::results{"resource.$id.awarded"} = '1'; } elsif ( $award eq 'INCORRECT' ) { $Apache::lonhomework::results{"resource.$id.tries"} = $Apache::lonhomework::history{"resource.$id.tries"} + 1; if (&Apache::lonhomework::hide_problem_status() || $Apache::lonhomework::scantronmode) { $Apache::lonhomework::results{"resource.$id.awarded"} = 0; } $Apache::lonhomework::results{"resource.$id.solved"} = 'incorrect_attempted'; } elsif ( $award eq 'SUBMITTED' ) { $Apache::lonhomework::results{"resource.$id.tries"} = $Apache::lonhomework::history{"resource.$id.tries"} + 1; $Apache::lonhomework::results{"resource.$id.solved"} = 'ungraded_attempted'; } elsif ( $award eq 'ANONYMOUS' ) { $Apache::lonhomework::results{"resource.$id.tries"} = $Apache::lonhomework::history{"resource.$id.tries"} + 1; $Apache::lonhomework::results{"resource.$id.solved"} = 'ungraded_attempted'; } elsif ( $award eq 'DRAFT' ) { $Apache::lonhomework::results{"resource.$id.solved"} = ''; } elsif ( $award eq 'NO_RESPONSE' ) { #no real response so delete any data that got stored &removealldata($id); return ''; } else { $Apache::lonhomework::results{"resource.$id.solved"} = 'incorrect_attempted'; if (&Apache::lonhomework::show_no_problem_status() || $Apache::lonhomework::scantronmode) { $Apache::lonhomework::results{"resource.$id.tries"} = $Apache::lonhomework::history{"resource.$id.tries"} + 1; $Apache::lonhomework::results{"resource.$id.awarded"} = 0; } if (&Apache::lonhomework::show_some_problem_status()) { # clear out the awarded if they had gotten it wrong/right # and are now in an error mode $Apache::lonhomework::results{"resource.$id.awarded"} = ''; } } if (defined($msg)) { $Apache::lonhomework::results{"resource.$id.awardmsg"} = $msg; } # did either of the overall awards chage? If so ignore the # previous check if (($Apache::lonhomework::results{"resource.$id.awarded"} eq $Apache::lonhomework::history{"resource.$id.awarded"}) && ($Apache::lonhomework::results{"resource.$id.solved"} eq $Apache::lonhomework::history{"resource.$id.solved"})) { # check if this was a previous submission if it was delete the # unneeded data and update the previously_used attribute if ( $previously_used eq 'PREVIOUSLY_USED') { if (&Apache::lonhomework::show_problem_status()) { delete($Apache::lonhomework::results{"resource.$id.tries"}); $Apache::lonhomework::results{"resource.$id.previous"} = '1'; } } elsif ( $previously_used eq 'PREVIOUSLY_LAST') { #delete all data as they student didn't do anything, but save #the list of collaborators. &removealldata($id); #and since they didn't do anything we were never here return ''; } else { $Apache::lonhomework::results{"resource.$id.previous"} = '0'; } } } elsif ( $Apache::lonhomework::history{"resource.$id.awarded"} == 1 ) { #delete all data as they student already has it correct &removealldata($id); #and since they didn't do anything we were never here return ''; } $Apache::lonhomework::results{"resource.$id.award"} = $award; if ($award eq 'SUBMITTED') { &Apache::response::add_to_gradingqueue(); } $Apache::lonhomework::results{"resource.$id.type"} = $Apache::lonhomework::type; $Apache::lonhomework::results{"resource.$id.duedate"} = &Apache::lonnet::EXT("resource.$id.duedate"); $Apache::lonhomework::results{"resource.$id.hinttries"} = &Apache::lonnet::EXT("resource.$id.hinttries"); $Apache::lonhomework::results{"resource.$id.version"} = &Apache::lonnet::usedversion(); $Apache::lonhomework::results{"resource.$id.maxtries"} = &Apache::lonnet::EXT("resource.$id.maxtries"); } sub find_which_previous { my ($version) = @_; my $part = $Apache::inputtags::part; my (@previous_version); foreach my $resp (@Apache::inputtags::response) { my $key = "$version:resource.$part.$resp.submission"; my $submission = $Apache::lonhomework::history{$key}; my %previous = &Apache::response::check_for_previous($submission, $part,$resp, $version); push(@previous_version,$previous{'version'}); } return &previous_match(\@previous_version, scalar(@Apache::inputtags::response)); } sub previous_match { my ($previous_array,$count) = @_; my $match = 0; my @matches; foreach my $versionar (@$previous_array) { foreach my $version (@$versionar) { $matches[$version]++; } } my $which=0; foreach my $elem (@matches) { if ($elem eq $count) { $match=1; last; } $which++; } return ($match,$which); } sub grade { my ($target) = @_; my $id = $Apache::inputtags::part; my $response=''; if ( defined $env{'form.submitted'}) { my (@awards,@msgs); foreach $response (@Apache::inputtags::response) { &Apache::lonxml::debug("looking for response.$id.$response.awarddetail"); my $value=$Apache::lonhomework::results{"resource.$id.$response.awarddetail"}; &Apache::lonxml::debug("keeping $value from $response for $id"); push (@awards,$value); $value=$Apache::lonhomework::results{"resource.$id.$response.awardmsg"}; &Apache::lonxml::debug("got message $value from $response for $id"); push (@msgs,$value); } my ($finalaward,$msg) = &finalizeawards(\@awards,\@msgs,undef,undef, $Apache::lonhomework::scantronmode); my $previously_used; if ( $#Apache::inputtags::previous eq $#awards ) { my ($match) = &previous_match(\@Apache::inputtags::previous_version, scalar(@Apache::inputtags::response)); if ($match) { $previously_used = 'PREVIOUSLY_LAST'; foreach my $value (@Apache::inputtags::previous) { if ($value eq 'PREVIOUSLY_USED' ) { $previously_used = $value; last; } } } } &Apache::lonxml::debug("final award $finalaward, $previously_used, message $msg"); &setgradedata($finalaward,$msg,$id,$previously_used); } return ''; } sub get_grade_messages { my ($id,$prefix,$target,$status,$nocorrect,$tdclass) = @_; # nocorrect suppresses "Computer's answer now shown above" my ($message,$latemessage,$trystr,$previousmsg); my $showbutton = 1; my $award = $Apache::lonhomework::history{"$prefix.award"}; my $awarded = $Apache::lonhomework::history{"$prefix.awarded"}; my $solved = $Apache::lonhomework::history{"$prefix.solved"}; my $previous = $Apache::lonhomework::history{"$prefix.previous"}; my $awardmsg = $Apache::lonhomework::history{"$prefix.awardmsg"}; &Apache::lonxml::debug("Found Award |$award|$solved|$awardmsg"); if ( $award ne '' || $solved ne '' || $status eq 'SHOW_ANSWER') { &Apache::lonxml::debug('Getting message'); ($showbutton,my $css_class,$message,$previousmsg) = &decideoutput($award,$awarded,$awardmsg,$solved,$previous, $target,(($status eq 'CAN_ANSWER') || $nocorrect),$tdclass); if ($target eq 'tex') { $message='\vskip 2 mm '.$message.' '; } else { if ($message) { $message="$message"; } else { $message=""; } if ($previousmsg) { $previousmsg="$previousmsg"; } } } my $tries = $Apache::lonhomework::history{"$prefix.tries"}; my $maxtries = &Apache::lonnet::EXT("resource.$id.maxtries"); &Apache::lonxml::debug("got maxtries of :$maxtries:"); #if tries are set to negative turn off the Tries/Button and messages if (defined($maxtries) && $maxtries < 0) { return ''; } if ( $tries eq '' ) { $tries = '0'; } if ( $maxtries eq '' ) { $maxtries = '2'; } if ( $maxtries eq 'con_lost' ) { $maxtries = '0'; } my $tries_text= &get_tries_text(); if ($showbutton) { if ($target eq 'tex') { if ($env{'request.state'} ne "construct" && $Apache::lonhomework::type ne 'exam' && $env{'form.suppress_tries'} ne 'yes') { $trystr ='{\vskip 1 mm \small ' .&mt('[_1]'.$tries_text.'[_2] [_3]' ,'\textit{','}',$tries.'/'.$maxtries ) .'} \vskip 2 mm'; } else { $trystr = '\vskip 0 mm '; } } else { my $trial =$tries; if ($Apache::lonhomework::parsing_a_task) { } elsif($env{'request.state'} ne 'construct') { $trial.="/".&Apache::lonhtmlcommon::direct_parm_link($maxtries,$env{'request.symb'},'maxtries',$id,$target); } else { if (defined($Apache::inputtags::params{'maxtries'})) { $trial.="/".$Apache::inputtags::params{'maxtries'}; } } unless (($env{'request.state'} ne "construct") && ($env{'course.'.$env{'request.course.id'}.'.type'} eq 'Placement') && (!$env{'request.role.adv'})) { $trystr = ''.&mt($tries_text.' [_1]',$trial).''; } $trystr = ''.$trystr.''; } } if ($Apache::lonhomework::history{"$prefix.afterduedate"}) { #last submissions was after due date $latemessage=&mt(' The last submission was after the Due Date ');; if ($target eq 'web') { $latemessage=''.$latemessage.''; } } return ($previousmsg,$latemessage,$message,$trystr,$showbutton); } sub gradestatus { my ($id,$target,$no_previous) = @_; my $showbutton = 1; my $message = ''; my $latemessage = ''; my $trystr=''; my $button=''; my $previousmsg=''; my $tdclass=''; my $status = $Apache::inputtags::status['-1']; &Apache::lonxml::debug("gradestatus has :$status:"); if ( $status ne 'CLOSED' && $status ne 'UNAVAILABLE' && $status ne 'INVALID_ACCESS' && $status ne 'NEEDS_CHECKIN' && $status ne 'NOT_IN_A_SLOT' && $status ne 'RESERVABLE' && $status ne 'RESERVABLE_LATER' && $status ne 'NOTRESERVABLE' && $status ne 'NEED_DIFFERENT_IP') { if ($status eq 'SHOW_ANSWER') { $showbutton = 0; } unless (($status eq 'SHOW_ANSWER') || ($status eq 'CANNOT_ANSWER')) { if ($target ne 'tex') { $tdclass = 'LC_status_submit_'.$id; } } ($previousmsg,$latemessage,$message,$trystr) = &get_grade_messages($id,"resource.$id",$target,$status, $showbutton,$tdclass); if ($status eq 'CANNOT_ANSWER') { $showbutton = 0; } if ( $status eq 'SHOW_ANSWER') { undef($previousmsg); } if ( $showbutton ) { if ($target ne 'tex') { $button = ' '. ''; } } } my $output= $previousmsg.$latemessage.$message.$trystr; if ($output =~ /^\s*$/) { return $button; } else { if ($target eq 'tex') { return $button.' \vskip 0 mm '.$output.' '; } else { $output = ''.$output; if ((!$no_previous) && (($env{'course.'.$env{'request.course.id'}.'.type'} ne 'Placement') || ($env{'request.role.adv'}))) { $output.=''; } $output.= '
'.$button.''.&previous_tries($id,$target).'
'; return $output; } } } sub previous_tries { my ($id,$target) = @_; my $output; my $status = $Apache::inputtags::status['-1']; my $count; my %count_lookup; my ($lastrndseed,$lasttype); my $numstamps = 0; foreach my $i (1..$Apache::lonhomework::history{'version'}) { my $prefix = $i.":resource.$id"; my $is_anon; my $curr_type = $Apache::lonhomework::history{"$prefix.type"}; if (defined($env{'form.grade_symb'})) { if (($curr_type eq 'anonsurvey') || ($curr_type eq 'anonsurveycred')) { $is_anon = 1; } } next if (!exists($Apache::lonhomework::history{"$prefix.award"})); $count++; $count_lookup{$i} = $count; my $curr_rndseed = $Apache::lonhomework::history{"$prefix.rndseed"}; my ($previousmsg,$latemessage,$message,$trystr); ($previousmsg,$latemessage,$message,$trystr) = &get_grade_messages($id,"$prefix",$target,$status); if ($previousmsg ne '') { my ($match,$which) = &find_which_previous($i); $message=$previousmsg; my $previous = $count_lookup{$which}; $message =~ s{()}{ as submission \# $previous $1}; } elsif ($Apache::lonhomework::history{"$prefix.tries"}) { if (!(&Apache::lonhomework::hide_problem_status() && $Apache::inputtags::status[-1] ne 'SHOW_ANSWER') && $Apache::lonhomework::history{"$prefix.solved"} =~/^correct/ ) { my $txt_correct = &mt('Correct'); my $awarded = $Apache::lonhomework::history{"$prefix.awarded"}; if ($awarded < 1 && $awarded > 0) { $txt_correct=&mt('Partially Correct'); } elsif ($awarded < 1) { if ($awarded eq '') { $txt_correct=''; } else { $txt_correct=&mt('Incorrect'); } } $message =~ s{()(.*?)()} {$1 $txt_correct. $3}s; } my $trystr = "(".&mt('Try [_1]',$Apache::lonhomework::history{"$prefix.tries"}).")"; if (($curr_rndseed ne '') && ($lastrndseed ne '')) { if (($curr_rndseed ne $lastrndseed) && (($curr_type eq 'randomizetry') || ($lasttype eq 'randomizetry'))) { $trystr .= '
'.&mt('New problem variation this try.').''; } } $message =~ s{()}{ $trystr $1}; } my ($class) = ($message =~ m{)}{}; $output .= ''. ''.$count.''.$message; if ((!$is_anon) && ($Apache::lonhomework::history{"$prefix.tries"}) && ($Apache::lonhomework::history{"$prefix.award"} ne 'ASSIGNED_SCORE') && ($Apache::lonhomework::history{$i.':timestamp'})) { $output .= ''.&Apache::lonlocal::locallocaltime( $Apache::lonhomework::history{$i.':timestamp'}).''; $numstamps ++; } else { $output .= ''; } foreach my $resid (@Apache::inputtags::response) { my $prefix = $prefix.".$resid"; if (exists($Apache::lonhomework::history{"$prefix.submission"})) { my $submission = $Apache::inputtags::submission_display{"$prefix.submission"}; if (!defined($submission)) { $submission = $Apache::lonhomework::history{"$prefix.submission"}; } if ($is_anon) { $output.=''.&mt('(only shown to submitter)').''; } else { $output.=''.$submission.''; } } else { $output.=''; } } $output.=&Apache::loncommon::end_data_table_row()."\n"; $lastrndseed = $curr_rndseed; $lasttype = $curr_type; } return if ($output eq ''); my $headers = ''. ''.&mt('Submission #').''. ''.&mt('Try').''; if ($numstamps) { $headers .= &mt('When'); } $headers .= ''; my $colspan = scalar(@Apache::inputtags::response); if ($colspan > 1) { $headers .= ''; } else { $headers .= ''; } $headers .= &mt('Submitted Answer').''; $output =''.$headers.$output.'
'; my $tries_text = &get_tries_text('link'); my $prefix = $env{'form.request.prefix'}; $prefix =~ tr{.}{_}; my $function_name = 'LONCAPA_previous_tries_'.$prefix; if (($env{'request.state'} eq 'construct') || ($id =~ /\W/)) { $function_name .= $Apache::lonxml::curdepth; } else { $function_name .= $id; } $function_name .= '_'.$Apache::lonxml::counter; my $possmathjax = 1; my $result = &Apache::loncommon::modal_adhoc_window($function_name,420,410,$output, &mt($tries_text),$possmathjax)."
"; return $result; } sub get_tries_text { my ($context) = @_; my $tries_text; if ($context eq 'link') { $tries_text = 'Previous Tries'; } else { $tries_text = 'Tries'; } if ( $Apache::lonhomework::type eq 'survey' || $Apache::lonhomework::type eq 'surveycred' || $Apache::lonhomework::type eq 'anonsurvey' || $Apache::lonhomework::type eq 'anonsurveycred' || $Apache::lonhomework::parsing_a_task) { if ($context eq 'link') { $tries_text = 'Previous Submissions'; } else { $tries_text = 'Submissions'; } } return $tries_text; } sub spelling_languages { my %langchoices; foreach my $id (&Apache::loncommon::languageids()) { my $code = &Apache::loncommon::supportedlanguagecode($id); if ($code ne '') { $langchoices{$code} = &Apache::loncommon::plainlanguagedescription($id); } } my @spelllangs = ('none'); foreach my $code ('en','de','he','es','fr','pt','tr') { push(@spelllangs,[$code,$langchoices{$code}]); } return \@spelllangs; } sub edit_mathresponse_button { my ($field) = @_; my $eqneditor = 'lcmath'; if ($env{'browser.type'} eq 'safari') { if ($env{'browser.os'} eq 'mac') { my ($prefix,$version) = ($env{'browser.version'} =~ /^(\d*)(\d{3})\./); if ($env{'browser.mobile'}) { if (($version < 531) || (($prefix eq '') && ($version < 533))) { $eqneditor = ''; } } elsif ($version < 533) { $eqneditor = 'dragmath'; } } elsif ($env{'browser.os'} eq 'win') { if ($env{'browser.version'} < 533) { $eqneditor = 'dragmath'; } } } elsif ($env{'browser.type'} eq 'explorer') { if ($env{'browser.version'} < 9) { $eqneditor = 'dragmath'; } } elsif ($env{'browser.type'} eq 'mozilla') { if ($env{'browser.version'} < 5) { $eqneditor = 'dragmath'; } else { if ($env{'browser.info'} =~ /^firefox\-([\d\.]+)/) { my $firefox = $1; if ($firefox < 4) { $eqneditor = 'dragmath'; } } } } elsif ($env{'browser.type'} eq 'chrome') { if ($env{'browser.version'} < 5) { $eqneditor = 'dragmath'; } } elsif ($env{'browser.type'} eq 'opera') { if ($env{'browser.version'} < 12) { $eqneditor = 'dragmath'; } } if ($eqneditor eq 'lcmath') { if (($env{'request.course.id'}) && ($env{'request.state'} ne 'construct')) { if (exists($env{'course.'.$env{'request.course.id'}.'.uselcmath'})) { if ($env{'course.'.$env{'request.course.id'}.'.uselcmath'} eq '0') { $eqneditor = 'dragmath'; } } else { my %domdefs = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'}); if ($domdefs{'uselcmath'} eq '0') { $eqneditor = 'dragmath'; } } } else { my %domdefs = &Apache::lonnet::get_domain_defaults($env{'course.'.$env{'request.course.id'}.'.domain'}); if ($domdefs{'uselcmath'} eq '0') { $eqneditor = 'dragmath'; } } } if ($eqneditor eq 'dragmath') { # DragMath applet my $button=&mt('Edit Answer'); # my $helplink=&Apache::loncommon::help_open_topic('Formula_Editor'); my $iconpath=$Apache::lonnet::perlvar{'lonIconsURL'}; return(< function LC_mathedit_${field} (LCtextline) { thenumber = LCtextline; var thedata = ''; if (document.getElementById(LCtextline)) { thedata = document.getElementById(LCtextline).value; } newwin = window.open("/adm/dragmath/MaximaPopup.html","","width=565,height=400,resizable"); } $button ENDFORMULABUTTON } elsif ($eqneditor eq 'lcmath') { # LON-CAPA math equation editor my $mathjaxjs; unless (lc(&Apache::lontexconvert::tex_engine()) eq 'mathjax') { $mathjaxjs = <<"MATHJAX_SCRIPT"; var mathjaxscript = document.createElement("script"); mathjaxscript.type = "text/javascript"; mathjaxscript.src = "/adm/MathJax/MathJax.js?config=TeX-AMS-MML_HTMLorMML"; document.body.appendChild(mathjaxscript); MATHJAX_SCRIPT } return(< var LCmathField = document.getElementById('${field}'); LCmathField.className += ' math'; // note the space LCmathField.setAttribute('data-implicit_operators', 'true'); var LCMATH_started; if (typeof LCMATH_started === 'undefined') { $mathjaxjs LCMATH_started = true; var script = document.createElement("script"); script.type = "text/javascript"; script.src = "/adm/LC_math_editor/LC_math_editor.min.js"; document.body.appendChild(script); window.addEventListener('load', function(e) { LCMATH.initEditors(); }, false); } EQ_EDITOR_SCRIPT } } 1; __END__ =pod =back =cut