--- loncom/homework/grades.pm 2006/04/02 00:17:26 1.345 +++ loncom/homework/grades.pm 2006/09/14 21:47:22 1.375 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # The LON-CAPA Grading handler # -# $Id: grades.pm,v 1.345 2006/04/02 00:17:26 bowersj2 Exp $ +# $Id: grades.pm,v 1.375 2006/09/14 21:47:22 albertel Exp $ # # Copyright Michigan State University Board of Trustees # @@ -36,10 +36,13 @@ use Apache::lonhtmlcommon; use Apache::lonnavmaps; use Apache::lonhomework; use Apache::loncoursedata; -use Apache::lonmsg qw(:user_normal_msg); +use Apache::lonmsg(); use Apache::Constants qw(:common); use Apache::lonlocal; use String::Similarity; +use lib '/home/httpd/lib/perl'; +use LONCAPA; + use POSIX qw(floor); my %oldessays=(); @@ -120,7 +123,7 @@ sub response_type { foreach (split(/,/,&Apache::lonnet::metadata($url,'packages'))) { if (/^\w+response_.*/ || /^Task_/) { my ($responsetype,$part) = split(/_/,$_,2); - my ($partid,$respid) = split(/_/,$part); + my ($partid,$respid) = split(/_/,$part,2); if ($responsetype eq 'Task') { $respid='0'; } if (&Apache::loncommon::check_if_partid_hidden($partid,$symb)) { next; @@ -141,6 +144,18 @@ sub response_type { return (\@partlist,\%handgrade,\%responseType); } +sub flatten_responseType { + my ($responseType) = @_; + my @part_response_id = + map { + my $part = $_; + map { + [$part,$_] + } sort(keys(%{ $responseType->{$part} })); + } sort(keys(%$responseType)); + return @part_response_id; +} + sub get_display_part { my ($partID,$symb)=@_; my $display=&Apache::lonnet::EXT('resource.'.$partID.'.display',$symb); @@ -165,25 +180,26 @@ sub showResourceInfo { my %resptype = (); my $hdgrade='no'; my %partsseen; - for my $part_resID (sort keys(%$handgrade)) { - my $handgrade=$$handgrade{$part_resID}; - my ($partID,$resID) = split(/_/,$part_resID); - my $responsetype = $responseType->{$partID}->{$resID}; - $hdgrade = $handgrade if ($handgrade eq 'yes'); - $result.='<tr>'; - if ($checkboxes) { - if (exists($partsseen{$partID})) { - $result.="<td> </td>"; - } else { - $result.="<td><input type='checkbox' name='vPart' value='$partID' checked='on' /></td>"; + foreach my $partID (sort keys(%$responseType)) { + foreach my $resID (sort keys(%{ $responseType->{$partID} })) { + my $handgrade=$$handgrade{$partID.'_'.$resID}; + my $responsetype = $responseType->{$partID}->{$resID}; + $hdgrade = $handgrade if ($handgrade eq 'yes'); + $result.='<tr>'; + if ($checkboxes) { + if (exists($partsseen{$partID})) { + $result.="<td> </td>"; + } else { + $result.="<td><input type='checkbox' name='vPart' value='$partID' checked='on' /></td>"; + } + $partsseen{$partID}=1; } - $partsseen{$partID}=1; - } - my $display_part=&get_display_part($partID,$symb); - $result.='<td><b>Part: </b>'.$display_part.' <font color="#999999">'. - $resID.'</font></td>'. - '<td><b>Type: </b>'.$responsetype.'</td></tr>'; + my $display_part=&get_display_part($partID,$symb); + $result.='<td><b>Part: </b>'.$display_part.' <font color="#999999">'. + $resID.'</font></td>'. + '<td><b>Type: </b>'.$responsetype.'</td></tr>'; # '<td><b>Handgrade: </b>'.$handgrade.'</td></tr>'; + } } $result.='</table>'."\n"; return $result,$responseType,$hdgrade,$partlist,$handgrade; @@ -722,7 +738,14 @@ LISTJAVASCRIPT $gradeTable.='<label><input type="radio" name="lastSub" value="lastonly" '.$checklastsub.' /> last submission only </label>'."\n". '<label><input type="radio" name="lastSub" value="last" /> last submission & parts info </label>'."\n". '<label><input type="radio" name="lastSub" value="datesub" /> by dates and submissions </label>'."\n". - '<label><input type="radio" name="lastSub" value="all" /> all details</label>'."\n". + '<label><input type="radio" name="lastSub" value="all" /> all details</label><br />'."\n". + ' <b>Grading Increments:</b> <select name="increment">'. + '<option value="1">Whole Points</option>'. + '<option value=".5">Half Points</option>'. + '<option value=".25">Quarter Points</option>'. + '<option value=".1">Tenths of a Point</option>'. + '</select>'. + '<input type="hidden" name="section" value="'.$getsec.'" />'."\n". '<input type="hidden" name="submitonly" value="'.$submitonly.'" />'."\n". '<input type="hidden" name="handgrade" value="'.$env{'form.handgrade'}.'" /><br />'."\n". @@ -1135,6 +1158,81 @@ sub sub_page_kw_js { my $request = shift; my $iconpath = $request->dir_config('lonIconsURL'); &commonJSfunctions($request); + + my $inner_js_msg_central=<<INNERJS; + <script text="text/javascript"> + function checkInput() { + opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value); + var nmsg = opener.document.SCORE.savemsgN.value; + var usrctr = document.msgcenter.usrctr.value; + var newval = opener.document.SCORE["newmsg"+usrctr]; + newval.value = opener.checkEntities(document.msgcenter.newmsg.value); + + var msgchk = ""; + if (document.msgcenter.subchk.checked) { + msgchk = "msgsub,"; + } + var includemsg = 0; + for (var i=1; i<=nmsg; i++) { + var opnmsg = opener.document.SCORE["savemsg"+i]; + var frmmsg = document.msgcenter["msg"+i]; + opnmsg.value = opener.checkEntities(frmmsg.value); + var showflg = opener.document.SCORE["shownOnce"+i]; + showflg.value = "1"; + var chkbox = document.msgcenter["msgn"+i]; + if (chkbox.checked) { + msgchk += "savemsg"+i+","; + includemsg = 1; + } + } + if (document.msgcenter.newmsgchk.checked) { + msgchk += "newmsg"+usrctr; + includemsg = 1; + } + imgformname = opener.document.SCORE["mailicon"+usrctr]; + imgformname.src = "$iconpath/"+((includemsg) ? "mailto.gif" : "mailbkgrd.gif"); + var includemsg = opener.document.SCORE["includemsg"+usrctr]; + includemsg.value = msgchk; + + self.close() + + } + </script> +INNERJS + + my $inner_js_highlight_central=<<INNERJS; + <script type="text/javascript"> + function updateChoice(flag) { + opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr); + opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize); + opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle); + opener.document.SCORE.refresh.value = "on"; + if (opener.document.SCORE.keywords.value!=""){ + opener.document.SCORE.submit(); + } + self.close() + } +</script> +INNERJS + + my $start_page_msg_central = + &Apache::loncommon::start_page('Message Central',$inner_js_msg_central, + {'js_ready' => 1, + 'only_body' => 1, + 'bgcolor' =>'#FFFFFF',}); + my $end_page_msg_central = + &Apache::loncommon::end_page({'js_ready' => 1}); + + + my $start_page_highlight_central = + &Apache::loncommon::start_page('Highlight Central', + $inner_js_highlight_central, + {'js_ready' => 1, + 'only_body' => 1, + 'bgcolor' =>'#FFFFFF',}); + my $end_page_highlight_central = + &Apache::loncommon::end_page({'js_ready' => 1}); + my $docopen=&Apache::lonhtmlcommon::javascript_docopen(); $docopen=~s/^document\.//; $request->print(<<SUBJAVASCRIPT); @@ -1250,51 +1348,7 @@ sub sub_page_kw_js { pWin.focus(); pDoc = pWin.document; pDoc.$docopen; - pDoc.write("<html><head>"); - pDoc.write("<title>Message Central</title>"); - - pDoc.write("<script language=javascript>"); - pDoc.write("function checkInput() {"); - pDoc.write(" opener.document.SCORE.msgsub.value = opener.checkEntities(document.msgcenter.msgsub.value);"); - pDoc.write(" var nmsg = opener.document.SCORE.savemsgN.value;"); - pDoc.write(" var usrctr = document.msgcenter.usrctr.value;"); - pDoc.write(" var newval = opener.document.SCORE[\\"newmsg\\"+usrctr];"); - pDoc.write(" newval.value = opener.checkEntities(document.msgcenter.newmsg.value);"); - - pDoc.write(" var msgchk = \\"\\";"); - pDoc.write(" if (document.msgcenter.subchk.checked) {"); - pDoc.write(" msgchk = \\"msgsub,\\";"); - pDoc.write(" }"); - pDoc.write(" var includemsg = 0;"); - pDoc.write(" for (var i=1; i<=nmsg; i++) {"); - pDoc.write(" var opnmsg = opener.document.SCORE[\\"savemsg\\"+i];"); - pDoc.write(" var frmmsg = document.msgcenter[\\"msg\\"+i];"); - pDoc.write(" opnmsg.value = opener.checkEntities(frmmsg.value);"); - pDoc.write(" var showflg = opener.document.SCORE[\\"shownOnce\\"+i];"); - pDoc.write(" showflg.value = \\"1\\";"); - pDoc.write(" var chkbox = document.msgcenter[\\"msgn\\"+i];"); - pDoc.write(" if (chkbox.checked) {"); - pDoc.write(" msgchk += \\"savemsg\\"+i+\\",\\";"); - pDoc.write(" includemsg = 1;"); - pDoc.write(" }"); - pDoc.write(" }"); - pDoc.write(" if (document.msgcenter.newmsgchk.checked) {"); - pDoc.write(" msgchk += \\"newmsg\\"+usrctr;"); - pDoc.write(" includemsg = 1;"); - pDoc.write(" }"); - pDoc.write(" imgformname = opener.document.SCORE[\\"mailicon\\"+usrctr];"); - pDoc.write(" imgformname.src = \\"$iconpath/\\"+((includemsg) ? \\"mailto.gif\\" : \\"mailbkgrd.gif\\");"); - pDoc.write(" var includemsg = opener.document.SCORE[\\"includemsg\\"+usrctr];"); - pDoc.write(" includemsg.value = msgchk;"); - - pDoc.write(" self.close()"); - - pDoc.write("}"); - - pDoc.write("<"); - pDoc.write("/script>"); - - pDoc.write("</head><body bgcolor=white>"); + pDoc.write('$start_page_msg_central'); pDoc.write("<form action=\\"inactive\\" name=\\"msgcenter\\">"); pDoc.write("<input value=\\""+usrctr+"\\" name=\\"usrctr\\" type=\\"hidden\\">"); @@ -1335,7 +1389,7 @@ sub sub_page_kw_js { pDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:checkInput()\\"> "); pDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />"); pDoc.write("</form>"); - pDoc.write("</body></html>"); + pDoc.write('$end_page_msg_central'); pDoc.close(); } @@ -1381,26 +1435,7 @@ sub sub_page_kw_js { hwdWin.focus(); var hDoc = hwdWin.document; hDoc.$docopen; - hDoc.write("<html><head>"); - hDoc.write("<title>Highlight Central</title>"); - - hDoc.write("<script language=javascript>"); - hDoc.write("function updateChoice(flag) {"); - hDoc.write(" opener.document.SCORE.kwclr.value = opener.radioSelection(document.hlCenter.kwdclr);"); - hDoc.write(" opener.document.SCORE.kwsize.value = opener.radioSelection(document.hlCenter.kwdsize);"); - hDoc.write(" opener.document.SCORE.kwstyle.value = opener.radioSelection(document.hlCenter.kwdstyle);"); - hDoc.write(" opener.document.SCORE.refresh.value = \\"on\\";"); - hDoc.write(" if (opener.document.SCORE.keywords.value!=\\"\\"){"); - hDoc.write(" opener.document.SCORE.submit();"); - hDoc.write(" }"); - hDoc.write(" self.close()"); - hDoc.write("}"); - - hDoc.write("<"); - hDoc.write("/script>"); - - hDoc.write("</head><body bgcolor=white>"); - + hDoc.write('$start_page_highlight_central'); hDoc.write("<form action=\\"inactive\\" name=\\"hlCenter\\">"); hDoc.write("<font color=\\"green\\" size=+1> Keyword Highlight Options</font><br /><br />"); @@ -1428,7 +1463,7 @@ sub sub_page_kw_js { hDoc.write("<input type=\\"button\\" value=\\"Save\\" onClick=\\"javascript:updateChoice(1)\\"> "); hDoc.write("<input type=\\"button\\" value=\\"Cancel\\" onClick=\\"self.close()\\"><br /><br />"); hDoc.write("</form>"); - hDoc.write("</body></html>"); + hDoc.write('$end_page_highlight_central'); hDoc.close(); } @@ -1436,6 +1471,15 @@ sub sub_page_kw_js { SUBJAVASCRIPT } +sub get_increment { + my $increment = $env{'form.increment'}; + if ($increment != 1 && $increment != .5 && $increment != .25 && + $increment != .1) { + $increment = 1; + } + return $increment; +} + #--- displays the grading box, used in essay type problem and grading by page/sequence sub gradeBox { my ($request,$symb,$uname,$udom,$counter,$partid,$record) = @_; @@ -1458,13 +1502,16 @@ sub gradeBox { $result.='<table border="0"><tr><td>'. '<b>Part: </b>'.$display_part.' <b>Points: </b></td><td>'."\n"; my $ctr = 0; + my $thisweight = 0; + my $increment = &get_increment(); $result.='<table border="0"><tr>'."\n"; # display radio buttons in a nice table 10 across - while ($ctr<=$wgt) { + while ($thisweight<=$wgt) { $result.= '<td><nobr><label><input type="radio" name="RADVAL'.$counter.'_'.$partid.'" '. 'onclick="javascript:writeBox(this.form,\''.$counter.'_'.$partid.'\','. - $ctr.')" value="'.$ctr.'" '. - ($score eq $ctr ? 'checked':'').' /> '.$ctr."</label></nobr></td>\n"; + $thisweight.')" value="'.$thisweight.'" '. + ($score eq $thisweight ? 'checked':'').' /> '.$thisweight."</label></nobr></td>\n"; $result.=(($ctr+1)%10 == 0 ? '</tr><tr>' : ''); + $thisweight += $increment; $ctr++; } $result.='</tr></table>'; @@ -1504,10 +1551,11 @@ sub handback_box { my ($symb,$uname,$udom,$counter,$partid,$record) = @_; my ($partlist,$handgrade,$responseType) = &response_type($symb); my (@respids); - foreach my $part_resp (sort(keys(%$handgrade))) { - my ($part,$resp) = split(/_/,$part_resp); + my @part_response_id = &flatten_responseType($responseType); + foreach my $part_response_id (@part_response_id) { + my ($part,$resp) = @{ $part_response_id }; if ($part eq $partid) { - push @respids,$resp; + push(@respids,$resp); } } my $result; @@ -1517,12 +1565,18 @@ sub handback_box { next if (!@$files); my $file_counter = 1; foreach my $file (@$files) { - my ($file_disp) = ($file =~ m|.+/(.+)$|); - $result.=&mt('Return commented version of [_1] to student.', - '<span class="filename">'.$file_disp.'</span>'); - $result.='<input type="file" name="'.$prefix.'returndoc'.$file_counter.'" />'."\n"; - $result.='<input type="hidden" name="'.$prefix.'origdoc'.$file_counter.'" value="'.$file.'" /><br />'; - $file_counter++; + if ($file =~ /\/portfolio\//) { + my ($file_path, $file_disp) = ($file =~ m|(.+/)(.+)$|); + my ($name,$version,$ext) = &file_name_version_ext($file_disp); + $file_disp = "$name.$ext"; + $file = $file_path.$file_disp; + $result.=&mt('Return commented version of [_1] to student.', + '<span class="LC_filename">'.$file_disp.'</span>'); + $result.='<input type="file" name="'.$prefix.'returndoc'.$file_counter.'" />'."\n"; + $result.='<input type="hidden" name="'.$prefix.'origdoc'.$file_counter.'" value="'.$file.'" /><br />'; + $result.='(File will be uploaded when you click on Save & Next below.)<br />'; + $file_counter++; + } } } return $result; @@ -1539,7 +1593,7 @@ sub show_problem { if ($removeform) { $rendered=~s|<form(.*?)>||g; $rendered=~s|</form>||g; - $rendered=~s|name="submit"|name="would_have_been_submit"|g; + $rendered=~s|(<input[^>]*name\s*=\s*"?)(\w+)("?)|$1would_have_been_$2$3|g; } my $companswer; if ($mode eq 'both' or $mode eq 'answer') { @@ -1713,7 +1767,7 @@ KEYWORDS # my (undef,undef,$essayurl) = &Apache::lonnet::decode_symb($symb); my ($adom,$aname,$apath)=($essayurl=~/^(\w+)\/(\w+)\/(.*)$/); - $apath=&Apache::lonnet::escape($apath); + $apath=&escape($apath); $apath=~s/\W/\_/gs; %oldessays=&Apache::lonnet::dump('nohist_essay_'.$apath,$adom,$aname); } @@ -1818,8 +1872,9 @@ KEYWORDS $lastsubonly.='<tr><td bgcolor="#ffffe6">'.$$string[0]; } else { my %seenparts; - for my $part (sort keys(%$handgrade)) { - my ($partid,$respid) = split(/_/,$part); + my @part_response_id = &flatten_responseType($responseType); + foreach my $part (@part_response_id) { + my ($partid,$respid) = @{ $part }; my $display_part=&get_display_part($partid,$symb); if ($env{"form.$uname:$udom:$partid:submitted_by"}) { if (exists($seenparts{$partid})) { next; } @@ -1843,7 +1898,7 @@ KEYWORDS } foreach (@$string) { my ($partid,$respid) = /^resource\.([^\.]*)\.([^\.]*)\.submission/; - if ($part ne ($partid.'_'.$respid)) { next; } + if (join('_',@{$part}) ne ($partid.'_'.$respid)) { next; } my ($ressub,$subval) = split(/:/,$_,2); # Similarity check my $similar=''; @@ -1931,8 +1986,8 @@ KEYWORDS '<input type="hidden" name="newmsg'.$counter.'" value="" />'."\n"; $result.=' <a href="javascript:msgCenter(document.SCORE,'.$counter. ',\''.$msgfor.'\')"; TARGET=_self>'. - &mt('Compose message to student').(scalar(@col_fullnames) >= 1 ? 's' : '').'</a> ('. - &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" />)'. + &mt('Compose message to student').(scalar(@col_fullnames) >= 1 ? 's' : '').'</a><label> ('. + &mt('incl. grades').' <input type="checkbox" name="withgrades'.$counter.'" /></label>)'. '<img src="'.$request->dir_config('lonIconsURL'). '/mailbkgrd.gif" width="14" height="10" name="mailicon'.$counter.'" />'."\n". '<br /> ('. @@ -1953,8 +2008,10 @@ KEYWORDS my %seen = (); my @partlist; my @gradePartRespid; - for my $part_resp (sort(keys(%$handgrade))) { - my ($partid,$respid) = split(/_/, $part_resp); + my @part_response_id = &flatten_responseType($responseType); + foreach my $part_response_id (@part_response_id) { + my ($partid,$respid) = @{ $part_response_id }; + my $part_resp = join('_',@{ $part_response_id }); next if ($seen{$partid} > 0); $seen{$partid}++; next if ($$handgrade{$part_resp} =~ /:no$/ && $env{'form.lastSub'} =~ /^(hdgrade)$/); @@ -1992,6 +2049,8 @@ KEYWORDS '<input type="button" value="Next" '. 'onClick="javascript:checksubmit(this.form,\'Next\');" TARGET=_self> '; $endform.='(Next and Previous (student) do not save the scores.)'."\n" ; + $endform.="<input type='hidden' value='".&get_increment(). + "' name='increment' />"; $endform.='</td><tr></table></form>'; $endform.=&show_grading_menu_form($symb); $request->print($endform); @@ -2068,6 +2127,7 @@ sub processHandGrade { if ($includemsg =~ /savemsg|newmsg\Q$ctr\E/) { $subject = $env{'form.msgsub'} if ($includemsg =~ /msgsub/); unless ($subject=~/\w/) { $subject=&mt('Grading Feedback'); } + $subject.=' ['.&Apache::lonnet::declutter($url).']'; my (@msgnum) = split(/,/,$includemsg); foreach (@msgnum) { $message.=$env{'form.'.$_} if ($_ =~ /savemsg|newmsg/ && $_ ne ''); @@ -2080,8 +2140,8 @@ sub processHandGrade { "?symb=$symb\">$env{'form.probTitle'}</a>"; } $msgstatus = &Apache::lonmsg::user_normal_msg($uname,$udom, - $subject.' ['. - &Apache::lonnet::declutter($url).']',$message); + $subject, + $message); $request->print('<br />'.&mt('Sending message to [_1]@[_2]',$uname,$udom).': '. $msgstatus); } @@ -2094,11 +2154,11 @@ sub processHandGrade { &saveHandGrade($request,$symb,$collaborator,$udom,$ctr, $env{'form.unamedom'.$ctr},$part); if ($errorflag eq 'not_allowed') { - $request->print("<font color=\"red\">Not allowed to modify grades for $collaborator:$udom</font>"); + $request->print("<span class=\"LC_error\">".&mt('Not allowed to modify grades for [_1]',"$collaborator:$udom")."</span>"); next; } else { if ($message ne '') { - $msgstatus = &Apache::lonmsg::user_normal_msg($collaborator,$udom,$env{'form.msgsub'},$message); + $msgstatus = &Apache::lonmsg::user_normal_msg($collaborator,$udom,$subject,$message); } } } @@ -2290,7 +2350,7 @@ sub saveHandGrade { if (exists($record{'resource.'.$new_part.'.awarded'})) { $newrecord{'resource.'.$new_part.'.awarded'} = ''; } - $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}"; + $newrecord{'resource.'.$new_part.'.regrader'}="$env{'user.name'}:$env{'user.domain'}"; } } elsif ($dropMenu eq 'reset status' && exists($record{'resource.'.$new_part.'.solved'})) { #don't bother if no old records -> no attempts @@ -2326,6 +2386,7 @@ sub saveHandGrade { my $partial= $pts/$wgt; if ($partial eq $record{'resource.'.$new_part.'.awarded'}) { #do not update score for part if not changed. + &handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord); next; } else { push @parts_graded, $new_part; @@ -2349,7 +2410,6 @@ sub saveHandGrade { } $newrecord{'resource.'.$new_part.'.regrader'}= "$env{'user.name'}:$env{'user.domain'}"; - &handback_files($request,$symb,$stuname,$domain,$newflg,$new_part,\%newrecord); } # unless problem has been graded, set flag to version the submitted files unless ($record{'resource.'.$new_part.'.solved'} =~ /^correct_/ || @@ -2364,8 +2424,13 @@ sub saveHandGrade { if (%newrecord) { if (@version_parts) { - my @changed_keys = &version_portfiles(\%record, \@parts_graded, $env{'request.course.id'}, $symb, $domain, $stuname, \@version_parts); + my @changed_keys = &version_portfiles(\%record, \@parts_graded, + $env{'request.course.id'}, $symb, $domain, $stuname, \@version_parts); @newrecord{@changed_keys} = @record{@changed_keys}; + foreach my $new_part (@version_parts) { + &handback_files($request,$symb,$stuname,$domain,$newflg, + $new_part,\%newrecord); + } } &Apache::lonnet::cstore(\%newrecord,$symb, $env{'request.course.id'},$domain,$stuname); @@ -2390,40 +2455,60 @@ sub saveHandGrade { sub handback_files { my ($request,$symb,$stuname,$domain,$newflg,$new_part,$newrecord) = @_; - my $portfolio_root = &Apache::loncommon::propath($domain, - $stuname). - '/userfiles/portfolio'; - my ($partlist,$handgrade,$responseType) = &response_type($symb); - foreach my $part_resp (sort(keys(%$handgrade))) { - my ($part_id, $resp_id) = split(/_/,$part_resp); + my $portfolio_root = &propath($domain,$stuname).'/userfiles/portfolio'; + my ($partlist,$handgrade,$responseType) = &response_type($symb); + + my @part_response_id = &flatten_responseType($responseType); + foreach my $part_response_id (@part_response_id) { + my ($part_id,$resp_id) = @{ $part_response_id }; + my $part_resp = join('_',@{ $part_response_id }); if (($env{'form.'.$newflg.'_'.$part_resp.'_returndoc1'}) && ($new_part == $part_id)) { # if multiple files are uploaded names will be 'returndoc2','returndoc3' my $file_counter = 1; + my $file_msg; while ($env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter}) { my $fname=$env{'form.'.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'.filename'}; my ($directory,$answer_file) = ($env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter} =~ /^(.*?)([^\/]*)$/); my ($answer_name,$answer_ver,$answer_ext) = &file_name_version_ext($answer_file); - $directory =~ /^.+$stuname\/portfolio(.*)/; - my $portfolio_path = $1; + my ($portfolio_path) = ($directory =~ /^.+$stuname\/portfolio(.*)/); my @dir_list = &Apache::lonnet::dirlist($portfolio_path,$domain,$stuname,$portfolio_root); my $version = &get_next_version($answer_name, $answer_ext, \@dir_list); - my $new_answer = &version_selected_portfile($domain, $stuname, $portfolio_path, $answer_file, $version); - $$newrecord{"resource.$new_part.$resp_id.handback"} = $new_answer; - # set the filename to match the submitted file name - $env{'form.'.$newflg.'_'.$part_resp.'_returndoc1.filename'} = $env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter}; - my $result=&Apache::lonnet::userfileupload($newflg.'_'.$part_resp.'_returndoc'.$file_counter,'', - 'portfolio',undef,undef,undef,$stuname,$domain); + # fix file name + my ($save_file_name) = (($directory.$answer_name.".$version.".$answer_ext) =~ /^.+\/${stuname}\/(.*)/); + my $result=&Apache::lonnet::finishuserfileupload($stuname,$domain, + $newflg.'_'.$part_resp.'_returndoc'.$file_counter, + $save_file_name); if ($result !~ m|^/uploaded/|) { $request->print('<font color="red"> An errror occured ('.$result. - ') while trying to upload '.&display_file().'</font><br />'); - # $request->print(&done('Back')); + ') while trying to upload '.$newflg.'_'.$part_resp.'_returndoc'.$file_counter.'</font><br />'); + } else { + # mark the file as read only + my @files = ($save_file_name); + my @what = ($symb,$env{'request.course.id'},'handback'); + &Apache::lonnet::mark_as_readonly($domain,$stuname,\@files,\@what); + if (exists($$newrecord{"resource.$new_part.$resp_id.handback"})) { + $$newrecord{"resource.$new_part.$resp_id.handback"}.=','; + } + $$newrecord{"resource.$new_part.$resp_id.handback"} .= $save_file_name; + $file_msg.= "\n".'<br /><span class="LC_filename"><a href="/uploaded/'."$domain/$stuname/".$save_file_name.'">'.$save_file_name."</a></span><br />"; + } $request->print("<br />".$fname." will be the uploaded file name"); - $request->print("<font color=\"red\">Will upload document </font>".$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter}); + $request->print(" ".$env{'form.'.$newflg.'_'.$part_resp.'_origdoc'.$file_counter}); $file_counter++; } + my $subject = "File Handed Back by Instructor "; + my $message = "A file has been returned that was originally submitted in reponse to: <br />"; + $message .= "<strong>".&Apache::lonnet::gettitle($symb)."</strong><br />"; + $message .= ' The returned file(s) are named: '. $file_msg; + $message .= " and can be found in your portfolio space."; + my $url = (&Apache::lonnet::decode_symb($symb))[2]; + $url = &Apache::lonnet::declutter($url); + my $msgstatus = &Apache::lonmsg::user_normal_msg($stuname,$domain, + $subject.' (File Returned) ['.$url.']',$message); + } } return; @@ -2509,14 +2594,13 @@ sub version_portfiles { my $version_parts = join('|',@$v_flag); my @returned_keys; my $parts = join('|', @$parts_graded); - my $portfolio_root = &Apache::loncommon::propath($domain, - $stu_name). - '/userfiles/portfolio'; + my $portfolio_root = &propath($domain,$stu_name). + '/userfiles/portfolio'; foreach my $key (keys(%$record)) { my $new_portfiles; if ($key =~ /^resource\.($version_parts)\./ && $key =~ /\.portfiles$/ ) { my @versioned_portfiles; - my @portfiles = split(/,/,$$record{$key}); + my @portfiles = split(/\s*,\s*/,$$record{$key}); foreach my $file (@portfiles) { &Apache::lonnet::unmark_as_readonly($domain,$stu_name,[$symb,$env{'request.course.id'}],$file); my ($directory,$answer_file) =($file =~ /^(.*?)([^\/]*)$/); @@ -2528,7 +2612,7 @@ sub version_portfiles { if ($new_answer ne 'problem getting file') { push(@versioned_portfiles, $directory.$new_answer); &Apache::lonnet::mark_as_readonly($domain,$stu_name, - ['/portfolio'.$directory.$new_answer], + [$directory.$new_answer], [$symb,$env{'request.course.id'},'graded']); } } @@ -2810,16 +2894,18 @@ sub viewgrades { '<table border=0><tr bgcolor="#ffffdd"><td>'; #radio buttons/text box for assigning points for a section or class. #handles different parts of a problem - my ($partlist,$handgrade) = &response_type($symb); + my ($partlist,$handgrade,$responseType) = &response_type($symb); my %weight = (); my $ctsparts = 0; $result.='<table border="0">'; my %seen = (); - for (sort keys(%$handgrade)) { - my ($partid,$respid) = split (/_/,$_,2); + my @part_response_id = &flatten_responseType($responseType); + foreach my $part_response_id (@part_response_id) { + my ($partid,$respid) = @{ $part_response_id }; + my $part_resp = join('_',@{ $part_response_id }); next if $seen{$partid}; $seen{$partid}++; - my $handgrade=$$handgrade{$_}; + my $handgrade=$$handgrade{$part_resp}; my $wgt = &Apache::lonnet::EXT('resource.'.$partid.'.weight',$symb); $weight{$partid} = $wgt eq '' ? '1' : $wgt; @@ -3378,9 +3464,10 @@ sub upcsvScores_form { $result.=$table; $result.='<br /><table width="100%" border="0"><tr><td bgcolor="#777777">'."\n"; $result.='<table width="100%" border="0"><tr bgcolor="#e6ffff"><td>'."\n"; - $result.=' <b>Specify a file containing the class scores for current resource'. + $result.=' <b>'.&mt('Specify a file containing the class scores for current resource'). '.</b></td></tr>'."\n"; $result.='<tr bgcolor=#ffffe6><td>'."\n"; + my $upload=&mt("Upload Scores"); my $upfile_select=&Apache::loncommon::upfile_select_html(); my $ignore=&mt('Ignore First Line'); $result.=<<ENDUPFORM; @@ -3390,11 +3477,13 @@ sub upcsvScores_form { <input type="hidden" name="probTitle" value="$env{'form.probTitle'}" /> <input type="hidden" name="saveState" value="$env{'form.saveState'}" /> $upfile_select -<br /><input type="button" onClick="javascript:checkUpload(this.form);" value="Upload Scores" /> +<br /><input type="button" onClick="javascript:checkUpload(this.form);" value="$upload" /> <label><input type="checkbox" name="noFirstLine" />$ignore</label> </form> ENDUPFORM - $result.='</td></tr></table>'."\n"; + $result.=&Apache::loncommon::help_open_topic("Course_Convert_To_CSV", + &mt("How do I create a CSV file from a spreadsheet")) + .'</td></tr></table>'."\n"; $result.='</td></tr></table><br /><br />'."\n"; $result.=&show_grading_menu_form($symb); return $result; @@ -4182,7 +4271,7 @@ sub scantron_filenames { my $cdom=$env{'course.'.$env{'request.course.id'}.'.domain'}; my $cname=$env{'course.'.$env{'request.course.id'}.'.num'}; my @files=&Apache::lonnet::dirlist('userfiles',$cdom,$cname, - &Apache::loncommon::propath($cdom,$cname)); + &propath($cdom,$cname)); my @possiblenames; foreach my $filename (sort(@files)) { ($filename)=split(/&/,$filename); @@ -4541,7 +4630,8 @@ sub scantron_parse_scanline { substr($questions,0,$$scantron_config{'Qlength'})=''; if (length($currentquest) < $$scantron_config{'Qlength'}) { next; } if ($$scantron_config{'Qon'} eq 'letter') { - if ($currentquest eq '?') { + if ($currentquest eq '?' + || $currentquest eq '*') { push(@{$record{'scantron.doubleerror'}},$questnum); $record{"scantron.$questnum.answer"}=''; } elsif (!$currentquest @@ -4555,7 +4645,8 @@ sub scantron_parse_scanline { $record{"scantron.$questnum.answer"}=$currentquest; } } elsif ($$scantron_config{'Qon'} eq 'number') { - if ($currentquest eq '?') { + if ($currentquest eq '?' + || $currentquest eq '*') { push(@{$record{'scantron.doubleerror'}},$questnum); $record{"scantron.$questnum.answer"}=''; } elsif (!$currentquest @@ -4566,8 +4657,14 @@ sub scantron_parse_scanline { push(@{$record{"scantron.missingerror"}},$questnum); } } else { - $record{"scantron.$questnum.answer"}= - $alphabet[$currentquest-1]; + # wrap zero back to J + if ($currentquest eq '0') { + $record{"scantron.$questnum.answer"}= + $alphabet[9]; + } else { + $record{"scantron.$questnum.answer"}= + $alphabet[$currentquest-1]; + } } } else { my @array=split($$scantron_config{'Qon'},$currentquest,-1); @@ -4738,15 +4835,15 @@ sub scantron_warning_screen { my ($button_text)=@_; my $title=&Apache::lonnet::gettitle($env{'form.selectpage'}); my %scantron_config=&get_scantron_config($env{'form.scantron_format'}); - my $CODElist="a"; + my $CODElist; if ($scantron_config{'CODElocation'} && $scantron_config{'CODEstart'} && $scantron_config{'CODElength'}) { $CODElist=$env{'form.scantron_CODElist'}; - if ($CODElist eq '') { $CODElist='<font color="red">None</font>'; } + if ($env{'form.scantron_CODElist'} eq '') { $CODElist='<font color="red">None</font>'; } $CODElist= '<tr><td><b>List of CODES to validate against:</b></td><td><tt>'. - $CODElist.'</tt></td></tr>'; + $env{'form.scantron_CODElist'}.'</tt></td></tr>'; } return (<<STUFF); <p> @@ -4794,7 +4891,7 @@ $warning <input type="hidden" name="command" value="scantron_validate" /> STUFF } - $r->print("</form><br />".&show_grading_menu_form($symb)."</body></html>"); + $r->print("</form><br />".&show_grading_menu_form($symb)); return ''; } @@ -4895,8 +4992,7 @@ STUFF $r->print(" this scanline saving it for later."); } } - $r->print(" </form><br />".&show_grading_menu_form($symb). - "</body></html>"); + $r->print(" </form><br />".&show_grading_menu_form($symb)); return ''; } @@ -5057,7 +5153,7 @@ sub scantron_validate_sequence { my @resources= $navmap->retrieveResources($map,\&scantron_filter_not_exam,1,0); if (@resources) { - $r->print("<p>".&mt('Some resource in the sequece currently are not set to exam mode. Grading these resources currently may not work correctly.')."</p>"); + $r->print("<p>".&mt('Some resources in the sequence currently are not set to exam mode. Grading these resources currently may not work correctly.')."</p>"); return (1,$currentphase); } } @@ -5211,11 +5307,11 @@ function change_radio(field) { </script> ENDSCRIPT my $href="/adm/pickcode?". - "form=".&Apache::lonnet::escape("scantronupload"). - "&scantron_format=".&Apache::lonnet::escape($env{'form.scantron_format'}). - "&scantron_CODElist=".&Apache::lonnet::escape($env{'form.scantron_CODElist'}). - "&curCODE=".&Apache::lonnet::escape($$scan_record{'scantron.CODE'}). - "&scantron_selectfile=".&Apache::lonnet::escape($env{'form.scantron_selectfile'}); + "form=".&escape("scantronupload"). + "&scantron_format=".&escape($env{'form.scantron_format'}). + "&scantron_CODElist=".&escape($env{'form.scantron_CODElist'}). + "&curCODE=".&escape($$scan_record{'scantron.CODE'}). + "&scantron_selectfile=".&escape($env{'form.scantron_selectfile'}); if ($env{'form.scantron_CODElist'} =~ /\S/) { $r->print("<label><input type='radio' name='scantron_CODE_resolution' value='use_found' /> <a target='_blank' href='$href'>Select</a> a CODE from the list of all CODEs and use it.</label> Selected CODE is <input readonly='true' type='text' size='8' name='scantron_CODE_selectedvalue' onfocus=\"javascript:change_radio('use_found')\" onchange=\"javascript:change_radio('use_found')\" />"); $r->print("\n<br />"); @@ -5899,7 +5995,7 @@ sub handler { if ($#commands > 0) { &Apache::lonnet::logthis("grades got multiple commands ".join(':',@commands)); } - &send_header($request); + $request->print(&Apache::loncommon::start_page('Grading')); if ($symb eq '' && $command eq '') { if ($env{'user.adv'}) { if (($env{'form.codeone'}) && ($env{'form.codetwo'}) && @@ -5990,20 +6086,8 @@ sub handler { $request->print("Access Denied ($command)"); } } - &send_footer($request); - return ''; -} - -sub send_header { - my ($request)= @_; - &Apache::lontexconvert::init_tth(); - $request->print(&Apache::loncommon::start_page('Grading')); - $request->rflush(); -} - -sub send_footer { - my ($request)= @_; $request->print(&Apache::loncommon::end_page()); + return ''; } 1;