[LON-CAPA-cvs] cvs: rat /client parameter.html loncom/interface lonparmset.pm

raeburn raeburn at source.lon-capa.org
Mon Jun 30 16:29:07 EDT 2025


raeburn		Mon Jun 30 20:29:07 2025 EDT

  Modified files:              
    /loncom/interface	lonparmset.pm 
    /rat/client	parameter.html 
  Log:
  - Bug 6623
    Select boxes for grace period parameter support 0-52 weeks, 0-6 days, 
    0-23 hours, and 0-59 minutes, i.e., can be set to the nearest minute for
    up to a year.  
  
  
-------------- next part --------------
Index: loncom/interface/lonparmset.pm
diff -u loncom/interface/lonparmset.pm:1.622 loncom/interface/lonparmset.pm:1.623
--- loncom/interface/lonparmset.pm:1.622	Sat Jun 28 14:34:46 2025
+++ loncom/interface/lonparmset.pm	Mon Jun 30 20:29:03 2025
@@ -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.623 2025/06/30 20:29:03 raeburn Exp $
 #
 # Copyright Michigan State University Board of Trustees
 #
@@ -329,6 +329,7 @@
 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 @@
                 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 @@
     return '<span style="white-space:nowrap">'.join('</span>, <span style="white-space:nowrap">', at timer).'</span>';
 }
 
+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 '<span style="white-space:nowrap">'.join('</span>, <span style="white-space:nowrap">', at timer).'</span>';
+}
+
 # Returns HTML containing a link on a parameter value, for table mode.
 # The link uses the javascript function 'pjump'.
 #
@@ -1278,6 +1308,7 @@
     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 @@
                     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<timeSels.length; j++) {
                             var sname = timeSels[j].getAttribute('name');
-                            var poss = parseInt(timeSels[j].options[timeSels[j].selectedIndex].value);
-                            if (sname == 'days_'+identifier) {
-                                if ((poss > 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 @@
                             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 @@
                             }
                         }
                     }
-                    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 @@
     my %lt = &grace_titles();
     &js_escape(\%lt);
     my $overdue = '<fieldset class="LC_grace"><legend>'.$lt{'sinc'}.'</legend>';
-    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 @@
         e.preventDefault();
         var identifier = \$(this).closest("div").attr("id");
         identifier = identifier.replace(graceRegExp,'');
-        \$(this).closest('div').find('.LC_string_grace_inner').append('<div><input type="hidden" name="setgrace_'+identifier+'" value="" />$overdue<fieldset class="LC_grace"><legend>$lt{scor}</legend><input type="text" size="3" name="frac_'+identifier+'" value="" />  <label><input type="checkbox" value="1" name="grad_'+identifier+'" />$lt{grad}</label></fieldset><a href="#" class="LC_remove_grace">$lt{remo}</a></div>');
+        \$(this).closest('div').find('.LC_string_grace_inner').append('<div><input type="hidden" name="setgrace_'+identifier+'" value="" />$overdue<fieldset class="LC_grace"><legend>$lt{pcr}</legend><input type="text" size="3" name="frac_'+identifier+'" value="" />  <label><input type="checkbox" value="1" name="grad_'+identifier+'" />$lt{grad}</label></fieldset><a href="#" class="LC_remove_grace">$lt{remo}</a></div>');
     });
 
     \$(wrapper).delegate(".LC_remove_grace","click", function(e){
@@ -5298,7 +5355,6 @@
     return $seconds;
 }
 
-
 # Returns HTML to enter a text value for a parameter.
 #
 # @param {string} $thiskey - parameter key
@@ -5677,18 +5733,22 @@
     my %lt = &grace_titles();
     my $output = '<div><input type="hidden" name="setgrace_'.$thiskey.'" value="" />'.
                  '<fieldset class="LC_grace"><legend>'.$lt{'sinc'}.'</legend>';
-    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 @@
                                          remo => 'Remove',
                                          pcr => 'Partial credit',
                                          grad => 'gradual',
+                                         weeks => 'weeks',
                                          days => 'days',
                                          hours => 'hours',
                                          minutes => 'minutes',
-                                         seconds => 'seconds',
     );
 }
 
Index: rat/client/parameter.html
diff -u rat/client/parameter.html:1.96 rat/client/parameter.html:1.97
--- rat/client/parameter.html:1.96	Sat Jun 28 14:35:11 2025
+++ rat/client/parameter.html	Mon Jun 30 20:29:06 2025
@@ -5,7 +5,7 @@
 The LearningOnline Network with CAPA
 Parameter Input Window
 //
