diff --git a/quals/basic_test.html b/quals/basic_test.html index e4020d7..e0005c2 100644 --- a/quals/basic_test.html +++ b/quals/basic_test.html @@ -35,8 +35,7 @@ showFocusRect: true, showContext: true}) //graph.loadGoal( "/home/saranli/work/privatesuite/data/searchby-bug.bb" ) - //graph.loadGoal( "/Users/dreeves/lab/privatesuite/data/searchby-bug.bb" ) - graph.loadGoal( "/quals/basic_test.bb" ) + graph.loadGoal( "../automon/data/testroad0.bb" ) diff --git a/quals/claude_quals.js b/quals/claude_quals.js index 136551d..a51d5a8 100644 --- a/quals/claude_quals.js +++ b/quals/claude_quals.js @@ -291,6 +291,8 @@ assert(bu.unaryflat([]) === false, 'unaryflat empty') assert(bu.clocky([1,2,6,9]) === 4, 'clocky([1,2,6,9])') assert(bu.clocky([1,2,6]) === 1, 'clocky odd ignores last') assert(bu.clocky([]) === 0, 'clocky empty') +assert(bu.clocky([22.5, 6]) === 7.5, 'clocky midnight crossing (#4382)') +assert(bu.clocky([23, 1]) === 2, 'clocky midnight crossing 23->1') // --- butil: mean, median, mode, trimmean --- assert(bu.mean([1,2,3]) === 2, 'mean([1,2,3])') @@ -401,6 +403,7 @@ assert(br.AGGR.nonzero([0,1]) === true, 'aggday nonzero (alias)') assert(br.AGGR.triangle([3]) === 6, 'aggday triangle 3*(3+1)/2') assert(br.AGGR.square([3]) === 9, 'aggday square 3^2') assert(br.AGGR.clocky([1,2,6,9]) === 4, 'aggday clocky') +assert(br.AGGR.clocky([22.5, 6]) === 7.5, 'aggday clocky midnight (#4382)') assert(br.AGGR.skatesum([1,2,3]) === 0, 'aggday skatesum (rsk8=0)') // --- skatesum FP consistency (github.com/beeminder/road/issues/250) --- diff --git a/src/beebrain.js b/src/beebrain.js index 1fd9815..5bc2507 100644 --- a/src/beebrain.js +++ b/src/beebrain.js @@ -636,13 +636,8 @@ function procData() { let pre = 0 // Current cumulative sum let prevpt - // HACK: aggday=skatesum needs to know rcur which we won't know until we do - // procParams. We do know rfin so we're making do with that for now... - // NB: Operation order should match fillroad's (rate/siru then *SID) to avoid - // floating point mismatch. - // br.rsk8 = gol.rfin / gol.siru * SID // daily rate for skatesum - br.rsk8 = gol.rfin * SID / gol.siru // old version for now - //br.rsk8 = br.rtf(roads, gol.asof) * SID // would this not work? + // Default rsk8 (daily rate for skatesum) from rfin as fallback + br.rsk8 = gol.rfin * SID / gol.siru // Process all datapoints for (i = 0; i <= n; i++) { @@ -652,6 +647,10 @@ function procData() { if (i >= data.length || data[i][0] != ct) { // Done recording all data for today let vlv = vl.map(dval) // extract all values for today + // Update rsk8 per-day for skatesum: use actual road rate for this day + // so weekends-off (and other road rate changes) are respected (#5451) + if (gol.aggday === "skatesum" && roads.length > 0) + br.rsk8 = br.rtf(roads, ct) * SID let ad = br.AGGR[gol.aggday](vlv) // compute aggregated value // Find previous point to record its info in the aggregated point if (newpts.length > 0) prevpt = newpts[newpts.length-1] @@ -1661,11 +1660,13 @@ function genStats(p, d, tm=null) { // after filling in road in procParams. if (bu.listy(gol.road)) gol.road.push([gol.tfin, gol.vfin, gol.rfin]) if (gol.error == "") gol.error = vetParams() - if (gol.error == "") gol.error = procData() // Extract road info into our internal format consisting of road segments: // [ [startt, startv], [endt, endv], slope, autofield ] + // NB: procRoad moved before procData so that skatesum aggregation can use + // per-day road rates (gissue #5451: weekends-off support) if (gol.error == "") gol.error = procRoad(p.road) + if (gol.error == "") gol.error = procData() if (gol.error == "") gol.error = self.reloadRoad() // does procParams here computeRosy() diff --git a/src/butil.js b/src/butil.js index 4e0b9a3..a4bd616 100644 --- a/src/butil.js +++ b/src/butil.js @@ -591,10 +591,18 @@ function unaryflat(a) { return a.some(x => x !== 0) } /** AGGDAY: Sum of differences of pairs, eg, [1,2,6,9] -> 2-1 + 9-6 = 1+3 = 4 If there's an odd number of elements then the last one is ignored. + If a pair's difference is negative, we assume the times span midnight + and add 24 hours, eg, [22.5, 6] -> (6-22.5)+24 = 7.5 + This allows e.g. a deadline of 14:00 to be used to log total time in + hours slept (see beebody gissue #4382) @param {Number[]} a Input list*/ function clocky(a) { let s = 0 - for (let i = 1; i < a.length; i += 2) s += a[i]-a[i-1] + for (let i = 1; i < a.length; i += 2) { + let d = a[i] - a[i-1] + if (d < 0) d += 24 // the pair spans midnight + s += d + } return s }