diff --git a/lib/sympto/index.js b/lib/sympto/index.js index 6a9f66a31788e76fcf076ae05b0fc602d3a01035..36f8e5e69402cfe20cf776fd0cf7c496ecc7311e 100644 --- a/lib/sympto/index.js +++ b/lib/sympto/index.js @@ -4,18 +4,20 @@ import getPreOvulatoryPhase from './pre-ovulatory' import { LocalDate } from 'js-joda' import assert from 'assert' -export default function ({ cycle, previousCycle }) { - throwIfArgsAreNotInRequiredFormat(cycle, previousCycle) +export default function ({ cycle, previousCycles = [] }) { + throwIfArgsAreNotInRequiredFormat(cycle, previousCycles) const status = { assumeFertility: true, phases: {} } + // TODO handle no previous cycles // if there was no first higher measurement in the previous cycle, // no infertile pre-ovulatory phase may be assumed - if (getTemperatureShift(previousCycle).detected && !cycle[0].mucus) { - status.phases.preOvulatory = getPreOvulatoryPhase(cycle) + const lastCycle = previousCycles[previousCycles.length - 1] + if (getTemperatureShift(lastCycle).detected && !cycle[0].mucus) { + status.phases.preOvulatory = getPreOvulatoryPhase(cycle, previousCycles) if (status.phases.preOvulatory.cycleDays.length === cycle.length) { status.assumeFertility = false return status @@ -70,9 +72,10 @@ export default function ({ cycle, previousCycle }) { return status } -function throwIfArgsAreNotInRequiredFormat(cycle, previousCycle) { - [cycle, previousCycle].forEach(cycle => { +function throwIfArgsAreNotInRequiredFormat(cycle, previousCycles) { + [cycle, ...previousCycles].forEach(cycle => { assert.ok(Array.isArray(cycle)) + // TODO handle case of no previous cycles assert.ok(cycle.length > 0) assert.equal(typeof cycle[0].bleeding, 'object') assert.equal(typeof cycle[0].bleeding.value, 'number') @@ -81,6 +84,8 @@ function throwIfArgsAreNotInRequiredFormat(cycle, previousCycle) { assert.doesNotThrow(() => LocalDate.parse(day.date)) if (day.temperature) assert.equal(typeof day.temperature.value, 'number') if (day.mucus) assert.equal(typeof day.mucus.value, 'number') + if (day.mucus) assert.ok(day.mucus.value >= 0) + if (day.mucus) assert.ok(day.mucus.value < 5) }) }) } \ No newline at end of file diff --git a/lib/sympto/pre-ovulatory.js b/lib/sympto/pre-ovulatory.js index 8cc86b9d518ff93998ffb60a0072d616a2be6122..5749e1a2908ce421550df0d2783f0af194c776d9 100644 --- a/lib/sympto/pre-ovulatory.js +++ b/lib/sympto/pre-ovulatory.js @@ -1,13 +1,20 @@ import { LocalDate } from "js-joda" -export default function(cycle) { +export default function(cycle, previousCycles) { + // TODO handle no previous cycles + let preOvuPhaseLength = 5 + + //TODO make sure it handles weird cases like fhm < 9 + const minus8DayRuleResult = apply8DayRule(previousCycles) + if (minus8DayRuleResult) preOvuPhaseLength = minus8DayRuleResult + const startDate = LocalDate.parse(cycle[0].date) - const fiveDayEndDate = startDate.plusDays(4).toString() - const fiveDayRuleDays = cycle.slice(0, 5).filter(d => d.date <= fiveDayEndDate) - const preOvulatoryDays = getDaysUntilFertileMucus(fiveDayRuleDays) + const preOvuPhaseEndDate = startDate.plusDays(preOvuPhaseLength - 1).toString() + const maybePreOvuDays = cycle.slice(0, 5).filter(d => d.date <= preOvuPhaseEndDate) + const preOvulatoryDays = getDaysUntilFertileMucus(maybePreOvuDays) let endDate - if (preOvulatoryDays.length === fiveDayRuleDays.length) { - endDate = fiveDayEndDate + if (preOvulatoryDays.length === maybePreOvuDays.length) { + endDate = preOvuPhaseEndDate } else { endDate = preOvulatoryDays[preOvulatoryDays.length - 1].date } @@ -29,4 +36,6 @@ function getDaysUntilFertileMucus(days) { return days.slice(0, firstFertileMucusDayIndex) } return days -} \ No newline at end of file +} + +function apply8DayRule() {} \ No newline at end of file diff --git a/test/sympto/index.spec.js b/test/sympto/index.spec.js index ebc0106fada7fcf39c8569bd12641e6f735829d7..eaf1e5c62ed9760c2df46b856bd891d7e7fe03ea 100644 --- a/test/sympto/index.spec.js +++ b/test/sympto/index.spec.js @@ -23,7 +23,7 @@ describe('sympto', () => { it('with no shifts detects only peri-ovulatory', function () { const status = getSensiplanStatus({ cycle: cycleWithoutAnyShifts, - previousCycle: cycleWithoutTempShift + previousCycles: [cycleWithoutTempShift] }) expect(status).to.eql({ @@ -40,7 +40,7 @@ describe('sympto', () => { it('with shifts detects only peri-ovulatory and post-ovulatory', function () { const status = getSensiplanStatus({ cycle: cycleWithTempAndMucusShift, - previousCycle: cycleWithoutTempShift + previousCycles: [cycleWithoutTempShift] }) expect(status.temperatureShift).to.be.an('object') @@ -66,7 +66,7 @@ describe('sympto', () => { it('according to 5-day-rule', function () { const status = getSensiplanStatus({ cycle: fiveDayCycle, - previousCycle: cycleWithTempShift + previousCycles: [cycleWithTempShift] }) expect(Object.keys(status.phases).length).to.eql(1) @@ -83,7 +83,7 @@ describe('sympto', () => { it('according to 5-day-rule', function () { const status = getSensiplanStatus({ cycle: cycleWithTempAndNoMucusShift, - previousCycle: cycleWithTempShift + previousCycles: [cycleWithTempShift] }) expect(Object.keys(status.phases).length).to.eql(2) @@ -101,7 +101,7 @@ describe('sympto', () => { it('according to 5-day-rule with shortened pre-phase', function () { const status = getSensiplanStatus({ cycle: cycleWithEarlyMucus, - previousCycle: cycleWithTempShift + previousCycles: [cycleWithTempShift] }) expect(Object.keys(status.phases).length).to.eql(2) @@ -121,7 +121,7 @@ describe('sympto', () => { it('according to 5-day-rule', function () { const status = getSensiplanStatus({ cycle: cycleWithTempAndMucusShift, - previousCycle: cycleWithTempShift + previousCycles: [cycleWithTempShift] }) expect(Object.keys(status.phases).length).to.eql(3) @@ -149,7 +149,7 @@ describe('sympto', () => { it('with fhM + mucus peak on same day finds correct start of post-ovu phase', () => { const status = getSensiplanStatus({ cycle: mucusPeakAndFhmOnSameDay, - previousCycle: cycleWithTempShift + previousCycles: [cycleWithTempShift] }) expect(status.temperatureShift).to.be.an('object') @@ -180,7 +180,7 @@ describe('sympto', () => { it('with fhM 2 days before mucus peak waits for end of mucus eval', () => { const status = getSensiplanStatus({ cycle: fhmTwoDaysBeforeMucusPeak, - previousCycle: cycleWithTempShift + previousCycles: [cycleWithTempShift] }) expect(status.temperatureShift).to.be.an('object') @@ -211,7 +211,7 @@ describe('sympto', () => { it('with another mucus peak 5 days after fHM ignores it', () => { const status = getSensiplanStatus({ cycle: mucusPeak5DaysAfterFhm, - previousCycle: cycleWithTempShift + previousCycles: [cycleWithTempShift] }) expect(status.temperatureShift).to.be.an('object') @@ -242,7 +242,7 @@ describe('sympto', () => { it('with mucus peak 2 days before fhM waits for end of temp eval', () => { const status = getSensiplanStatus({ cycle: mucusPeakTwoDaysBeforeFhm, - previousCycle: cycleWithTempShift + previousCycles: [cycleWithTempShift] }) expect(status.temperatureShift).to.be.an('object') @@ -273,7 +273,7 @@ describe('sympto', () => { it('with mucus peak 5 days before fhM waits for end of temp eval', () => { const status = getSensiplanStatus({ cycle: fhm5DaysAfterMucusPeak, - previousCycle: cycleWithTempShift + previousCycles: [cycleWithTempShift] }) expect(status.temperatureShift).to.be.an('object') @@ -302,6 +302,15 @@ describe('sympto', () => { }) }) + describe('applying the minus-8 rule', () => { + it('shortens the pre-ovu phase if there is a previous <13 fhm') + it('shortens the pre-ovu phase if there is a previous <13 fhm with less than 12 cycles') + it('shortens the pre-ovu phase if mucus occurs') + it('lengthens the pre-ovu phase if >= 12 cycles') + it('does not lengthen the pre-ovu phase if < 12 cycles') + it('does not lengthen the pre-ovu phase if < 12 cycles') + }) + describe('when args are wrong', () => { it('throws when arg object is not in right format', () => { const wrongObject = { hello: 'world' } @@ -316,10 +325,10 @@ describe('sympto', () => { hello: 'world', bleeding: { value: 0 } }], - previousCycle: [{ + previousCycles: [[{ date: '1992-09-09', bleeding: { value: 0 } - }] + }]] })).to.throw(AssertionError) expect(() => getSensiplanStatus({ cycle: [{ @@ -327,20 +336,20 @@ describe('sympto', () => { temperature: {value: '35'}, bleeding: { value: 0 } }], - previousCycle: [{ + previousCycles: [[{ date: '1992-09-09', bleeding: { value: 0 } - }] + }]] })).to.throw(AssertionError) expect(() => getSensiplanStatus({ cycle: [{ date: '09-14-2017', bleeding: { value: 0 } }], - previousCycle: [{ + previousCycles: [[{ date: '1992-09-09', bleeding: { value: 0 } - }] + }]] })).to.throw(AssertionError) }) it('throws if first cycle day does not have bleeding value', () => { @@ -351,11 +360,11 @@ describe('sympto', () => { value: 'medium' } }], - previousCycle: [ + previousCycles: [[ { date: '2017-09-23', } - ] + ]] })).to.throw(AssertionError) }) })