-// $Id: parameter.html,v 1.96 2025/06/28 14:35:11 raeburn Exp $
+// $Id: parameter.html,v 1.97 2025/06/30 20:29:06 raeburn Exp $
 //
 // Copyright Michigan State University Board of Trustees
 //
@@ -328,10 +328,34 @@
     }
     result += '</select>';
     return result;
-} 
+}
+
+function intweek(weeks) {
+    var thisweek;
+    if ((typeof weeks === 'number') && (!isNaN(weeks))) {
+        thisweek=weeks;
+    }
+    var i;
+    var result ='';
+    var funcname = '';
+    if (pscat == 'grace') {
+        funcname = 'parent.gracestringeval()';
+    }
+    result += '<select name="weeks" onchange="'+funcname+';">';
+    for (i=0;i<=52;i++) {
+        result += '<option value="'+i+'"';
+        if (i==thisweek) {
+            result += ' selected="selected"';
+        }
+        result += '>'+i+'</option>';
+    }
+    result += '</select>';
+    return result;
+}
 
 function intday(days) {
     var thisdate;
+    var maxallowed;
     if ((typeof days === 'number') && (!isNaN(days))) {
         thisdate=days;
     } else {
@@ -342,11 +366,13 @@
     var funcname = '';
     if (pscat == 'grace') {
         funcname = 'parent.gracestringeval()';
+        maxallowed = 6;
     } else {
         funcname = 'parent.intcalc()';
+        maxallowed = 31;
     }
     result += '<select name="date" onchange="'+funcname+';">';
-    for (i=0;i<=31;i++) {
+    for (i=0;i<=maxallowed;i++) {
         result += '<option value="'+i+'"';
         if (i==thisdate) {
             result += ' selected="selected"';
@@ -721,8 +747,12 @@
                     for (var j=0; j<timeSels.length; j++) {
                         var sname = timeSels[j].getAttribute('name');
                         var poss = parseInt(timeSels[j].options[timeSels[j].selectedIndex].value);
-                        if (sname == 'date') {
-                            if ((poss > 0) && (poss <= 31)) {
+                        if (sname == 'weeks') {
+                            if ((poss > 0) && (poss <= 52)) {
+                                total += (poss * 604800);
+                            }
+                        } else if (sname == 'date') {
+                            if ((poss > 0) && (poss < 7)) {
                                 total += (poss * 86400);
                             }
                         } else if (sname == 'hours') {
@@ -733,10 +763,6 @@
                             if ((poss > 0) && (poss < 60)) {
                                 total += (poss * 60);
                             }
-                        } else if (sname == 'seconds') {
-                            if ((poss > 0) && (poss < 60)) {
-                                total += poss;
-                            }
                         }
                     }
                 }
@@ -775,6 +801,7 @@
 }
 
 function graceitem(current) {
+    var gweeks = 0;
     var gdays = 0;
     var ghours = 0;
     var gmins = 0;
@@ -786,6 +813,8 @@
         var graceItems = new Array;
         graceItems = current.split(':');
         gsecs=graceItems[0];
+        gweeks=Math.floor(gsecs/604800);
+        gsecs -= gweeks*604800;
         gdays=Math.floor(gsecs/86400);
         gsecs -= gdays*86400;
         ghours=Math.floor(gsecs/3600);
@@ -799,10 +828,10 @@
     }
     return '<input type="hidden" name="setgrace" value="" />'+
            '<fieldset class="LC_grace"><legend>Time past due</legend>'+
-           '<span style="white-space:nowrap">'+intday(gdays)+' days </span>'+
-           '<span style="white-space:nowrap">'+inthour(ghours)+' hours</span><br />'+
-           '<span style="white-space:nowrap">'+intminute(gmins)+' mins</span>'+
-           '<span style="white-space:nowrap">'+intsecond(gsecs)+' secs</span>'+
+           '<span style="white-space:nowrap">'+intweek(gweeks)+' weeks </span>'+
+           '<span style="white-space:nowrap">'+intday(gdays)+' days </span><br />'+
+           '<span style="white-space:nowrap">'+inthour(ghours)+' hours </span>'+
+           '<span style="white-space:nowrap">'+intminute(gmins)+' mins </span>'+
            '</fieldset><fieldset class="LC_grace"><legend>Partial Credit</legend>'+
            '<input type="text" size="3" name="frac" value="'+gfrac+'" onblur="parent.gracestringeval();" />'+
            '  <label><input type="checkbox" value="1" name="grad"'+checktext+' onclick="parent.gracestringeval();" />'+


More information about the LON-CAPA-cvs mailing list