Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: custom calculations (DHIS2-13871) #2232

Merged
merged 49 commits into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from 38 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
79b6593
fix: include expression in nested field
martinkrulltott Jan 31, 2023
80f62f0
fix: include dataElementDimensions in baseFields (might not be needed)
martinkrulltott Jan 31, 2023
c03ffa3
chore: en.pot updates
martinkrulltott Jan 31, 2023
ffd9558
chore: dedupe ui in yarn.lock
martinkrulltott Jan 31, 2023
c3875f1
fix: save metadata on calculation save
martinkrulltott Jan 31, 2023
790f4f7
chore: update snapshot
martinkrulltott Jan 31, 2023
6c31fee
test: initial draft for calculation tests
martinkrulltott Feb 8, 2023
da8f7ce
test: minor test updates
martinkrulltott Feb 10, 2023
3102617
Merge branch 'master' into feat/DHIS2-13871-custom-calculations
martinkrulltott Feb 15, 2023
06755b8
fix: extract metadata from /visualizations response for calculations
martinkrulltott Feb 20, 2023
f6788ac
fix: fallback when dataDimensionItems aren't available
martinkrulltott Feb 20, 2023
ed193b6
fix: add dimensionItemType and expression to metadata
martinkrulltott Feb 22, 2023
c45e27b
fix: include metadata in SET_CURRENT_FROM_UI dx items
martinkrulltott Feb 23, 2023
04d6584
chore: add default state for tests to pass
martinkrulltott Feb 23, 2023
62d93ea
fix: map expression to metadata
martinkrulltott Feb 28, 2023
277f24e
fix: revert the metadata changes
martinkrulltott Feb 28, 2023
a16d705
Merge branch 'dev' into feat/DHIS2-13871-custom-calculations
martinkrulltott Mar 1, 2023
b4ef170
fix: add correct metadata needed for edi_cache + refactor the use of …
martinkrulltott Mar 2, 2023
7262838
test: adapt jest tests to the new reducer input format
martinkrulltott Mar 2, 2023
bc74527
fix: add id to default metadata items
martinkrulltott Mar 2, 2023
4f0705d
Merge branch 'dev' into feat/DHIS2-13871-custom-calculations
martinkrulltott Mar 2, 2023
7a1d45d
fix: include calculation in the indicator error message
martinkrulltott Mar 7, 2023
0464981
Merge branch 'dev' into feat/DHIS2-13871-custom-calculations
martinkrulltott Mar 10, 2023
293d0e4
Merge branch 'dev' into feat/DHIS2-13871-custom-calculations
martinkrulltott Mar 13, 2023
d6f4391
Merge branch 'dev' into feat/DHIS2-13871-custom-calculations
martinkrulltott Mar 17, 2023
f4cc3e4
Merge branch 'dev' into feat/DHIS2-13871-custom-calculations
martinkrulltott Mar 27, 2023
28792d7
Merge branch 'dev' into feat/DHIS2-13871-custom-calculations
martinkrulltott Apr 3, 2023
d0176bb
Merge branch 'dev' into feat/DHIS2-13871-custom-calculations
martinkrulltott Apr 3, 2023
d430f64
fix: pass access prop for dimension items (DHIS2-15047)
martinkrulltott Apr 12, 2023
82d5cc6
Merge branch 'dev' into feat/DHIS2-13871-custom-calculations
martinkrulltott Apr 17, 2023
56a317b
v0.0.0-2023-04-17T13-48-48
martinkrulltott Apr 17, 2023
fbbd9de
v0.0.0-2023-04-17T13-59-33
martinkrulltott Apr 17, 2023
e09b0d9
v0.0.0-2023-04-17T15-05-21
martinkrulltott Apr 17, 2023
c3ed05c
v0.0.0-2023-04-17T15-13-17
martinkrulltott Apr 17, 2023
80e62cf
v0.0.0-2023-04-17T15-27-05
martinkrulltott Apr 17, 2023
d3adb67
fix: prevent error when dimension has no items (DHIS2-15133)
martinkrulltott Apr 18, 2023
19da80e
Merge branch 'dev' into feat/DHIS2-13871-custom-calculations
martinkrulltott Apr 20, 2023
2ba442b
Merge branch 'dev' into feat/DHIS2-13871-custom-calculations
martinkrulltott Apr 21, 2023
4e2af60
chore: bump analytics to latest
martinkrulltott Apr 24, 2023
f34447d
test: refactor tests to use a single it
martinkrulltott Apr 24, 2023
f1c4600
test: save, load, delete an EDI
martinkrulltott Apr 25, 2023
4b64295
test: data elements - scroll, search, filter, switch type
martinkrulltott Apr 26, 2023
bb0d875
test: save button is disabled and has tooltip when no name is entered
martinkrulltott Apr 26, 2023
770b977
test: add/remove data elements and math operators
martinkrulltott Apr 26, 2023
f4b46d9
test: verify name (metadata) of items in formula field
martinkrulltott Apr 26, 2023
933f6ab
test: formula validation
martinkrulltott Apr 27, 2023
936e3ba
test: remove only
martinkrulltott Apr 27, 2023
50250f6
test: refactor to remove expectCalculationsModalTitleToContain that w…
martinkrulltott Apr 27, 2023
b1a2d37
Merge branch 'dev' into feat/DHIS2-13871-custom-calculations
martinkrulltott Apr 27, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions cypress/elements/calculationsModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const modalTitleEl = 'calculation-modal-title'
martinkrulltott marked this conversation as resolved.
Show resolved Hide resolved

