Improving cervix:

* adding tempEvalEnd value to cervix tests
* more comments in code for cervix sympto
* better description in cervix temp tests
* take out cervix.value
This commit is contained in:
emelko
2018-09-13 09:54:13 +02:00
parent 3f5c86086d
commit 76056e1db0
5 changed files with 141 additions and 56 deletions
+18 -7
View File
@@ -1,15 +1,26 @@
export default function (cycleDays, tempEvalEndIndex) { export default function (cycleDays, tempEvalEndIndex) {
const cervixDays = cycleDays.filter(day => day.cervix && !day.cervix.exclude) const notDetected = { detected: false }
const cervixDays = cycleDays
.filter(day => day.cervix && !day.cervix.exclude)
.filter(day => typeof day.cervix.opening === 'number' && typeof day.cervix.firmness === 'number')
// we search for the day of cervix peak, which must:
// * have fertile cervix values
// * be followed by at least 3 days
// these 3 following days must all show infertile cervix values
// if everything applies we must check the days until the end of temperature evaluation
// during these relevantDays no fertile cervix must occur
for (let i = 0; i < cervixDays.length; i++) { for (let i = 0; i < cervixDays.length; i++) {
const day = cervixDays[i] const day = cervixDays[i]
if (isClosedAndHard(day.cervix)) continue if (isClosedAndHard(day.cervix)) continue
// the three following days must be with closed and hard cervix
// AND no other cervix value may occur until temperature evaluation has // the three following days must be with closed and hard cervix (indicating an infertile cervix)
// been completed
const threeFollowingDays = cervixDays.slice(i + 1, i + 4) const threeFollowingDays = cervixDays.slice(i + 1, i + 4)
if (threeFollowingDays.length < 3) continue if (threeFollowingDays.length < 3) continue
// no other fertile cervix value may occur until temperature evaluation has
// been completed
const fertileCervixOccursIn3FollowingDays = threeFollowingDays.some(day => { const fertileCervixOccursIn3FollowingDays = threeFollowingDays.some(day => {
return !isClosedAndHard(day.cervix) return !isClosedAndHard(day.cervix)
}) })
@@ -33,9 +44,9 @@ export default function (cycleDays, tempEvalEndIndex) {
} }
} }
return { detected: false } return notDetected
} }
function isClosedAndHard (cervix) { function isClosedAndHard (cervixDay) {
return cervix.value.opening === 0 && cervix.value.firmness === 0 return cervixDay.opening === 0 && cervixDay.firmness === 0
} }
+4 -4
View File
@@ -115,10 +115,10 @@ function throwIfArgsAreNotInRequiredFormat(cycles) {
if (day.mucus) assert.equal(typeof day.mucus.value, 'number', "Mucus value must be a number.") if (day.mucus) assert.equal(typeof day.mucus.value, 'number', "Mucus value must be a number.")
if (day.mucus) assert.ok(day.mucus.value >= 0, "Mucus value must greater or equal to 0.") if (day.mucus) assert.ok(day.mucus.value >= 0, "Mucus value must greater or equal to 0.")
if (day.mucus) assert.ok(day.mucus.value <= 4, "Mucus value must be below 5.") if (day.mucus) assert.ok(day.mucus.value <= 4, "Mucus value must be below 5.")
if (day.cervix) assert.ok(day.cervix.value.opening >= 0, "Cervix opening value must be 0 or bigger") if (day.cervix) assert.ok(day.cervix.opening >= 0, "Cervix opening value must be 0 or bigger")
if (day.cervix) assert.ok(day.cervix.value.opening <= 2, "Cervix opening value must be 2 or smaller") if (day.cervix) assert.ok(day.cervix.opening <= 2, "Cervix opening value must be 2 or smaller")
if (day.cervix) assert.ok(day.cervix.value.firmness >= 0, "Cervix firmness value must be 0 or bigger") if (day.cervix) assert.ok(day.cervix.firmness >= 0, "Cervix firmness value must be 0 or bigger")
if (day.cervix) assert.ok(day.cervix.value.firmness <= 1, "Cervix firmness value must be 1 or smaller") if (day.cervix) assert.ok(day.cervix.firmness <= 1, "Cervix firmness value must be 1 or smaller")
assert.equal(typeof cycle[0].bleeding.value, 'number', "Bleeding value must be a number") assert.equal(typeof cycle[0].bleeding.value, 'number', "Bleeding value must be a number")
}) })
}) })
+5 -3
View File
@@ -4,8 +4,10 @@ function convertToSymptoFormat(val) {
value: val.temperature, value: val.temperature,
exclude: false exclude: false
} }
if (val.cervix) sympto.cervix = {
value: val.cervix, if (val.cervix && typeof val.cervix.opening === 'number' && typeof val.cervix.firmness === 'number') sympto.cervix = {
opening: val.cervix.opening,
firmness: val.cervix.firmness,
exclude: false exclude: false
} }
if (val.bleeding) sympto.bleeding = { if (val.bleeding) sympto.bleeding = {
@@ -130,7 +132,7 @@ export const tempShift3DaysAfterCervixShift = [
{ date: '2018-05-20', temperature: 36.7, cervix: { opening: 0, firmness: 0 } }, { date: '2018-05-20', temperature: 36.7, cervix: { opening: 0, firmness: 0 } },
{ date: '2018-05-21', temperature: 36.6, cervix: { opening: 0, firmness: 0 } }, { date: '2018-05-21', temperature: 36.6, cervix: { opening: 0, firmness: 0 } },
{ date: '2018-05-22', temperature: 36.85, cervix: { opening: 0, firmness: 0 } }, { date: '2018-05-22', temperature: 36.85, cervix: { opening: 0, firmness: 0 } },
{ date: '2018-05-23', temperature: 36.8, cervix: { opening: 0, firmness: 0 } }, { date: '2018-05-23', temperature: 36.8, cervix: { opening: 1, firmness: 0 } },
{ date: '2018-05-24', temperature: 36.85, cervix: { opening: 0, firmness: 0 } }, { date: '2018-05-24', temperature: 36.85, cervix: { opening: 0, firmness: 0 } },
{ date: '2018-05-25', temperature: 36.95, cervix: { opening: 0, firmness: 0 } }, { date: '2018-05-25', temperature: 36.95, cervix: { opening: 0, firmness: 0 } },
{ date: '2018-05-26', temperature: 36.85, cervix: { opening: 0, firmness: 1 } }, { date: '2018-05-26', temperature: 36.85, cervix: { opening: 0, firmness: 1 } },
+7 -8
View File
@@ -48,10 +48,9 @@ describe('sympto', () => {
} }
}) })
}) })
it('with temp and cervix shifts detects only peri- and post-ovulatory phases', () => { it('with temp and cervix shifts at the same day an no previous cycle detects only peri- and post-ovulatory phases', () => {
const status = getSensiplanStatus({ const status = getSensiplanStatus({
cycle: cervixShiftAndFhmOnSameDay, cycle: cervixShiftAndFhmOnSameDay,
previousCycle: cycleWithoutFhm,
secondarySymptom: 'cervix' secondarySymptom: 'cervix'
}) })
expect(Object.keys(status.phases).length).to.eql(2) expect(Object.keys(status.phases).length).to.eql(2)
@@ -72,7 +71,7 @@ describe('sympto', () => {
}) })
}) })
describe('with previous higher temp measurement', () => { describe('with previous higher temp measurement', () => {
it('with no shifts detects only peri-ovulatory in 5-day long cycle according to 5-day rule', () => { it('with no shifts in 5-day long cycle detects only peri-ovulatory according to 5-day rule', () => {
const status = getSensiplanStatus({ const status = getSensiplanStatus({
cycle: fiveDayCycle, cycle: fiveDayCycle,
previousCycle: cervixShiftAndFhmOnSameDay, previousCycle: cervixShiftAndFhmOnSameDay,
@@ -85,7 +84,7 @@ describe('sympto', () => {
end: { date: '2018-08-05' } end: { date: '2018-08-05' }
}) })
}) })
it('with no shifts detects pre- and peri-ovulatory phase according to 5-day-rule', () => { it('with no shifts in long cycle detects pre- and peri-ovulatory phase according to 5-day-rule', () => {
const status = getSensiplanStatus({ const status = getSensiplanStatus({
cycle: longCycleWithoutAnyShifts, cycle: longCycleWithoutAnyShifts,
previousCycle: cervixShiftAndFhmOnSameDay, previousCycle: cervixShiftAndFhmOnSameDay,
@@ -105,7 +104,7 @@ describe('sympto', () => {
start: { date: '2018-07-06' } start: { date: '2018-07-06' }
}) })
}) })
it('with evaluation of temperature and cervix end on same day', () => { it('with temperature and cervix evaluation end on same day detects all 3 phases', () => {
const status = getSensiplanStatus({ const status = getSensiplanStatus({
cycle: cervixShiftAndFhmOnSameDay, cycle: cervixShiftAndFhmOnSameDay,
previousCycle: cervixShiftAndFhmOnSameDay, previousCycle: cervixShiftAndFhmOnSameDay,
@@ -135,7 +134,7 @@ describe('sympto', () => {
.filter(({date}) => date >= '2018-08-15') .filter(({date}) => date >= '2018-08-15')
}) })
}) })
it('when temperature shift happens 3 days after cervix shift', () => { it('with temperature shift 3 days after cervix shift detects all 3 phases', () => {
const status = getSensiplanStatus({ const status = getSensiplanStatus({
cycle: tempShift3DaysAfterCervixShift, cycle: tempShift3DaysAfterCervixShift,
previousCycle: cervixShiftAndFhmOnSameDay, previousCycle: cervixShiftAndFhmOnSameDay,
@@ -167,7 +166,7 @@ describe('sympto', () => {
.filter(({date}) => date >= '2018-05-21') .filter(({date}) => date >= '2018-05-21')
}) })
}) })
it('when cervix shift happens 2 days after temperature shift', () => { it('with cervix shift 2 days after temperature shift detects all 3 phases', () => {
const status = getSensiplanStatus({ const status = getSensiplanStatus({
cycle: cervixShift2DaysAfterTempShift, cycle: cervixShift2DaysAfterTempShift,
previousCycle: cervixShiftAndFhmOnSameDay, previousCycle: cervixShiftAndFhmOnSameDay,
@@ -198,7 +197,7 @@ describe('sympto', () => {
start: { date: '2018-04-19', time: '18:00' } start: { date: '2018-04-19', time: '18:00' }
}) })
}) })
it('when no infertile phase can be detected', () => { it('with no shifts no ovulation is found detects only pre and peri-ovulatory phase', () => {
const status = getSensiplanStatus({ const status = getSensiplanStatus({
cycle: noOvulationDetected, cycle: noOvulationDetected,
previousCycle: cervixShiftAndFhmOnSameDay, previousCycle: cervixShiftAndFhmOnSameDay,
+107 -34
View File
@@ -5,87 +5,160 @@ const expect = chai.expect
function turnIntoCycleDayObject(value, fakeDate) { function turnIntoCycleDayObject(value, fakeDate) {
const hardAndClosed = { const hardAndClosed = {
value: { opening: 0, firmness: 0 } opening: 0,
firmness: 0
} }
const hardAndOpen = { const hardAndOpen = {
value: { opening: 1, firmness: 0 } opening: 1,
firmness: 0
} }
const softAndClosed = { const softAndClosed = {
value: { opening: 0, firmness: 1 } opening: 0,
firmness: 1
} }
const softAndOpen = { const softAndOpen = {
value: { opening: 1, firmness: 1 } opening: 1,
firmness: 1
} }
const cervixStates = [hardAndClosed, hardAndOpen, softAndClosed, softAndOpen] const cervixStates = [hardAndClosed, hardAndOpen, softAndClosed, softAndOpen]
return { return {
date: fakeDate, date: fakeDate,
cervix: cervixStates[value], cervix: {
exclude: false opening: cervixStates[value].opening,
firmness: cervixStates[value].firmness,
exclude: false
}
} }
} }
describe('sympto', () => { describe('sympto', () => {
describe('detects cervix shift', () => { describe('detects cervix shift', () => {
it('when shift happens at day 15 with consistent following days', function () { it('when shift happens at day 13 with consistent following days of infertile cervix until tempEvalEnd', () => {
const values = [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3, 1, 3, 1, 0, 0, 0, 0] const values = [0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 3, 0, 0, 0, 0, 0, 0, 0]
.map(turnIntoCycleDayObject) .map(turnIntoCycleDayObject)
const status = getCervixStatus(values) const status = getCervixStatus(values, 16)
expect(status).to.eql({ expect(status).to.eql({
detected: true, detected: true,
cervixPeakBeforeShift: { cervixPeakBeforeShift: {
date: 13, date: 10,
cervix: {value: { opening: 1, firmness: 0 }}, cervix: {
exclude: false opening: 1,
firmness: 1,
exclude: false
}
}, },
evaluationCompleteDay: { evaluationCompleteDay: {
date: 16, date: 13,
cervix: { value: { opening: 0, firmness: 0 }}, cervix: {
exclude: false opening: 0,
firmness: 0,
exclude: false
}
} }
}) })
}) })
it('at the very first day of cycle days even if later shift happens again', function () { it('right at the start of cycle days even if later shift happens again because tempEvalEnd happened before second potential shift', () => {
const values = [2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] const values = [2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
.map(turnIntoCycleDayObject) .map(turnIntoCycleDayObject)
const status = getCervixStatus(values) const status = getCervixStatus(values, 5)
expect(status).to.eql({ expect(status).to.eql({
detected: true, detected: true,
cervixPeakBeforeShift: { cervixPeakBeforeShift: {
date: 0, date: 0,
cervix: { value: { opening: 0, firmness: 1 } }, cervix: {
exclude: false opening: 0,
firmness: 1,
exclude: false
},
}, },
evaluationCompleteDay: { evaluationCompleteDay: {
date: 3, date: 3,
cervix: { value: { opening: 0, firmness: 0 } }, cervix: {
exclude: false opening: 0,
firmness: 0,
exclude: false
}
}
})
})
it('at day 6 although right at the start of cycle days a potential shift happened but because tempEvalEnd happens after second shift', () => {
const values = [2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
.map(turnIntoCycleDayObject)
const status = getCervixStatus(values, 10)
expect(status).to.eql({
detected: true,
cervixPeakBeforeShift: {
date: 6,
cervix: {
opening: 1,
firmness: 0,
exclude: false
},
},
evaluationCompleteDay: {
date: 9,
cervix: {
opening: 0,
firmness: 0,
exclude: false
}
}
})
})
it('when the cervix shift is happening after tempEvalEnd', () => {
const values = [1,1,1,1,1,2,3,3,3,3,1,1,1,1,0,0,0,0,0,0,0]
.map(turnIntoCycleDayObject)
const status = getCervixStatus(values, 10)
expect(status).to.eql({
detected: true,
cervixPeakBeforeShift: {
date: 13,
cervix: {
opening: 1,
firmness: 0,
exclude: false
}
},
evaluationCompleteDay: {
date: 16,
cervix: {
opening: 0,
firmness: 0,
exclude: false
}
} }
}) })
}) })
}) })
describe('detects no cervix shift', () => { describe('detects no cervix shift', () => {
it('if there are less than 3 days closed and hard cervix', function () { it('if there are less than 3 days closed and hard cervix', () => {
const values = [0, 0, 0, 1, 1, 1, 2, 2, 3, 3, 3, 1, 1, 1, 0, 0, 2, 0] const values = [0, 0, 0, 1, 1, 1, 2, 0, 3, 3, 3, 1, 1, 1, 0, 0, 2, 0]
.map(turnIntoCycleDayObject) .map(turnIntoCycleDayObject)
const status = getCervixStatus(values) const status = getCervixStatus(values, 15)
expect(status).to.eql({ detected: false }) expect(status).to.eql({ detected: false })
}) })
it('if there are no cervix values', function () { it('if cycleDays have not enough cervix values to detect valid cervix shift', () => {
const values = [].map(turnIntoCycleDayObject) const values = [2,0,0]
const status = getCervixStatus(values)
expect(status).to.eql({ detected: false })
})
it('when the cervix values are all the same', function () {
const values = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
.map(turnIntoCycleDayObject) .map(turnIntoCycleDayObject)
const status = getCervixStatus(values) const status = getCervixStatus(values, 17)
expect(status).to.eql({ detected: false }) expect(status).to.eql({ detected: false })
}) })
it('if no days of hard and closed cervix are tracked', function () { it('if no days indicate fertile cervix which could be cervix peak', () => {
const values = [1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1] const values = [1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1, 3, 2, 1]
.map(turnIntoCycleDayObject) .map(turnIntoCycleDayObject)
const status = getCervixStatus(values) const status = getCervixStatus(values, 12)
expect(status).to.eql({ detected: false })
})
it('if all days indicate infertile cervix values', () => {
const values = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
.map(turnIntoCycleDayObject)
const status = getCervixStatus(values, 9)
expect(status).to.eql({ detected: false })
})
it('if there are no cervix values', () => {
const values = [].map(turnIntoCycleDayObject)
const status = getCervixStatus(values, 15)
expect(status).to.eql({ detected: false }) expect(status).to.eql({ detected: false })
}) })
}) })