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:
+1
-1
@@ -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
@@ -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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user