export const clickNewCalculationButton = () =>
cy
.getBySel('data-dimension-transfer-leftfooter')
.containsExact('Calculation')
.click()

export const expectCalculationsModalToBeVisible = () =>
cy.getBySel('calculation-modal').should('be.visible')

export const expectCalculationsModalTitleToContain = (text) =>
cy.getBySel(modalTitleEl).should('contain', text)
102 changes: 102 additions & 0 deletions cypress/integration/dimensions/calculations.cy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { DIMENSION_ID_DATA } from '@dhis2/analytics'
import {
clickNewCalculationButton,
expectCalculationsModalTitleToContain,
expectCalculationsModalToBeVisible,
} from '../../elements/calculationsModal.js'
import { expectDataDimensionModalToBeVisible } from '../../elements/dimensionModal/index.js'
import { openDimension } from '../../elements/dimensionsPanel.js'
import { goToStartPage } from '../../elements/startScreen.js'

const PAGE_SIZE = 50
const DATA_ELEMENTS_URL = '**/dataElements?*'

describe('Data dimension', () => {
describe('initial state', () => {
it('navigates to the start page', () => {
martinkrulltott marked this conversation as resolved.
Show resolved Hide resolved
goToStartPage()
})
it('opens the data dimension modal', () => {
openDimension(DIMENSION_ID_DATA)
expectDataDimensionModalToBeVisible()
})
it('opens the calculations modal', () => {
cy.intercept('GET', DATA_ELEMENTS_URL).as('request')
clickNewCalculationButton()
cy.wait('@request').then(({ request, response }) => {
expect(request.url).to.contain('page=1')
expect(response.statusCode).to.eq(200)
expect(response.body.dataElements.length).to.eq(PAGE_SIZE)
})
expectCalculationsModalToBeVisible()
})
it('modal has a title', () => {
expectCalculationsModalTitleToContain('Data / New calculation')
})
it('"All groups" is selected', () => {
cy.getBySel('data-element-group-select').contains('All groups')
})
it('"Totals only" is selected', () => {
cy.getBySel('data-element-disaggregation-select').contains(
'Totals only'
)
})
it('items are loaded', () => {
cy.getBySelLike('dimension-list')
.findBySel('dimension-option')
.should('have.length', PAGE_SIZE)
})

/*

--search/filter
searching
filtering by group
changing disaggregation type
scrolling down and fetching the next page

--creating a formula
double-click to add
//dnd to add
add math operators
remove by selecting and "remove item" button
//remove by double click
reorder with DND
all math operators work and creates a valid formula when used
dataElements (Totals only) show correct name when added
dataElementOperands (Details only) show correct name when added

--frontend validation / "Check formula"
"Empty formula"
"Consecutive math operators"
"Consecutive data elements"
"Starts or ends with a math operator"
"Missing right parenthesis )"
"Missing left parenthesis ("
"Valid"
sample test formula from request to validation (note that detailed tests are already done in Jest)
changing the formula resets the status
EDI can't be saved without a valid formula (check with Joe if this is still valid)

--saving a formula
enter a label
create a valid formula
validate the formula
save
EDI displays correctly in the visualization

--opening a saved formula
unselected formula is listed under "Data Type: Calculations"
tooltip shows on hover
has label and formula when opened
selected formula is selected and not listed under "Data Type: Calculations"
tooltip shows on hover
has label and formula when opened
editing and saving works
edited formula shows in tooltip on hover
edited name shows in list of data items
edited EDI displays correctly in visualization

*/
})
})
12 changes: 6 additions & 6 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2023-02-08T10:41:55.462Z\n"
"PO-Revision-Date: 2023-02-08T10:41:55.462Z\n"
"POT-Creation-Date: 2023-03-07T12:07:38.393Z\n"
"PO-Revision-Date: 2023-03-07T12:07:38.393Z\n"

