moves getCycleLength to corresponding files, adds function to get next menses (not finished) with first tests
This commit is contained in:
+2
-12
@@ -4,10 +4,10 @@ import {
|
||||
View,
|
||||
ScrollView
|
||||
} from 'react-native'
|
||||
import { LocalDate, ChronoUnit } from 'js-joda'
|
||||
|
||||
import styles from '../styles/index'
|
||||
import cycleModule from '../lib/cycle'
|
||||
import getCycleInfo from '../lib/cycle-length'
|
||||
import {getCycleLengthStats as getCycleInfo, getCycleLength} from '../lib/cycle-length'
|
||||
import {stats as labels} from './labels'
|
||||
|
||||
export default class Stats extends Component {
|
||||
@@ -57,13 +57,3 @@ export default class Stats extends Component {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function getCycleLength(cycleStartDates) {
|
||||
const cycleLengths = []
|
||||
for (let i = 0; i < cycleStartDates.length - 1; i++) {
|
||||
const nextCycleStart = LocalDate.parse(cycleStartDates[i])
|
||||
const cycleStart = LocalDate.parse(cycleStartDates[i + 1])
|
||||
cycleLengths.push(cycleStart.until(nextCycleStart, ChronoUnit.DAYS))
|
||||
}
|
||||
return cycleLengths
|
||||
}
|
||||
+13
-1
@@ -1,6 +1,8 @@
|
||||
import assert from 'assert'
|
||||
import { LocalDate, ChronoUnit } from 'js-joda'
|
||||
import cycleModule from '../lib/cycle'
|
||||
|
||||
export default function getCycleLengthStats(cycleLengths) {
|
||||
export function getCycleLengthStats(cycleLengths) {
|
||||
throwIfArgsAreNotInRequiredFormat(cycleLengths)
|
||||
const cycleLengthStats = {}
|
||||
const sortedCycleLengths = cycleLengths.sort((a, b) => {
|
||||
@@ -47,3 +49,13 @@ function throwIfArgsAreNotInRequiredFormat(cycleLengths) {
|
||||
assert.ok(!isNaN(cycleLength), 'Elements of array should not be NaN.')
|
||||
})
|
||||
}
|
||||
|
||||
export function getCycleLength(cycleStartDates) {
|
||||
const cycleLengths = []
|
||||
for (let i = 0; i < cycleStartDates.length - 1; i++) {
|
||||
const nextCycleStart = LocalDate.parse(cycleStartDates[i])
|
||||
const cycleStart = LocalDate.parse(cycleStartDates[i + 1])
|
||||
cycleLengths.push(cycleStart.until(nextCycleStart, ChronoUnit.DAYS))
|
||||
}
|
||||
return cycleLengths
|
||||
}
|
||||
+32
-1
@@ -1,4 +1,5 @@
|
||||
import * as joda from 'js-joda'
|
||||
import {getCycleLengthStats, getCycleLength} from './cycle-length'
|
||||
const LocalDate = joda.LocalDate
|
||||
const DAYS = joda.ChronoUnit.DAYS
|
||||
|
||||
@@ -143,11 +144,41 @@ export default function config(opts) {
|
||||
}
|
||||
}
|
||||
|
||||
function getPredictedMenses(maxCycleLength, minCyclesForPrediction) {
|
||||
maxCycleLength = maxCycleLength || 99
|
||||
minCyclesForPrediction = minCyclesForPrediction || 3
|
||||
const allMensesStarts = getAllMensesStarts()
|
||||
|
||||
const atLeastOneCycle = allMensesStarts.length > 1
|
||||
if (!atLeastOneCycle ||
|
||||
allMensesStarts.length < minCyclesForPrediction ||
|
||||
getCycleDayNumber(LocalDate.now().toString()) > maxCycleLength
|
||||
) {
|
||||
return {}
|
||||
}
|
||||
const cycleLengths = getCycleLength(allMensesStarts)
|
||||
const cycleInfo = getCycleLengthStats(cycleLengths)
|
||||
const periodDistance = Math.round(cycleInfo.mean)
|
||||
const periodStartVariation = (cycleInfo.stdDeviation < 1.5) ? 1 : 2 // threshold is choosen a little arbitrarily
|
||||
var lastStart = allMensesStarts[0]
|
||||
const predictedMenses = []
|
||||
for (let i = 0; i < 3; i++) {
|
||||
lastStart = LocalDate.parse(lastStart).plusDays(periodDistance).toString()
|
||||
const nextPredictedRange = {
|
||||
'startDate': LocalDate.parse(lastStart).minusDays(periodStartVariation).toString(),
|
||||
'endDate': LocalDate.parse(lastStart).plusDays(periodStartVariation).toString()
|
||||
}
|
||||
predictedMenses.push(nextPredictedRange)
|
||||
}
|
||||
return predictedMenses
|
||||
}
|
||||
|
||||
return {
|
||||
getCycleDayNumber,
|
||||
getCycleForDay,
|
||||
getPreviousCycle,
|
||||
getCyclesBefore,
|
||||
getAllMensesStarts
|
||||
getAllMensesStarts,
|
||||
getPredictedMenses
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import chai from 'chai'
|
||||
import { AssertionError } from 'assert'
|
||||
|
||||
import cycleInfo from '../lib/cycle-length'
|
||||
import {getCycleLengthStats as cycleInfo} from '../lib/cycle-length'
|
||||
|
||||
const expect = chai.expect
|
||||
|
||||
|
||||
@@ -344,3 +344,126 @@ describe('getCycleForDay', () => {
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe.only('getPredictedMenses', () => {
|
||||
describe('cannot predict next menses', () => {
|
||||
it('if no bleeding is documented', () => {
|
||||
const cycleDaysSortedByDate = [ {} ]
|
||||
|
||||
const { getPredictedMenses } = cycleModule({
|
||||
cycleDaysSortedByDate,
|
||||
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding)
|
||||
})
|
||||
const result = getPredictedMenses(99, 1)
|
||||
expect(result).to.eql({})
|
||||
})
|
||||
it('if no cycle is completed', () => {
|
||||
const cycleDaysSortedByDate = [
|
||||
{
|
||||
date: '2018-06-02',
|
||||
bleeding: { value: 2 }
|
||||
}
|
||||
]
|
||||
|
||||
const { getPredictedMenses } = cycleModule({
|
||||
cycleDaysSortedByDate,
|
||||
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding)
|
||||
})
|
||||
const result = getPredictedMenses(99, 1)
|
||||
expect(result).to.eql({})
|
||||
})
|
||||
it('if number of cycles is below minCyclesForPrediction', () => {
|
||||
const cycleDaysSortedByDate = [
|
||||
{
|
||||
date: '2018-06-02',
|
||||
bleeding: { value: 2 }
|
||||
},
|
||||
{
|
||||
date: '2018-06-01',
|
||||
bleeding: { value: 2 }
|
||||
},
|
||||
{
|
||||
date: '2018-05-01',
|
||||
bleeding: { value: 2 }
|
||||
},
|
||||
]
|
||||
|
||||
const { getPredictedMenses } = cycleModule({
|
||||
cycleDaysSortedByDate,
|
||||
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding)
|
||||
})
|
||||
const result = getPredictedMenses()
|
||||
expect(result).to.eql({})
|
||||
})
|
||||
it('if last bleeding was more than maxCycleLength days ago', () => {
|
||||
const cycleDaysSortedByDate = [
|
||||
{
|
||||
date: '2017-07-02',
|
||||
bleeding: { value: 2 }
|
||||
},
|
||||
{
|
||||
date: '2017-06-01',
|
||||
bleeding: { value: 2 }
|
||||
},
|
||||
{
|
||||
date: '2017-05-01',
|
||||
bleeding: { value: 2 }
|
||||
},
|
||||
{
|
||||
date: '2017-04-01',
|
||||
bleeding: { value: 2 }
|
||||
}
|
||||
]
|
||||
|
||||
const { getPredictedMenses } = cycleModule({
|
||||
cycleDaysSortedByDate,
|
||||
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding)
|
||||
})
|
||||
const result = getPredictedMenses()
|
||||
expect(result).to.eql({})
|
||||
})
|
||||
})
|
||||
describe('works', () => {
|
||||
it('if number of cycles is above minCyclesForPrediction with little standard deviation', () => {
|
||||
const cycleDaysSortedByDate = [
|
||||
{
|
||||
date: '2018-08-02',
|
||||
bleeding: { value: 2 }
|
||||
},
|
||||
{
|
||||
date: '2018-07-02',
|
||||
bleeding: { value: 2 }
|
||||
},
|
||||
{
|
||||
date: '2018-06-01',
|
||||
bleeding: { value: 2 }
|
||||
},
|
||||
{
|
||||
date: '2018-05-01',
|
||||
bleeding: { value: 2 }
|
||||
},
|
||||
]
|
||||
|
||||
const { getPredictedMenses } = cycleModule({
|
||||
cycleDaysSortedByDate,
|
||||
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding)
|
||||
})
|
||||
const result = getPredictedMenses()
|
||||
const expectedResult = [
|
||||
{
|
||||
'startDate': '2018-09-01',
|
||||
'endDate': '2018-09-03'
|
||||
},
|
||||
{
|
||||
'startDate': '2018-10-02',
|
||||
'endDate': '2018-10-04'
|
||||
},
|
||||
{
|
||||
'startDate': '2018-11-02',
|
||||
'endDate': '2018-11-04'
|
||||
}
|
||||
]
|
||||
expect(result).to.eql(expectedResult)
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user