Skip to content

Commit

Permalink
fix: handle indicator decimals [DHIS2-15631] (#350)
Browse files Browse the repository at this point in the history
  • Loading branch information
tomzemp authored Aug 8, 2023
1 parent 018e785 commit a747150
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/data-workspace/custom-form/replace-td-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ const replaceIndicatorCell = (indicatorId, metadata) => {
denominator,
numerator,
indicatorType: { factor },
decimals,
} = metadata.indicators[indicatorId]

return (
<IndicatorTableCell
denominator={denominator}
numerator={numerator}
factor={factor}
decimals={decimals}
/>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,24 @@ const parseExpressionTemplate = (expression, values) => {
return substitudedExpression.replace(match, value)
}, expression)
}

/*
* round(value, decimals)
*
* @param {number} value unrounded computed indicactorValue
* @param {number} decimals number of decimal places to include output
* @returns {number} number rounded to number of decimals
*/

export const round = (value, decimals) => {
if (decimals < 0 || !Number.isInteger(decimals)) {
return value
}
// Math.round only rounds to whole digit, so multiply by 10^x, round, then divide by 10^x
const factor = Math.pow(10, decimals)
return Math.round(value * factor) / factor
}

/**
* Parses and evaluates the denominator and numerator expressions
* and then computes the indicator value
Expand All @@ -110,6 +128,7 @@ export const computeIndicatorValue = ({
numerator,
factor,
formState,
decimals,
}) => {
const numeratorExpression = parseExpressionTemplate(
numerator,
Expand All @@ -123,6 +142,8 @@ export const computeIndicatorValue = ({
const denominatorValue = evaluate(denominatorExpression)
const indicatorValue = (numeratorValue / denominatorValue) * factor
const isReadableNumber = isFinite(indicatorValue) && !isNaN(indicatorValue)

return isReadableNumber ? indicatorValue : ''
if (!isReadableNumber) {
return ''
}
return decimals ? round(indicatorValue, decimals) : indicatorValue
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { computeIndicatorValue } from './compute-indicator-value.js'
import { computeIndicatorValue, round } from './compute-indicator-value.js'

describe('computeIndicatorValue', () => {
const formState = {
Expand Down Expand Up @@ -67,4 +67,59 @@ describe('computeIndicatorValue', () => {
})
expect(value).toBe('')
})
it('returns an appropriately rounded value', () => {
const value = computeIndicatorValue({
numerator: '#{a.a1}', // 1
denominator: '#{c.c1}', // 3
factor: 100,
formState,
decimals: 3,
})
expect(value).toBe(33.333)
})
})

describe('round', () => {
it('rounds to 2 values if decimals is 2', () => {
const decimals = 2
const originalValue = 3.1415926
const roundedValue = round(originalValue, decimals)
expect(roundedValue).toBe(3.14)
})
it('rounds to whole number if decimals is 0', () => {
const decimals = 0
const originalValue = 3.1415926
const roundedValue = round(originalValue, decimals)
expect(roundedValue).toBe(3)
})
it('rounds down if original value is negative', () => {
const decimals = 1
const originalValue = -3.1415926
const roundedValue = round(originalValue, decimals)
expect(roundedValue).toBe(-3.1)
})
it('returns NaN if original value is NaN', () => {
const decimals = 3
const originalValue = NaN
const roundedValue = round(originalValue, decimals)
expect(roundedValue).toBe(originalValue)
})
it('returns the original value if decimals is not provided', () => {
const decimals = undefined
const originalValue = 3.1415926
const roundedValue = round(originalValue, decimals)
expect(roundedValue).toBe(originalValue)
})
it('returns the original value if decimals is not an integer', () => {
const decimals = 2.71828
const originalValue = 3.1415926
const roundedValue = round(originalValue, decimals)
expect(roundedValue).toBe(originalValue)
})
it('returns the original value if decimals is negative', () => {
const decimals = -4
const originalValue = 3.1415926
const roundedValue = round(originalValue, decimals)
expect(roundedValue).toBe(originalValue)
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import { useBlurredField } from '../../shared/index.js'
import styles from '../table-body.module.css'
import { useIndicatorValue } from './use-indicator-value.js'

export const IndicatorTableCell = ({ denominator, factor, numerator }) => {
export const IndicatorTableCell = ({
denominator,
factor,
numerator,
decimals,
}) => {
const form = useForm()
const blurredField = useBlurredField()
const indicatorValue = useIndicatorValue({
Expand All @@ -15,6 +20,7 @@ export const IndicatorTableCell = ({ denominator, factor, numerator }) => {
factor,
form,
numerator,
decimals,
})

return (
Expand All @@ -26,4 +32,5 @@ IndicatorTableCell.propTypes = {
denominator: PropTypes.string.isRequired,
factor: PropTypes.number.isRequired,
numerator: PropTypes.string.isRequired,
decimals: PropTypes.number,
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const IndicatorsTableBody = ({
denominator={indicator.denominator}
numerator={indicator.numerator}
factor={indicator.indicatorType.factor}
decimals={indicator.decimals}
/>
{padColumns.map((_, i) => (
<TableCell
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const useIndicatorValue = ({
factor,
form,
numerator,
decimals,
}) => {
const indicatorValueRef = useRef(null)
const affectedDataElementsLookup = useMemo(
Expand Down Expand Up @@ -57,6 +58,7 @@ export const useIndicatorValue = ({
numerator,
factor,
formState: form.getState(),
decimals,
})
}

Expand All @@ -68,5 +70,6 @@ export const useIndicatorValue = ({
factor,
form,
numerator,
decimals,
])
}

1 comment on commit a747150

@dhis2-bot
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.