Implement chunkRangeIntervals() utility
This commit is contained in:
parent
e62359a553
commit
72a02f2d19
|
|
@ -8,7 +8,8 @@ export { Workout, Interval, Comment } from "./ast";
|
||||||
export { Duration } from "./Duration";
|
export { Duration } from "./Duration";
|
||||||
export { Intensity, ConstantIntensity, RangeIntensity, FreeIntensity } from "./Intensity";
|
export { Intensity, ConstantIntensity, RangeIntensity, FreeIntensity } from "./Intensity";
|
||||||
|
|
||||||
// stats utils
|
// utils
|
||||||
export { totalDuration } from "./stats/totalDuration";
|
export { totalDuration } from "./stats/totalDuration";
|
||||||
export { intensityToZoneIndex, ZoneIndex } from "./stats/zoneDistribution";
|
export { intensityToZoneIndex, ZoneIndex } from "./stats/zoneDistribution";
|
||||||
export { maximumIntensity } from "./stats/maximumIntensity";
|
export { maximumIntensity } from "./stats/maximumIntensity";
|
||||||
|
export { chunkRangeIntervals } from "./utils/chunkRangeIntervals";
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
import { Interval } from "../ast";
|
||||||
|
import { Duration } from "../Duration";
|
||||||
|
import { ConstantIntensity, RangeIntensity } from "../Intensity";
|
||||||
|
import { chunkRangeIntervals } from "./chunkRangeIntervals";
|
||||||
|
|
||||||
|
describe("chunkRangeIntervals()", () => {
|
||||||
|
const minute = new Duration(60);
|
||||||
|
|
||||||
|
it("does nothing with empty array", () => {
|
||||||
|
expect(chunkRangeIntervals([], minute)).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("does nothing with constant-intensity intervals", () => {
|
||||||
|
const intervals: Interval[] = [
|
||||||
|
{
|
||||||
|
type: "Interval",
|
||||||
|
duration: new Duration(2 * 60),
|
||||||
|
intensity: new ConstantIntensity(0.7),
|
||||||
|
comments: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Interval",
|
||||||
|
duration: new Duration(10 * 60),
|
||||||
|
intensity: new ConstantIntensity(1),
|
||||||
|
comments: [],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "Rest",
|
||||||
|
duration: new Duration(30),
|
||||||
|
intensity: new ConstantIntensity(0.5),
|
||||||
|
comments: [],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
expect(chunkRangeIntervals(intervals, minute)).toEqual(intervals);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("converts 1-minute range-interval to 1-minute constant-interval", () => {
|
||||||
|
expect(
|
||||||
|
chunkRangeIntervals(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: "Warmup",
|
||||||
|
duration: minute,
|
||||||
|
intensity: new RangeIntensity(0.5, 1),
|
||||||
|
comments: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
minute,
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"comments": Array [],
|
||||||
|
"duration": Duration {
|
||||||
|
"seconds": 60,
|
||||||
|
},
|
||||||
|
"intensity": ConstantIntensity {
|
||||||
|
"_value": 0.75,
|
||||||
|
},
|
||||||
|
"type": "Warmup",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("splits 3-minute range-interval to three 1-minute constant-intervals", () => {
|
||||||
|
expect(
|
||||||
|
chunkRangeIntervals(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: "Warmup",
|
||||||
|
duration: new Duration(3 * 60),
|
||||||
|
intensity: new RangeIntensity(0.5, 1),
|
||||||
|
comments: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
minute,
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"comments": Array [],
|
||||||
|
"duration": Duration {
|
||||||
|
"seconds": 60,
|
||||||
|
},
|
||||||
|
"intensity": ConstantIntensity {
|
||||||
|
"_value": 0.5833333333333334,
|
||||||
|
},
|
||||||
|
"type": "Warmup",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"comments": Array [],
|
||||||
|
"duration": Duration {
|
||||||
|
"seconds": 60,
|
||||||
|
},
|
||||||
|
"intensity": ConstantIntensity {
|
||||||
|
"_value": 0.75,
|
||||||
|
},
|
||||||
|
"type": "Warmup",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"comments": Array [],
|
||||||
|
"duration": Duration {
|
||||||
|
"seconds": 60,
|
||||||
|
},
|
||||||
|
"intensity": ConstantIntensity {
|
||||||
|
"_value": 0.9166666666666667,
|
||||||
|
},
|
||||||
|
"type": "Warmup",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("splits 1:30 range-interval to 1min & 30sec constant-intervals", () => {
|
||||||
|
expect(
|
||||||
|
chunkRangeIntervals(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
type: "Warmup",
|
||||||
|
duration: new Duration(60 + 30),
|
||||||
|
intensity: new RangeIntensity(0.5, 1),
|
||||||
|
comments: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
minute,
|
||||||
|
),
|
||||||
|
).toMatchInlineSnapshot(`
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"comments": Array [],
|
||||||
|
"duration": Duration {
|
||||||
|
"seconds": 60,
|
||||||
|
},
|
||||||
|
"intensity": ConstantIntensity {
|
||||||
|
"_value": 0.6666666666666666,
|
||||||
|
},
|
||||||
|
"type": "Warmup",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"comments": Array [],
|
||||||
|
"duration": Duration {
|
||||||
|
"seconds": 30,
|
||||||
|
},
|
||||||
|
"intensity": ConstantIntensity {
|
||||||
|
"_value": 0.9166666666666667,
|
||||||
|
},
|
||||||
|
"type": "Warmup",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { chain, curry } from "ramda";
|
||||||
|
import { Interval } from "../ast";
|
||||||
|
import { Duration } from "../Duration";
|
||||||
|
import { ConstantIntensity, Intensity, RangeIntensity } from "../Intensity";
|
||||||
|
|
||||||
|
const chunkDuration = (seconds: number, chunkSize: Duration, intervalDuration: Duration): Duration => {
|
||||||
|
return seconds + chunkSize.seconds > intervalDuration.seconds
|
||||||
|
? new Duration(intervalDuration.seconds % chunkSize.seconds)
|
||||||
|
: chunkSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
const chunkIntensity = (
|
||||||
|
startSeconds: number,
|
||||||
|
chunkSize: Duration,
|
||||||
|
{ start, end }: Intensity,
|
||||||
|
intervalDuration: Duration,
|
||||||
|
): ConstantIntensity => {
|
||||||
|
const endSeconds =
|
||||||
|
startSeconds + chunkSize.seconds > intervalDuration.seconds
|
||||||
|
? intervalDuration.seconds
|
||||||
|
: startSeconds + chunkSize.seconds;
|
||||||
|
|
||||||
|
const middleSeconds = (startSeconds + endSeconds) / 2;
|
||||||
|
|
||||||
|
return new ConstantIntensity(start + (end - start) * (middleSeconds / intervalDuration.seconds));
|
||||||
|
};
|
||||||
|
|
||||||
|
const chunkInterval = curry((chunkSize: Duration, interval: Interval): Interval[] => {
|
||||||
|
if (!(interval.intensity instanceof RangeIntensity)) {
|
||||||
|
return [interval];
|
||||||
|
}
|
||||||
|
|
||||||
|
const intervals: Interval[] = [];
|
||||||
|
for (let seconds = 0; seconds < interval.duration.seconds; seconds += chunkSize.seconds) {
|
||||||
|
intervals.push({
|
||||||
|
...interval,
|
||||||
|
duration: chunkDuration(seconds, chunkSize, interval.duration),
|
||||||
|
intensity: chunkIntensity(seconds, chunkSize, interval.intensity, interval.duration),
|
||||||
|
comments: [], // TODO: for now, ignoring comments
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return intervals;
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Breaks intervals that use RangeIntensity into multiple intervals with ConstantIntensity
|
||||||
|
*/
|
||||||
|
export const chunkRangeIntervals = (intervals: Interval[], chunkSize: Duration): Interval[] =>
|
||||||
|
chain(chunkInterval(chunkSize), intervals);
|
||||||
Loading…
Reference in New Issue