--- loncom/interface/lonparmset.pm 2025/06/28 14:34:46 1.622 +++ loncom/interface/lonparmset.pm 2025/06/30 21:12:21 1.624 @@ -1,7 +1,7 @@ # The LearningOnline Network with CAPA # Handler to set parameters for assessments # -# $Id: lonparmset.pm,v 1.622 2025/06/28 14:34:46 raeburn Exp $ +# $Id: lonparmset.pm,v 1.624 2025/06/30 21:12:21 raeburn Exp $ # # Copyright Michigan State University Board of Trustees # @@ -329,6 +329,7 @@ use Apache::lonnavmaps; use Apache::longroup; use Apache::lonrss; use HTML::Entities; +use POSIX qw (floor); use Text::Wrap(); use LONCAPA qw(:DEFAULT :match); @@ -1003,7 +1004,7 @@ sub valout { foreach my $item (@items) { if ($item =~ /^\d+:(0|1)\.?\d*:(0|1)$/) { my ($totalsecs,$fraction,$grad) = split(/:/,$item); - $result .= &interval_to_humanstr($totalsecs); + $result .= &grace_to_humanstr($totalsecs); if (($fraction >=0) && ($fraction <=1)) { $result .= ' | '.$fraction.' '.&mt('pts'); if ($grad == 1) { @@ -1076,6 +1077,35 @@ sub interval_to_humanstr { return ''.join(', ',@timer).''; } +sub grace_to_humanstr { + my ($totalsecs) = @_; + my @timer; + my $weeks = floor($totalsecs/604800); + $totalsecs -= $weeks*604800; + my $days = floor($totalsecs/86400); + $totalsecs -= $days*86400; + my $hours = floor($totalsecs/3600); + $totalsecs -= $hours*3600; + my $mins= floor($totalsecs/60); + $totalsecs -= $mins*60; + if ($weeks) { + push(@timer,&mt('[quant,_1,wk]',$weeks)); + } + if ($days) { + push(@timer,&mt('[quant,_1,day]',$days)); + } + if ($hours) { + push(@timer,&mt('[quant,_1,hr]',$hours)); + } + if ($mins) { + push(@timer,&mt('[quant,_1,min]',$mins)); + } + if (!@timer) { # Special case: all entries 0 -> display "0 mins" intead of empty field to keep this field editable + push(@timer,&mt('[quant,_1,min]',0)); + } + return ''.join(', ',@timer).''; +} + # Returns HTML containing a link on a parameter value, for table mode. # The link uses the javascript function 'pjump'. # @@ -1278,6 +1308,7 @@ function validateParms() { var dlExitRegExp = /^deeplink_exit_/; var dlExitTextRegExp = /^deeplink_exittext_/; var patternIP = /[\[\]\*\.a-zA-Z\d\-]+/; + var patternGrace = /^\d+:(0|1)\.?\d*:(0|1)\$/; var numelements = document.parmform.elements.length; if ((typeof(numelements) != 'undefined') && (numelements != null)) { if (numelements) { @@ -1487,29 +1518,37 @@ function validateParms() { var divElem = document.parmform.elements[i].closest('div'); var timeSels = divElem.getElementsByTagName("select"); var total = 0; + var numnotnull = 0; if (timeSels.length) { for (var j=0; j 0) && (poss <= 31)) { - total += (poss * 86400); - } - } else if (sname == 'hours_'+identifier) { - if ((poss > 0) && (poss < 24)) { - total += (poss * 3600); - } - } else if (sname == 'minutes_'+identifier) { - if ((poss > 0) && (poss < 60)) { - total += (poss * 60); - } - } else if (sname == 'seconds_'+identifier) { - if ((poss > 0) && (poss < 60)) { - total += poss; + var value = timeSels[j].options[timeSels[j].selectedIndex].value; + if ((value !== null) && (value !== '') && (value !== 'undefined')) { + numnotnull ++; + var poss = parseInt(value); + if (sname == 'weeks_'+identifier) { + if ((poss > 0) && (poss <= 52)) { + total += (poss * 604800); + } + } else if (sname == 'days_'+identifier) { + if ((poss > 0) && (poss <= 6)) { + total += (poss * 86400); + } + } else if (sname == 'hours_'+identifier) { + if ((poss > 0) && (poss < 24)) { + total += (poss * 3600); + } + } else if (sname == 'minutes_'+identifier) { + if ((poss > 0) && (poss < 60)) { + total += (poss * 60); + } } } } } + if (!numnotnull) { + total = ''; + } var inputElems = divElem.getElementsByTagName("input"); var frac = ''; var grad = ''; @@ -1519,10 +1558,13 @@ function validateParms() { if (iname == 'frac_'+identifier) { var ival = inputElems[j].value; ival.trim(); - var poss = parseFloat(ival); - if ((typeof poss === 'number') && (!isNaN(poss))) { - if ((poss => 0) && (poss <= 1)) { - frac = poss; + if ((ival != '') && (value != 'undefined')) { + var poss = parseFloat(ival); + if ((typeof poss === 'number') && (!isNaN(poss))) { + if ((poss => 0) && (poss <= 1)) { + frac = poss; + numnotnull ++; + } } } } else if (iname == 'grad_'+identifier) { @@ -1534,11 +1576,24 @@ function validateParms() { } } } - document.parmform.elements[i].value = total+':'+frac+':'+grad; - if (document.parmform.elements['set_'+identifier].value) { - document.parmform.elements['set_'+identifier].value += ','; + if (numnotnull) { + var possgrace = total+':'+frac+':'+grad; + if (patternGrace.test(possgrace)) { + document.parmform.elements[i].value = possgrace; + if (document.parmform.elements['set_'+identifier].value) { + document.parmform.elements['set_'+identifier].value += ','; + } + document.parmform.elements['set_'+identifier].value += document.parmform.elements[i].value; + } else { + if (frac == '') { + alert('Grace Period Past-Due: enter partial credit (number between 0 and 1.0).'); + return false; + } else { + alert('Grace Period Past-Due: select a number in at least one of the time past due select boxes, or delete the value for partial credit.'); + return false; + } + } } - document.parmform.elements['set_'+identifier].value += document.parmform.elements[i].value; } } } @@ -1595,13 +1650,15 @@ sub grace_js { my %lt = &grace_titles(); &js_escape(\%lt); my $overdue = '
'.$lt{'sinc'}.''; - foreach my $which (['days', 86400, 31], + foreach my $which (['weeks', 604800, 52], + ['days', 86400, 6], ['hours', 3600, 23], - ['minutes', 60, 59], - ['seconds', 1, 59]) { + ['minutes', 60, 59]) { my ($name, $factor, $max) = @{ $which }; my %select = ((map {$_ => $_} (0..$max)), 'select_form_order' => [0..$max]); + unshift(@{$select{'select_form_order'}},''); + $select{''} = ''; my $selector = &Apache::loncommon::select_form('',$name."_'+identifier+'", \%select); $selector =~ s/([\r\n\f]+)//g; @@ -1618,7 +1675,7 @@ sub grace_js { e.preventDefault(); var identifier = \$(this).closest("div").attr("id"); identifier = identifier.replace(graceRegExp,''); - \$(this).closest('div').find('.LC_string_grace_inner').append('
$overdue
$lt{scor}  
$lt{remo}
'); + \$(this).closest('div').find('.LC_string_grace_inner').append('
$overdue
$lt{pcr}  
$lt{remo}
'); }); \$(wrapper).delegate(".LC_remove_grace","click", function(e){ @@ -5298,7 +5355,6 @@ sub get_date_interval_from_form { return $seconds; } - # Returns HTML to enter a text value for a parameter. # # @param {string} $thiskey - parameter key @@ -5677,18 +5733,22 @@ sub grace_form { my %lt = &grace_titles(); my $output = '
'. '
'.$lt{'sinc'}.''; - foreach my $which (['days', 86400, 31], + foreach my $which (['weeks', 604800, 52], + ['days', 86400, 6], ['hours', 3600, 23], - ['minutes', 60, 59], - ['seconds', 1, 59]) { + ['minutes', 60, 59]) { my ($name, $factor, $max) = @{ $which }; my $amount; - if ($delta ne '') { + my %select = ((map {$_ => $_} (0..$max)), + 'select_form_order' => [0..$max]); + if ($delta eq '') { + unshift(@{$select{'select_form_order'}},''); + $select{''} = ''; + $amount = ''; + } else { $amount = int($delta/$factor); $delta %= $factor; } - my %select = ((map {$_ => $_} (0..$max)), - 'select_form_order' => [0..$max]); $output .= &Apache::loncommon::select_form($amount,$name.'_'.$thiskey, \%select,'',$readonly); $output .= ' '.$lt{$name}.'   '; @@ -5711,10 +5771,10 @@ sub grace_titles { remo => 'Remove', pcr => 'Partial credit', grad => 'gradual', + weeks => 'weeks', days => 'days', hours => 'hours', minutes => 'minutes', - seconds => 'seconds', ); } @@ -6336,7 +6396,8 @@ sub newoverview { $r->print($start_page.$breadcrumbs); &startSettingsScreen($r,'parmset',$crstype); $r->print(< +
+ ENDOVER my @ids=(); my %typep=(); @@ -6444,7 +6505,7 @@ ENDOVER &sortmenu($r,$sortorder,'newoverview'); $r->print('
'); - $r->print('

'); + $r->print('

'); # Build the list data hash from the specified parms @@ -6456,9 +6517,10 @@ ENDOVER &secgroup_lister($cat,$pschp,$parmlev,$listdata,\@psprt,\@selected_groups,\%defkeytype,\%allmaps,\@ids,\%symbp); } - if (($env{'form.store'}) || ($env{'form.dis'})) { + my $foundkeys; + if ($env{'form.newoverviewsubm'}) { - if ($env{'form.store'}) { &storedata($r,$crs,$dom); } + if ($env{'form.newoverviewsubm'} eq 'store') { &storedata($r,$crs,$dom); } # Read modified data @@ -6474,13 +6536,76 @@ ENDOVER $hash_for_realm->{$symbp{$ids[$i]}} = $i; } } - &listdata($r,$resourcedata,$listdata,$sortorder,'newoverview',undef,$readonly,$parmlev,$hash_for_realm,$pschp); + $foundkeys = &listdata($r,$resourcedata,$listdata,$sortorder,'newoverview',undef,$readonly,$parmlev,$hash_for_realm,$pschp); } $r->print(&tableend()); - unless ($readonly) { - $r->print( ((($env{'form.store'}) || ($env{'form.dis'}))?'

':'') ); + if ((!$readonly) && ($foundkeys)) { + $r->print( ($env{'form.newoverviewsubm'}? '

':'') ); } $r->print(''); + if ($env{'form.newoverviewsubm'}) { + $r->print(<<"END"); + + +END + } &endSettingsScreen($r); $r->print(&Apache::loncommon::end_page()); }