Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Better prediction of next menstruation (#20)
* Added jest * Refactored predictions and added test
- Loading branch information
Showing
13 changed files
with
1,680 additions
and
182 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
const babelOptions = { | ||
presets: ["babel-preset-gatsby"], | ||
} | ||
module.exports = require("babel-jest").createTransformer(babelOptions) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
module.exports = { | ||
transform: { | ||
"^.+\\.jsx?$": `<rootDir>/jest-preprocess.js`, | ||
}, | ||
moduleNameMapper: { | ||
".+\\.(css|styl|less|sass|scss)$": `identity-obj-proxy`, | ||
".+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": `<rootDir>/__mocks__/file-mock.js`, | ||
}, | ||
testPathIgnorePatterns: [`node_modules`, `.cache`, `public`], | ||
transformIgnorePatterns: [`node_modules/(?!(gatsby)/)`], | ||
globals: { | ||
__PATH_PREFIX__: ``, | ||
}, | ||
testURL: `http://localhost`, | ||
setupFiles: [`<rootDir>/loadershim.js`], | ||
} |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import useCycle from "./useCycle" | ||
|
||
export default useCycle |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
import { useEffect } from "react" | ||
import { analyzeEntries, daysBetweenDates, addDaysToDate } from "./utils" | ||
|
||
import { useStore } from "../store" | ||
import useEntries from "../useEntries" | ||
import useSettings from "../useSettings" | ||
|
||
const useCycle = () => { | ||
const [{ cycle }, setState] = useStore() | ||
const [{ entriesByDate }] = useEntries() | ||
const [{ menstruationSettings }] = useSettings() | ||
|
||
const updateCycle = (newCycle) => { | ||
setState((state) => ({ | ||
...state, | ||
cycle: { | ||
...state.cycle, | ||
...newCycle, | ||
}, | ||
})) | ||
} | ||
|
||
const getCycleValue = (key) => { | ||
if (!cycle.hasOwnProperty(key)) { | ||
console.warn(`No ${key} on cycle slice of state`) | ||
} | ||
return cycle[key] | ||
} | ||
|
||
const getCurrentStartDate = () => { | ||
const startDates = getCycleValue("startDates") | ||
if (startDates.length > 0) { | ||
return startDates[0] | ||
} | ||
} | ||
|
||
const getNextStartDate = () => { | ||
const currentStartDate = getCurrentStartDate() | ||
const averageLength = getCycleValue("averageLength") | ||
if (averageLength && currentStartDate) { | ||
return addDaysToDate(currentStartDate, averageLength) | ||
} | ||
} | ||
|
||
const getCurrentDayInCycle = (date) => { | ||
const currentStartDate = getCurrentStartDate() | ||
if (currentStartDate) { | ||
return daysBetweenDates(date, currentStartDate) + 1 | ||
} | ||
} | ||
|
||
useEffect(() => { | ||
const tag = menstruationSettings.tag | ||
const averageLength = cycle.averageLength | ||
const newCycle = analyzeEntries({ entriesByDate, tag, averageLength }) | ||
updateCycle(newCycle) | ||
console.log(newCycle) | ||
}, [entriesByDate, menstruationSettings.tag]) | ||
|
||
return [ | ||
{ | ||
startDates: getCycleValue("startDates"), | ||
currentStartDate: getCurrentStartDate(), | ||
nextStartDate: getNextStartDate(), | ||
averageLength: getCycleValue("averageLength"), | ||
tags: getCycleValue("tags"), | ||
}, | ||
{ | ||
getCurrentDayInCycle, | ||
}, | ||
] | ||
} | ||
|
||
export default useCycle |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { values, sum } from "lodash" | ||
import { differenceInDays, addDays, format } from "date-fns" | ||
|
||
const getLastInArray = (array) => { | ||
const index = array.length > 0 ? array.length - 1 : 0 | ||
return array[index] | ||
} | ||
|
||
const replaceLastItemInArray = (array, item) => { | ||
const index = array.length > 0 ? array.length - 1 : 0 | ||
array[index] = item | ||
} | ||
|
||
export const daysBetweenDates = (dateA, dateB) => { | ||
if (!(dateA instanceof Date)) { | ||
dateA = new Date(dateA) | ||
} | ||
if (!(dateB instanceof Date)) { | ||
dateB = new Date(dateB) | ||
} | ||
|
||
if (isNaN(dateA.valueOf()) || isNaN(dateB.valueOf())) return 0 | ||
|
||
return Math.abs(differenceInDays(dateA, dateB)) | ||
} | ||
|
||
export const addDaysToDate = (date, days) => { | ||
if (!(date instanceof Date)) { | ||
date = new Date(date) | ||
} | ||
|
||
if (isNaN(date.valueOf())) return | ||
|
||
const newDate = addDays(date, days) | ||
return format(newDate, "yyyy-MM-dd") | ||
} | ||
|
||
const entryHasTag = (entry, tag) => { | ||
return entry.tags.includes(tag) | ||
} | ||
|
||
export const analyzeEntries = ({ entriesByDate, tag }) => { | ||
const sortedEntries = values(entriesByDate).sort((a, b) => | ||
a.date > b.date ? -1 : 1 | ||
) | ||
|
||
const startDates = [] | ||
const cycleLengths = [] | ||
let averageLength = undefined | ||
|
||
for (let entry of sortedEntries) { | ||
if (entryHasTag(entry, tag)) { | ||
const lastStartDate = getLastInArray(startDates) | ||
let difference = daysBetweenDates(entry.date, lastStartDate) | ||
|
||
if (difference >= 14) { | ||
startDates.push(entry.date) | ||
cycleLengths.push(difference) | ||
} else { | ||
replaceLastItemInArray(startDates, entry.date) | ||
|
||
if (difference > 0 && cycleLengths.length > 0) { | ||
difference = getLastInArray(cycleLengths) + difference | ||
replaceLastItemInArray(cycleLengths, difference) | ||
} | ||
} | ||
} | ||
} | ||
|
||
if (cycleLengths.length > 0) { | ||
averageLength = sum(cycleLengths) / cycleLengths.length | ||
} | ||
|
||
return { | ||
startDates, | ||
averageLength, | ||
tags: {}, | ||
} | ||
} |
Oops, something went wrong.