msgid "All items"
msgstr "All items"
Expand Down Expand Up @@ -780,11 +780,11 @@ msgid "There's a problem with the layout"
msgstr "There's a problem with the layout"

msgid ""
"A single indicator must be the only data item when using indicators as Data "
"in Filter."
"A single indicator or calculation must be the only data item when using "
"indicators or calculations as Data in Filter."
msgstr ""
"A single indicator must be the only data item when using indicators as Data "
"in Filter."
"A single indicator or calculation must be the only data item when using "
"indicators or calculations as Data in Filter."

msgid "Data Element Group Sets and Reporting Rates cannot be used together."
msgstr "Data Element Group Sets and Reporting Rates cannot be used together."
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "data-visualizer-app",
"version": "100.0.0",
"version": "0.0.0-2023-04-17T15-27-05",
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This was committed by mistake

"description": "DHIS2 Data Visualizer",
"license": "BSD-3-Clause",
"private": true,
Expand Down
4 changes: 4 additions & 0 deletions src/actions/__tests__/index.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ describe('index', () => {
{
type: SET_VISUALIZATION,
value: vis,
metadata: [],
},
{ type: SET_CURRENT, value: vis },
{
Expand Down Expand Up @@ -107,6 +108,7 @@ describe('index', () => {
{
type: SET_VISUALIZATION,
value: vis,
metadata: [],
},
{ type: SET_CURRENT, value: vis },
{
Expand Down Expand Up @@ -269,6 +271,7 @@ describe('index', () => {
{
type: SET_VISUALIZATION,
value: { ...visualization, ...extraParams },
metadata: [],
},
{
type: SET_CURRENT,
Expand Down Expand Up @@ -299,6 +302,7 @@ describe('index', () => {
{
type: SET_VISUALIZATION,
value: { ...visualization, ...extraParams },
metadata: [],
},
{
type: SET_CURRENT,
Expand Down
8 changes: 7 additions & 1 deletion src/actions/current.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
SET_CURRENT_FROM_UI,
CLEAR_CURRENT,
} from '../reducers/current.js'
import { sGetMetadata } from '../reducers/metadata.js'
import { sGetUi } from '../reducers/ui.js'

export const acSetCurrent = (value) => ({
Expand All @@ -20,5 +21,10 @@ export const acSetCurrentFromUi = (value) => ({
})

export const tSetCurrentFromUi = () => async (dispatch, getState) => {
dispatch(acSetCurrentFromUi(sGetUi(getState())))
dispatch(
acSetCurrentFromUi({
ui: sGetUi(getState()),
metadata: sGetMetadata(getState()),
})
)
}
14 changes: 10 additions & 4 deletions src/actions/visualization.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { getDimensionMetadataFromVisualization } from '../modules/visualization.js'
import {
SET_VISUALIZATION,
CLEAR_VISUALIZATION,
} from '../reducers/visualization.js'

export const acSetVisualization = (visualization) => ({
type: SET_VISUALIZATION,
value: visualization,
})
export const acSetVisualization = (visualization) => {
const metadata = getDimensionMetadataFromVisualization(visualization)

return {
type: SET_VISUALIZATION,
value: visualization,
metadata,
}
}

export const acClear = () => ({
type: CLEAR_VISUALIZATION,
Expand Down
6 changes: 3 additions & 3 deletions src/components/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export class UnconnectedApp extends Component {
this.props.clearCurrent()

this.props.setUiFromVisualization(this.props.currentAO)
this.props.setCurrentFromUi(this.props.ui)
this.props.setCurrentFromUi()
}

if (!urlContainsCurrentAOKey && this.refetch(location)) {
Expand Down Expand Up @@ -198,7 +198,7 @@ export class UnconnectedApp extends Component {
(e) =>
e.key === 'Enter' &&
e.ctrlKey === true &&
this.props.setCurrentFromUi(this.props.ui)
this.props.setCurrentFromUi()
)

window.addEventListener('beforeunload', (event) => {
Expand Down Expand Up @@ -346,7 +346,7 @@ const mapStateToProps = (state) => ({
})

const mapDispatchToProps = {
setCurrentFromUi: fromActions.fromCurrent.acSetCurrentFromUi,
setCurrentFromUi: fromActions.fromCurrent.tSetCurrentFromUi,
clearVisualization: fromActions.fromVisualization.acClear,
clearCurrent: fromActions.fromCurrent.acClear,
setUiFromVisualization: fromActions.fromUi.acSetUiFromVisualization,
Expand Down
22 changes: 21 additions & 1 deletion src/components/DimensionsPanel/Dialogs/DialogManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,10 @@ export class DialogManager extends Component {
id: item.id,
name: item.name || item.displayName,
displayName: item.displayName,
type: item.type,
dimensionItemType: item.type,
...(item.expression
? { expression: item.expression }
: {}),
}

return obj
Expand Down Expand Up @@ -203,6 +206,12 @@ export class DialogManager extends Component {
type:
this.props.metadata[id]?.type ||
this.props.metadata[id]?.dimensionItemType,
...(this.props.metadata[id]?.expression
? {
expression: this.props.metadata[id].expression,
}
: {}),
access: this.props.metadata[id]?.access,
}))
}

Expand Down Expand Up @@ -315,12 +324,23 @@ export class DialogManager extends Component {
itemAttribute: dialogId,
})
: dimensionProps.onSelect
const onCalculationSave = (calculation) => {
this.props.addMetadata({
[calculation.id]: {
id: calculation.id,
name: calculation.name,
dimensionItemType: calculation.type,
expression: calculation.expression,
},
})
}
const dimensionSelector = (
<DataDimension
displayNameProp={displayNameProperty}
selectedDimensions={selectedItems}
infoBoxMessage={infoBoxMessage}
onSelect={onSelect}
onCalculationSave={onCalculationSave}
/>
)
const dataTabs = isScatterAttribute(dialogId) ? (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ exports[`The DialogManager component renders OUDimension content with display:no
/>
</div>
<DataDimension
onCalculationSave={[Function]}
onSelect={[Function]}
selectedDimensions={Array []}
/>
Expand Down Expand Up @@ -125,6 +126,7 @@ exports[`The DialogManager component renders the DataDimension content in dialog
dataTest="dialog-manager-modal-content"
>
<DataDimension
onCalculationSave={[Function]}
onSelect={[Function]}
selectedDimensions={Array []}
/>
Expand Down
20 changes: 17 additions & 3 deletions src/middleware/metadata.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,23 @@ import { acAddMetadata } from '../actions/metadata.js'
export default ({ dispatch }) =>
(next) =>
(action) => {
typeof action.metadata === 'object' &&
!Array.isArray(action.metadata) &&
dispatch(acAddMetadata(action.metadata))
if (typeof action.metadata === 'object') {
if (Array.isArray(action.metadata)) {
dispatch(
acAddMetadata(
action.metadata.reduce(
(meta, obj) => ({
...meta,
...obj,
}),
{}
)
)
)
} else {
dispatch(acAddMetadata(action.metadata))
}
}

next(action)
}
4 changes: 1 addition & 3 deletions src/modules/__tests__/current.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,7 @@ describe('getSingleValueCurrentFromUi', () => {

ui.type = 'SINGLEVALUE'

const actualState = getSingleValueCurrentFromUi(DEFAULT_CURRENT, {
value: ui,
})
const actualState = getSingleValueCurrentFromUi(DEFAULT_CURRENT, ui)

expect(actualState).toEqual(expectedState)
})
Expand Down
Loading