diff --git a/lib/cycle.js b/lib/cycle.js index b5a1e21..f60a74f 100644 --- a/lib/cycle.js +++ b/lib/cycle.js @@ -1,10 +1,9 @@ import * as joda from 'js-joda' - const LocalDate = joda.LocalDate + export default function config(opts) { let bleedingDaysSortedByDate - let temperatureDaysSortedByDate let cycleDaysSortedByDate let maxBreakInBleeding @@ -12,12 +11,10 @@ export default function config(opts) { // we only want to require (and run) the db module // when not running the tests bleedingDaysSortedByDate = require('../db').bleedingDaysSortedByDate - temperatureDaysSortedByDate = require('../db').temperatureDaysSortedByDate cycleDaysSortedByDate = require('../db').cycleDaysSortedByDate maxBreakInBleeding = 1 } else { bleedingDaysSortedByDate = opts.bleedingDaysSortedByDate || [] - temperatureDaysSortedByDate = opts.temperatureDaysSortedByDate || [] cycleDaysSortedByDate = opts.cycleDaysSortedByDate || [] maxBreakInBleeding = opts.maxBreakInBleeding || 1 } @@ -38,20 +35,44 @@ export default function config(opts) { ) }) - if (firstBleedingDayBeforeTargetDayIndex < 0) return null + if (firstBleedingDayBeforeTargetDayIndex < 0) { + withWrappedDates.forEach(day => delete day.wrappedDate) + return null + } + const previousBleedingDays = withWrappedDates.slice(firstBleedingDayBeforeTargetDayIndex) - const lastPeriodStart = previousBleedingDays.find((day, i) => { + const lastMensesStart = previousBleedingDays.find((day, i) => { return thereIsNoPreviousBleedingDayWithinTheThreshold(day, previousBleedingDays.slice(i + 1)) }) function thereIsNoPreviousBleedingDayWithinTheThreshold(bleedingDay, previousBleedingDays) { const periodThreshold = bleedingDay.wrappedDate.minusDays(maxBreakInBleeding + 1) - return !previousBleedingDays.some(({ wrappedDate }) => wrappedDate.equals(periodThreshold) || wrappedDate.isAfter(periodThreshold)) + return !previousBleedingDays.some(({ wrappedDate }) => { + return wrappedDate.equals(periodThreshold) || wrappedDate.isAfter(periodThreshold) + }) } withWrappedDates.forEach(day => delete day.wrappedDate) - return lastPeriodStart + return lastMensesStart + } + + function getFollowingMensesStart(targetDateString) { + const targetDate = LocalDate.parse(targetDateString) + const withWrappedDates = bleedingDaysSortedByDate + .filter(day => !day.bleeding.exclude) + .map(day => { + day.wrappedDate = LocalDate.parse(day.date) + return day + }) + + const firstBleedingDayAfterTargetDay = withWrappedDates.reverse().find(day => { + return day.wrappedDate.isAfter(targetDate) + }) + + withWrappedDates.forEach(day => delete day.wrappedDate) + + return firstBleedingDayAfterTargetDay } function getCycleDayNumber(targetDateString) { @@ -65,47 +86,44 @@ export default function config(opts) { return diffInDays + 1 } - function getPreviousTemperaturesInCycle(targetDateString, lastMensesStart) { - const startIndex = temperatureDaysSortedByDate.findIndex(day => day.date <= targetDateString) - const previousTemperaturesInCycle = temperatureDaysSortedByDate.slice(startIndex) - const endIndex = previousTemperaturesInCycle.findIndex(day => day.date < lastMensesStart.date) - return previousTemperaturesInCycle - .slice(0, endIndex) - .map(day => day.temperature.value) + function getCyclesBefore(targetCycleStartDay) { + return collectPreviousCycles([], targetCycleStartDay.date) } - function getCycleDaysBeforeDay(targetDateString) { - const firstCycleDay = getLastMensesStart(targetDateString) - if (!firstCycleDay) return null - return cycleDaysSortedByDate.filter(({date}) => { - return date >= firstCycleDay.date && date <= targetDateString - }) + function collectPreviousCycles(acc, startOfFollowingCycle) { + const cycle = getPreviousCycle(startOfFollowingCycle) + if (!cycle || !cycle.length) return acc + acc.push(cycle) + return collectPreviousCycles(acc, cycle[cycle.length - 1].date) } - function getPreviousCycles(targetCycleStartDay) { - let previousCycleStartIndex = cycleDaysSortedByDate.indexOf(targetCycleStartDay) - const cycles = [] - while (previousCycleStartIndex < cycleDaysSortedByDate.length - 1) { - const prevDate = cycleDaysSortedByDate[previousCycleStartIndex + 1].date - const cycleStart = getLastMensesStart(prevDate) + function getPreviousCycle(dateString) { + const startOfCycle = getLastMensesStart(dateString) + if (!startOfCycle) return null + const dateBeforeStartOfCycle = LocalDate.parse(startOfCycle.date).minusDays(1).toString() + return getCycleForDay(dateBeforeStartOfCycle) + } - if (!cycleStart) break - - const cycleStartIndex = cycleDaysSortedByDate.indexOf(cycleStart) - const lastDayInCycle = previousCycleStartIndex + 1 - const cycle = cycleDaysSortedByDate.slice(lastDayInCycle, cycleStartIndex + 1) - cycles.push(cycle) - previousCycleStartIndex = cycleStartIndex + function getCycleForDay(dayOrDate) { + const dateString = typeof dayOrDate === 'string' ? dayOrDate : dayOrDate.date + const cycleStart = getLastMensesStart(dateString) + if (!cycleStart) return null + const cycleStartIndex = cycleDaysSortedByDate.indexOf(cycleStart) + const nextMensesStart = getFollowingMensesStart(dateString) + if (nextMensesStart) { + return cycleDaysSortedByDate.slice( + cycleDaysSortedByDate.indexOf(nextMensesStart) + 1, + cycleStartIndex + 1 + ) + } else { + return cycleDaysSortedByDate.slice(0, cycleStartIndex + 1) } - - return cycles } return { getCycleDayNumber, - getLastMensesStart, - getPreviousTemperaturesInCycle, - getCycleDaysBeforeDay, - getPreviousCycles + getCycleForDay, + getPreviousCycle, + getCyclesBefore } -} +} \ No newline at end of file diff --git a/test/cycle.spec.js b/test/cycle.spec.js index 4b7858c..3b82c40 100644 --- a/test/cycle.spec.js +++ b/test/cycle.spec.js @@ -144,7 +144,7 @@ describe('getCycleDay', () => { }) }) -describe('getPreviousCycles', () => { +describe('getCyclesBefore', () => { it('gets previous cycles', () => { const cycleDaysSortedByDate = [ { @@ -185,11 +185,11 @@ describe('getPreviousCycles', () => { }, ] - const { getPreviousCycles } = cycleModule({ + const { getCyclesBefore } = cycleModule({ cycleDaysSortedByDate, bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding) }) - const result = getPreviousCycles(cycleDaysSortedByDate[0]) + const result = getCyclesBefore(cycleDaysSortedByDate[0]) expect(result.length).to.eql(3) expect(result).to.eql([ [ @@ -230,4 +230,107 @@ describe('getPreviousCycles', () => { ] ]) }) +}) + +describe('getCycleForDay', () => { + const cycleDaysSortedByDate = [ + { + date: '2018-07-05', + bleeding: { value: 2 } + }, + { + date: '2018-06-05', + bleeding: { value: 2 } + }, + { + date: '2018-05-05', + mucus: { value: 2 } + }, + { + date: '2018-05-04', + bleeding: { value: 2 } + }, + { + date: '2018-05-03', + bleeding: { value: 2 } + }, + { + date: '2018-04-05', + mucus: { value: 2 } + }, + { + date: '2018-04-04', + mucus: { value: 2 } + }, + { + date: '2018-04-03', + mucus: { value: 2 } + }, + { + date: '2018-04-02', + bleeding: { value: 2 } + }, + ] + const { getCycleForDay } = cycleModule({ + cycleDaysSortedByDate, + bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding) + }) + + it('gets cycle that has only one day', () => { + const result = getCycleForDay('2018-07-05') + expect(result.length).to.eql(1) + expect(result).to.eql([ + { + date: '2018-07-05', + bleeding: { value: 2 } + } + ]) + const result2 = getCycleForDay('2018-06-05') + expect(result2.length).to.eql(1) + expect(result2).to.eql([ + { + date: '2018-06-05', + bleeding: { value: 2 } + } + ]) + }) + + it('for later date gets cycle that has only one day', () => { + const result = getCycleForDay('2018-06-20') + expect(result.length).to.eql(1) + expect(result).to.eql([ + { + date: '2018-06-05', + bleeding: { value: 2 } + } + ]) + }) + + it('returns null if there is no cycle start for that date', () => { + const result = getCycleForDay('2018-04-01') + expect(result).to.eql(null) + }) + + it('gets cycle for day', () => { + const result = getCycleForDay('2018-04-04') + expect(result.length).to.eql(4) + expect(result).to.eql([ + { + date: '2018-04-05', + mucus: { value: 2 } + }, + { + date: '2018-04-04', + mucus: { value: 2 } + }, + { + date: '2018-04-03', + mucus: { value: 2 } + }, + { + date: '2018-04-02', + bleeding: { value: 2 } + }, + ]) + }) }) \ No newline at end of file