adds more tests for period predictions, moves getcycleslength into cycle module

This commit is contained in:
tina
2018-08-23 11:36:57 +02:00
parent ea21fc92a2
commit 497a3a3ff5
4 changed files with 152 additions and 45 deletions
+8 -4
View File
@@ -7,7 +7,7 @@ import {
import styles from '../styles/index'
import cycleModule from '../lib/cycle'
import {getCycleLengthStats as getCycleInfo, getCycleLength} from '../lib/cycle-length'
import {getCycleLengthStats as getCycleInfo} from '../lib/cycle-length'
import {stats as labels} from './labels'
export default class Stats extends Component {
@@ -18,7 +18,7 @@ export default class Stats extends Component {
let numberOfCycles
let cycleInfo
if (atLeastOneCycle) {
cycleLengths = getCycleLength(allMensesStarts)
cycleLengths = cycleModule().getCycleLength(allMensesStarts)
numberOfCycles = cycleLengths.length
if (numberOfCycles > 1) {
cycleInfo = getCycleInfo(cycleLengths)
@@ -31,10 +31,14 @@ export default class Stats extends Component {
<Text style={styles.statsIntro}>{labels.emptyStats}</Text>
}
{atLeastOneCycle && numberOfCycles === 1 &&
<Text style={styles.statsIntro}>{labels.oneCycleStats(cycleLengths[0])}</Text>
<Text style={styles.statsIntro}>
{labels.oneCycleStats(cycleLengths[0])}
</Text>
}
{atLeastOneCycle && numberOfCycles > 1 && <View>
<Text style={styles.statsIntro}>{labels.getBasisOfStats(numberOfCycles)}</Text>
<Text style={styles.statsIntro}>
{labels.getBasisOfStats(numberOfCycles)}
</Text>
<View style={styles.statsRow}>
<Text style={styles.statsLabelLeft}>{labels.averageLabel}</Text>
<Text style={styles.statsLabelRight}>{cycleInfo.mean + ' ' + labels.daysLabel}</Text>
-12
View File
@@ -1,6 +1,4 @@
import assert from 'assert'
import { LocalDate, ChronoUnit } from 'js-joda'
import cycleModule from '../lib/cycle'
export function getCycleLengthStats(cycleLengths) {
throwIfArgsAreNotInRequiredFormat(cycleLengths)
@@ -49,13 +47,3 @@ 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
}
+29 -7
View File
@@ -1,5 +1,5 @@
import * as joda from 'js-joda'
import {getCycleLengthStats, getCycleLength} from './cycle-length'
import {getCycleLengthStats} from './cycle-length'
const LocalDate = joda.LocalDate
const DAYS = joda.ChronoUnit.DAYS
@@ -7,6 +7,8 @@ export default function config(opts) {
let bleedingDaysSortedByDate
let cycleDaysSortedByDate
let maxBreakInBleeding
let maxCycleLength
let minCyclesForPrediction
if (!opts) {
// we only want to require (and run) the db module
@@ -14,10 +16,14 @@ export default function config(opts) {
bleedingDaysSortedByDate = require('../db').bleedingDaysSortedByDate
cycleDaysSortedByDate = require('../db').cycleDaysSortedByDate
maxBreakInBleeding = 1
maxCycleLength = 99
minCyclesForPrediction = 3
} else {
bleedingDaysSortedByDate = opts.bleedingDaysSortedByDate || []
cycleDaysSortedByDate = opts.cycleDaysSortedByDate || []
maxBreakInBleeding = opts.maxBreakInBleeding || 1
maxCycleLength = opts.maxCycleLength || 99
minCyclesForPrediction = opts.minCyclesForPrediction || 3
}
function getLastMensesStart(targetDateString) {
@@ -144,22 +150,37 @@ export default function config(opts) {
}
}
function getPredictedMenses(maxCycleLength, minCyclesForPrediction) {
maxCycleLength = maxCycleLength || 99
minCyclesForPrediction = minCyclesForPrediction || 3
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])
const cycleLength = cycleStart.until(nextCycleStart, DAYS)
if (cycleLength <= maxCycleLength) { cycleLengths.push(cycleLength) }
}
return cycleLengths
}
function getPredictedMenses() {
const allMensesStarts = getAllMensesStarts()
const atLeastOneCycle = allMensesStarts.length > 1
if (!atLeastOneCycle ||
allMensesStarts.length < minCyclesForPrediction ||
getCycleDayNumber(LocalDate.now().toString()) > maxCycleLength
allMensesStarts.length < minCyclesForPrediction
) {
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
let periodStartVariation
if (cycleInfo.stdDeviation === null) {
periodStartVariation = 2
} else if (cycleInfo.stdDeviation < 1.5) { // threshold is choosen a little arbitrarily
periodStartVariation = 1
} else {
periodStartVariation = 2
}
var lastStart = allMensesStarts[0]
const predictedMenses = []
for (let i = 0; i < 3; i++) {
@@ -179,6 +200,7 @@ export default function config(opts) {
getPreviousCycle,
getCyclesBefore,
getAllMensesStarts,
getCycleLength,
getPredictedMenses
}
}
+115 -22
View File
@@ -345,19 +345,21 @@ describe('getCycleForDay', () => {
})
})
describe.only('getPredictedMenses', () => {
describe('getPredictedMenses', () => {
describe('cannot predict next menses', () => {
it('if no bleeding is documented', () => {
const cycleDaysSortedByDate = [ {} ]
const { getPredictedMenses } = cycleModule({
cycleDaysSortedByDate,
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding)
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding),
maxCycleLength: 99,
minCyclesForPrediction: 1
})
const result = getPredictedMenses(99, 1)
const result = getPredictedMenses()
expect(result).to.eql({})
})
it('if no cycle is completed', () => {
it('if one bleeding is documented (no completed cycle)', () => {
const cycleDaysSortedByDate = [
{
date: '2018-06-02',
@@ -367,9 +369,11 @@ describe.only('getPredictedMenses', () => {
const { getPredictedMenses } = cycleModule({
cycleDaysSortedByDate,
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding)
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding),
maxCycleLength: 99,
minCyclesForPrediction: 1
})
const result = getPredictedMenses(99, 1)
const result = getPredictedMenses()
expect(result).to.eql({})
})
it('if number of cycles is below minCyclesForPrediction', () => {
@@ -395,36 +399,43 @@ describe.only('getPredictedMenses', () => {
const result = getPredictedMenses()
expect(result).to.eql({})
})
it('if last bleeding was more than maxCycleLength days ago', () => {
})
describe('works', () => {
it('for one completed cycle with minCyclesForPrediction = 1', () => {
const cycleDaysSortedByDate = [
{
date: '2017-07-02',
date: '2018-07-15',
bleeding: { value: 2 }
},
{
date: '2017-06-01',
bleeding: { value: 2 }
},
{
date: '2017-05-01',
bleeding: { value: 2 }
},
{
date: '2017-04-01',
date: '2018-07-01',
bleeding: { value: 2 }
}
]
const { getPredictedMenses } = cycleModule({
cycleDaysSortedByDate,
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding)
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding),
minCyclesForPrediction: 1
})
const result = getPredictedMenses()
expect(result).to.eql({})
const expectedResult = [
{
'startDate': '2018-07-27',
'endDate': '2018-07-31'
},
{
'startDate': '2018-08-10',
'endDate': '2018-08-14'
},
{
'startDate': '2018-08-24',
'endDate': '2018-08-28'
}
]
expect(result).to.eql(expectedResult)
})
})
describe('works', () => {
it('if number of cycles is above minCyclesForPrediction with little standard deviation', () => {
it('if number of cycles is above minCyclesForPrediction', () => {
const cycleDaysSortedByDate = [
{
date: '2018-08-02',
@@ -465,5 +476,87 @@ describe.only('getPredictedMenses', () => {
]
expect(result).to.eql(expectedResult)
})
it('3 cycles with little standard deviation', () => {
const cycleDaysSortedByDate = [
{
date: '2018-08-01',
bleeding: { value: 2 }
},
{
date: '2018-07-18',
bleeding: { value: 2 }
},
{
date: '2018-07-05',
bleeding: { value: 2 }
},
{
date: '2018-06-20',
bleeding: { value: 2 }
},
]
const { getPredictedMenses } = cycleModule({
cycleDaysSortedByDate,
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding)
})
const result = getPredictedMenses()
const expectedResult = [
{
'startDate': '2018-08-14',
'endDate': '2018-08-16'
},
{
'startDate': '2018-08-28',
'endDate': '2018-08-30'
},
{
'startDate': '2018-09-11',
'endDate': '2018-09-13'
}
]
expect(result).to.eql(expectedResult)
})
it('3 cycles with bigger standard deviation', () => {
const cycleDaysSortedByDate = [
{
date: '2018-08-01',
bleeding: { value: 2 }
},
{
date: '2018-07-14',
bleeding: { value: 2 }
},
{
date: '2018-07-04',
bleeding: { value: 2 }
},
{
date: '2018-06-20',
bleeding: { value: 2 }
},
]
const { getPredictedMenses } = cycleModule({
cycleDaysSortedByDate,
bleedingDaysSortedByDate: cycleDaysSortedByDate.filter(d => d.bleeding)
})
const result = getPredictedMenses()
const expectedResult = [
{
'startDate': '2018-08-13',
'endDate': '2018-08-17'
},
{
'startDate': '2018-08-27',
'endDate': '2018-08-31'
},
{
'startDate': '2018-09-10',
'endDate': '2018-09-14'
}
]
expect(result).to.eql(expectedResult)
})
})
})