Merge branch '73-implement-nfp-logic-for-mucus-mode' into 'master'

Resolve "implement NFP logic for mucus mode"

Closes #73

See merge request bloodyhealth/drip!20
This commit is contained in:
Julia Friesel
2018-07-25 08:56:34 +00:00
28 changed files with 2384 additions and 239 deletions
+117 -9
View File
@@ -11,11 +11,12 @@ import Svg,{
} from 'react-native-svg'
import { LocalDate } from 'js-joda'
import { getCycleDay, getOrCreateCycleDay, cycleDaysSortedByDate } from '../../db'
import getCycleDayNumberModule from '../../lib/get-cycle-day-number'
import cycleModule from '../../lib/cycle'
import styles from './styles'
import config from './config'
import { getCycleStatusForDay } from '../../lib/sympto-adapter'
const getCycleDayNumber = getCycleDayNumberModule()
const getCycleDayNumber = cycleModule().getCycleDayNumber
const yAxis = makeYAxis(config)
@@ -63,13 +64,29 @@ export default class CycleChart extends Component {
const cycleDayNumber = getCycleDayNumber(dateString)
const label = styles.column.label
const dateLabel = dateString.split('-').slice(1).join('-')
const getFhmAndLtlInfo = setUpFertilityStatusFunc()
const nfpLineInfo = getFhmAndLtlInfo(dateString, cycleDay)
return (
<G onPress={() => this.passDateToDayView(dateString)}>
<Rect {...styles.column.rect} />
{nfpLineInfo.drawFhmLine ?
<Line
x1={0 + styles.nfpLine.strokeWidth / 2}
y1="20"
x2={0 + styles.nfpLine.strokeWidth / 2}
y2={config.chartHeight - 20}
{...styles.nfpLine}
/> : null}
{this.placeHorizontalGrid()}
<Text {...label.number} y={config.cycleDayNumberRowY}>{cycleDayNumber}</Text>
<Text {...label.date} y={config.dateRowY}>{dateLabel}</Text>
<Text {...label.number} y={config.cycleDayNumberRowY}>
{cycleDayNumber}
</Text>
<Text {...label.date} y={config.dateRowY}>
{dateLabel}
</Text>
{cycleDay && cycleDay.bleeding ?
<Path {...styles.bleedingIcon}
@@ -79,10 +96,23 @@ export default class CycleChart extends Component {
Q13.5 6.8 15 3z" />
: null}
{nfpLineInfo.drawLtlAt ?
<Line
x1="0"
y1={nfpLineInfo.drawLtlAt}
x2={config.columnWidth}
y2={nfpLineInfo.drawLtlAt}
{...styles.nfpLine}
/> : null}
{y ?
this.drawDotAndLines(y, cycleDay.temperature.exclude, index)
: null
}
{cycleDay && cycleDay.mucus ?
<Circle
{...styles.mucusIcon}
fill={styles.mucusIconShades[cycleDay.mucus.computedNfp]}
fill={styles.mucusIconShades[cycleDay.mucus.value]}
/> : null}
{y ? this.drawDotAndLines(y, cycleDay.temperature.exclude, index) : null}
@@ -181,15 +211,18 @@ function makeColumnInfo(n) {
function getPreviousDays(n) {
const today = new Date()
today.setHours(0); today.setMinutes(0); today.setSeconds(0); today.setMilliseconds(0)
today.setHours(0)
today.setMinutes(0)
today.setSeconds(0)
today.setMilliseconds(0)
const earlierDate = new Date(today - (range.DAY * n))
return range(earlierDate, today).reverse()
}
function normalizeToScale(temp) {
const temperatureScale = config.temperatureScale
const valueRelativeToScale = (temperatureScale.high - temp) / (temperatureScale.high - temperatureScale.low)
const scale = config.temperatureScale
const valueRelativeToScale = (scale.high - temp) / (scale.high - scale.low)
const scaleHeight = config.chartHeight
return scaleHeight * valueRelativeToScale
}
@@ -202,7 +235,6 @@ function makeYAxis() {
const tickPositions = []
const labels = []
// for style reasons, we don't want the first and last tick
for (let i = 1; i < numberOfTicks - 1; i++) {
const y = tickDistance * i
@@ -223,3 +255,79 @@ function makeYAxis() {
return {labels, tickPositions}
}
function setUpFertilityStatusFunc() {
let cycleStatus
let cycleStartDate
let noMoreCycles = false
function updateCurrentCycle(dateString) {
cycleStatus = getCycleStatusForDay(dateString)
if(!cycleStatus) {
noMoreCycles = true
return
}
if (cycleStatus.phases.preOvulatory) {
cycleStartDate = cycleStatus.phases.preOvulatory.start.date
} else {
cycleStartDate = cycleStatus.phases.periOvulatory.start.date
}
}
function dateIsInPeriOrPostPhase(dateString) {
return (
dateString >= cycleStatus.phases.periOvulatory.start.date
)
}
function precededByAnotherTempValue(dateString) {
return (
// we are only interested in days that have a preceding
// temp
Object.keys(cycleStatus.phases).some(phaseName => {
return cycleStatus.phases[phaseName].cycleDays.some(day => {
return day.temperature && day.date < dateString
})
})
// and also a following temp, so we don't draw the line
// longer than necessary
&&
cycleStatus.phases.postOvulatory.cycleDays.some(day => {
return day.temperature && day.date > dateString
})
)
}
function isInTempMeasuringPhase(cycleDay, dateString) {
return (
cycleDay && cycleDay.temperature
|| precededByAnotherTempValue(dateString)
)
}
return function(dateString, cycleDay) {
const ret = {}
if (!cycleStatus && !noMoreCycles) updateCurrentCycle(dateString)
if (noMoreCycles) return ret
if (dateString < cycleStartDate) updateCurrentCycle(dateString)
if (noMoreCycles) return ret
const tempShift = cycleStatus.temperatureShift
if (tempShift) {
if (tempShift.firstHighMeasurementDay.date === dateString) {
ret.drawFhmLine = true
}
if (
dateIsInPeriOrPostPhase(dateString) &&
isInTempMeasuringPhase(cycleDay, dateString)
) {
ret.drawLtlAt = normalizeToScale(tempShift.ltl)
}
}
return ret
}
}
+4
View File
@@ -74,6 +74,10 @@ const styles = {
horizontalGrid: {
stroke: 'lightgrey',
strokeWidth: 1
},
nfpLine: {
stroke: '#00b159',
strokeWidth: 3
}
}
+3 -3
View File
@@ -14,10 +14,10 @@ import {
cervixFirmness as firmnessLabels,
cervixPosition as positionLabels
} from './labels/labels'
import cycleDayModule from '../../lib/get-cycle-day-number'
import cycleDayModule from '../../lib/cycle'
import { bleedingDaysSortedByDate } from '../../db'
const getCycleDayNumber = cycleDayModule()
const getCycleDayNumber = cycleDayModule().getCycleDayNumber
export default class DayView extends Component {
constructor(props) {
@@ -72,7 +72,7 @@ export default class DayView extends Component {
if (this.cycleDay.mucus) {
const mucus = this.cycleDay.mucus
if (typeof mucus.feeling === 'number' && typeof mucus.texture === 'number') {
mucusLabel = `${feelingLabels[mucus.feeling]} + ${textureLabels[mucus.texture]} ( ${computeSensiplanMucusLabels[mucus.computedNfp]} )`
mucusLabel = `${feelingLabels[mucus.feeling]} + ${textureLabels[mucus.texture]} ( ${computeSensiplanMucusLabels[mucus.value]} )`
if (mucus.exclude) mucusLabel = "( " + mucusLabel + " )"
}
} else {
+12 -3
View File
@@ -4,7 +4,8 @@ import {
Text,
ScrollView
} from 'react-native'
import cycleDayModule from '../../lib/get-cycle-day-number'
import cycleModule from '../../lib/cycle'
import { getFertilityStatusStringForDay } from '../../lib/sympto-adapter'
import DayView from './cycle-day-overview'
import BleedingEditView from './symptoms/bleeding'
import TemperatureEditView from './symptoms/temperature'
@@ -14,7 +15,7 @@ import CervixEditView from './symptoms/cervix'
import styles from '../../styles'
import actionButtonModule from './action-buttons'
const getCycleDayNumber = cycleDayModule()
const getCycleDayNumber = cycleModule().getCycleDayNumber
export default class Day extends Component {
constructor(props) {
@@ -34,6 +35,7 @@ export default class Day extends Component {
render() {
const cycleDayNumber = getCycleDayNumber(this.cycleDay.date)
const fertilityStatus = getFertilityStatusStringForDay(this.cycleDay.date)
return (
<ScrollView>
<View style={ styles.cycleDayDateView }>
@@ -42,7 +44,14 @@ export default class Day extends Component {
</Text>
</View >
<View style={ styles.cycleDayNumberView }>
{ cycleDayNumber && <Text style={styles.cycleDayNumber} >Cycle day {cycleDayNumber}</Text> }
{ cycleDayNumber &&
<Text style={styles.cycleDayNumber} >
Cycle day {cycleDayNumber}
</Text> }
<Text style={styles.cycleDayNumber} >
{fertilityStatus}
</Text>
</View >
<View>
{
+13 -16
View File
@@ -1,17 +1,14 @@
const bleeding = ['spotting', 'light', 'medium', 'heavy']
const mucusFeeling = ['dry', 'nothing', 'wet', 'slippery']
const mucusTexture = ['nothing', 'creamy', 'egg white']
const mucusNFP = ['t', 'Ø', 'f', 'S', '+S']
const cervixOpening = ['closed', 'medium', 'open']
const cervixFirmness = ['hard', 'soft']
const cervixPosition = ['low', 'medium', 'high']
export const bleeding = ['spotting', 'light', 'medium', 'heavy']
export const mucusFeeling = ['dry', 'nothing', 'wet', 'slippery']
export const mucusTexture = ['nothing', 'creamy', 'egg white']
export const mucusNFP = ['t', 'Ø', 'f', 'S', '+S']
export const cervixOpening = ['closed', 'medium', 'open']
export const cervixFirmness = ['hard', 'soft']
export const cervixPosition = ['low', 'medium', 'high']
export {
bleeding,
mucusFeeling,
mucusTexture,
mucusNFP,
cervixOpening,
cervixFirmness,
cervixPosition
}
export const fertilityStatus = {
fertile: 'fertile',
infertile: 'infertile',
fertileUntilEvening: 'Fertile phase ends in the evening',
unknown: 'We cannot show any cycle information because no menses has been entered'
}
+1 -1
View File
@@ -94,7 +94,7 @@ export default class Mucus extends Component {
saveSymptom('mucus', this.cycleDay, {
feeling: this.state.feeling,
texture: this.state.texture,
computedNfp: computeSensiplanValue(this.state.feeling, this.state.texture),
value: computeSensiplanValue(this.state.feeling, this.state.texture),
exclude: this.state.exclude
})
},
+10 -4
View File
@@ -6,11 +6,11 @@ import {
ScrollView
} from 'react-native'
import { LocalDate } from 'js-joda'
import styles from '../styles'
import cycleDayModule from '../lib/get-cycle-day-number'
import { getOrCreateCycleDay, bleedingDaysSortedByDate, deleteAll } from '../db'
import styles from '../styles/index'
import cycleModule from '../lib/cycle'
import { getOrCreateCycleDay, bleedingDaysSortedByDate, fillWithDummyData, deleteAll } from '../db'
const getCycleDayNumber = cycleDayModule()
const getCycleDayNumber = cycleModule().getCycleDayNumber
export default class Home extends Component {
constructor(props) {
@@ -68,6 +68,12 @@ export default class Home extends Component {
title="Go to chart">
</Button>
</View>
<View style={styles.homeButton}>
<Button
onPress={() => fillWithDummyData()}
title="fill with example data">
</Button>
</View>
<View style={styles.homeButton}>
<Button
onPress={() => deleteAll()}