Merge branch '98-function-to-get-predicted-bleeding-dates' into 'master'

Resolve "write function to get predicted bleeding dates (and use it in calendar)"

Closes #97 and #98

See merge request bloodyhealth/drip!60
This commit is contained in:
tina
2018-08-31 09:48:30 +00:00
9 changed files with 1852 additions and 1497 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
import assert from 'assert'
export default function getCycleLengthStats(cycleLengths) {
export function getCycleLengthStats(cycleLengths) {
throwIfArgsAreNotInRequiredFormat(cycleLengths)
const cycleLengthStats = {}
const sortedCycleLengths = cycleLengths.sort((a, b) => {
+58 -1
View File
@@ -1,4 +1,5 @@
import * as joda from 'js-joda'
import {getCycleLengthStats} from './cycle-length'
const LocalDate = joda.LocalDate
const DAYS = joda.ChronoUnit.DAYS
@@ -6,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
@@ -13,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) {
@@ -143,11 +150,61 @@ export default function config(opts) {
}
}
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
) {
return []
}
const cycleLengths = getCycleLength(allMensesStarts)
const cycleInfo = getCycleLengthStats(cycleLengths)
const periodDistance = Math.round(cycleInfo.mean)
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
}
if (periodDistance - 5 < periodStartVariation) { // otherwise predictions overlap
return []
}
let lastStart = LocalDate.parse(allMensesStarts[0])
const predictedMenses = []
for (let i = 0; i < 3; i++) {
lastStart = lastStart.plusDays(periodDistance)
const nextPredictedDates = [lastStart.toString()]
for (let j = 0; j < periodStartVariation; j++) {
nextPredictedDates.push(lastStart.minusDays(j+1).toString())
nextPredictedDates.push(lastStart.plusDays(j+1).toString())
}
nextPredictedDates.sort()
predictedMenses.push(nextPredictedDates)
}
return predictedMenses
}
return {
getCycleDayNumber,
getCycleForDay,
getPreviousCycle,
getCyclesBefore,
getAllMensesStarts
getAllMensesStarts,
getCycleLength,
getPredictedMenses
}
}