Skip to content

Commit

Permalink
feat: support Outlier table visualization type (DHIS2-13858) (#2942)
Browse files Browse the repository at this point in the history
  • Loading branch information
edoardo authored Mar 5, 2024
1 parent 77c5bcd commit b4a5d53
Show file tree
Hide file tree
Showing 46 changed files with 2,135 additions and 653 deletions.
142 changes: 130 additions & 12 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-11-13T12:11:28.959Z\n"
"PO-Revision-Date: 2023-11-13T12:11:28.959Z\n"
"POT-Creation-Date: 2024-03-01T08:28:43.727Z\n"
"PO-Revision-Date: 2024-03-01T08:28:43.727Z\n"

msgid "All items"
msgstr "All items"
Expand Down Expand Up @@ -43,11 +43,11 @@ msgid "Add to {{axisName}}"
msgstr "Add to {{axisName}}"

msgid ""
"'{{visualizationType}}' is intended to show a single data item. Only the "
"first item will be used and saved."
"'{{visualizationType}}' is intended to show a single item for this type of "
"dimension. Only the first item will be used and saved."
msgstr ""
"'{{visualizationType}}' is intended to show a single data item. Only the "
"first item will be used and saved."
"'{{visualizationType}}' is intended to show a single item for this type of "
"dimension. Only the first item will be used and saved."

msgid ""
"'{{visualiationType}}' is intended to show maximum {{maxNumber}} number of "
Expand All @@ -63,6 +63,13 @@ msgstr ""
"'Scatter' is intended to show a single data item per axis. Only the first "
"item will be used and saved."

msgid ""
"'Outlier table' shows values from data elements only. Only data elements "
"will be used and saved."
msgstr ""
"'Outlier table' shows values from data elements only. Only data elements "
"will be used and saved."

msgid "Vertical"
msgstr "Vertical"

Expand Down Expand Up @@ -183,11 +190,14 @@ msgstr "horizontal"
msgid "None selected"
msgstr "None selected"

msgid "None in use"
msgstr "None in use"

msgid "Only '{{- name}}' in use"
msgstr "Only '{{- name}}' in use"

msgid "Only '{{number}}' in use"
msgstr "Only '{{number}}' in use"
msgid "Only {{number}} in use"
msgstr "Only {{number}} in use"

msgid "All items are selected"
msgstr "All items are selected"
Expand Down Expand Up @@ -581,6 +591,16 @@ msgstr ""
msgid "Outlier detection method"
msgstr "Outlier detection method"

msgid "Max results"
msgstr "Max results"

msgid ""
"The maximum number of outlier values to show in the table. Must be between "
"1-500."
msgstr ""
"The maximum number of outlier values to show in the table. Must be between "
"1-500."

msgid "Organisation unit"
msgstr "Organisation unit"

Expand Down Expand Up @@ -706,12 +726,101 @@ msgstr "Change org unit"
msgid "{{level}} level in {{orgunit}}"
msgstr "{{level}} level in {{orgunit}}"

msgid "Sort ascending by {{columnName}} and update"
msgstr "Sort ascending by {{columnName}} and update"

msgid "Sort descending by {{columnName}} and update"
msgstr "Sort descending by {{columnName}} and update"

msgid "Open as Map"
msgstr "Open as Map"

msgid "Visually plot data on a world map. Data elements use separate map layers."
msgstr "Visually plot data on a world map. Data elements use separate map layers."

msgid "Category option combination"
msgstr "Category option combination"

msgid "Absolute deviation"
msgstr "Absolute deviation"

msgid ""
"A measure of the absolute difference between each data point and a central "
"value, usually the mean or median, providing a straightforward "
"understanding of dispersion in the dataset."
msgstr ""
"A measure of the absolute difference between each data point and a central "
"value, usually the mean or median, providing a straightforward "
"understanding of dispersion in the dataset."

msgid ""
"A measure of how far a data point deviates from the median, using the "
"median absolute deviation instead of the standard deviation, making it "
"robust against outliers."
msgstr ""
"A measure of how far a data point deviates from the median, using the "
"median absolute deviation instead of the standard deviation, making it "
"robust against outliers."

msgid "Median"
msgstr "Median"

msgid ""
"The middle value in a dataset when the values are arranged in ascending or "
"descending order. It's a robust measure of central tendency that is less "
"affected by outliers compared to the mean."
msgstr ""
"The middle value in a dataset when the values are arranged in ascending or "
"descending order. It's a robust measure of central tendency that is less "
"affected by outliers compared to the mean."

msgid "Median absolute deviation"
msgstr "Median absolute deviation"

msgid ""
"A robust measure of variability, found by calculating the median of the "
"absolute differences between each data point and the overall median. It's "
"less influenced by outliers compared to other measures like the standard "
"deviation."
msgstr ""
"A robust measure of variability, found by calculating the median of the "
"absolute differences between each data point and the overall median. It's "
"less influenced by outliers compared to other measures like the standard "
"deviation."

msgid "Z-score"
msgstr "Z-score"

msgid ""
"A measure of how many standard deviations a data point is from the mean of "
"a dataset, providing insight into how unusual or typical that data point is "
"relative to the rest of the distribution."
msgstr ""
"A measure of how many standard deviations a data point is from the mean of "
"a dataset, providing insight into how unusual or typical that data point is "
"relative to the rest of the distribution."

msgid "Mean"
msgstr "Mean"

msgid "Average of the value over time."
msgstr "Average of the value over time."

msgid ""
"A measure of how dispersed the data is in relation to the mean. Low "
"standard deviation indicates data are clustered tightly around the mean, "
"and high standard deviation indicates data are more spread out."
msgstr ""
"A measure of how dispersed the data is in relation to the mean. Low "
"standard deviation indicates data are clustered tightly around the mean, "
"and high standard deviation indicates data are more spread out."

msgid "Minimum score threshold"
msgstr "Minimum score threshold"

msgid "Maximum score threshold"
msgstr "Maximum score threshold"

msgid "Not supported when using cumulative values"
msgstr "Not supported when using cumulative values"

Expand Down Expand Up @@ -855,6 +964,12 @@ msgstr ""
msgid "There's a syntax problem with the analytics request."
msgstr "There's a syntax problem with the analytics request."

msgid "No outliers found"
msgstr "No outliers found"

msgid "There were no outliers found for the selected data items and options."
msgstr "There were no outliers found for the selected data items and options."

msgid "or"
msgstr "or"

Expand Down Expand Up @@ -1111,11 +1226,14 @@ msgid "Display a single value. Recommend relative period to show latest data."
msgstr "Display a single value. Recommend relative period to show latest data."

msgid ""
"View the relationship between two data items at a place or time. "
"Recommended for finding outliers."
"Compare the relationship between two data items across multiple places. "
"Recommended for visualizing outliers."
msgstr ""
"View the relationship between two data items at a place or time. "
"Recommended for finding outliers."
"Compare the relationship between two data items across multiple places. "
"Recommended for visualizing outliers."

msgid "Automatically identify extreme outliers based on historical data."
msgstr "Automatically identify extreme outliers based on historical data."

msgid "Weeks per year"
msgstr "Weeks per year"
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@
"start-server-and-test": "^2.0.0"
},
"dependencies": {
"@dhis2/analytics": "^26.3.0",
"@dhis2/analytics": "999.9.9-outlier-table.alpha.4",
"@dhis2/app-runtime": "^3.7.0",
"@dhis2/app-runtime-adapter-d2": "^1.1.0",
"@dhis2/app-service-datastore": "^1.0.0-beta.3",
"@dhis2/d2-i18n": "^1.1.0",
"@dhis2/ui": "^8.7.6",
"@dhis2/ui": "^9.2.0",
"@krakenjs/post-robot": "^11.0.0",
"d2": "^31.9.1",
"decode-uri-component": "^0.2.2",
Expand All @@ -66,5 +66,8 @@
"reselect": "^4.1.7",
"styled-jsx": "^4",
"whatwg-fetch": "^3.6.2"
},
"resolutions": {
"@dhis2/ui": "^9.2.0"
}
}
7 changes: 5 additions & 2 deletions src/PluginWrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import postRobot from '@krakenjs/post-robot'
import debounce from 'lodash-es/debounce'
import PropTypes from 'prop-types'
import React, { useEffect, useLayoutEffect, useState } from 'react'
import { VisualizationPlugin } from './components/VisualizationPlugin/VisualizationPlugin.js'
import { VisualizationPluginWrapper } from './components/VisualizationPlugin/VisualizationPluginWrapper.js'
import { getPWAInstallationStatus } from './modules/getPWAInstallationStatus.js'

const LoadingMask = () => {
Expand Down Expand Up @@ -128,7 +128,10 @@ const PluginWrapper = () => {
cacheNow={propsFromParent.recordOnNextLoad}
isParentCached={propsFromParent.isParentCached}
>
<VisualizationPlugin id={renderId} {...propsFromParent} />
<VisualizationPluginWrapper
id={renderId}
{...propsFromParent}
/>
</CacheableSectionWrapper>
<CssVariables colors spacers elevations />
</div>
Expand Down
11 changes: 11 additions & 0 deletions src/actions/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { getDisabledOptions } from '../modules/disabledOptions.js'
import {
SET_UI,
CLEAR_UI,
CLEAR_UI_DATA_SORTING,
SET_UI_FROM_VISUALIZATION,
SET_UI_TYPE,
SET_UI_DISABLED_OPTIONS,
Expand All @@ -23,6 +24,7 @@ import {
UPDATE_UI_SERIES_ITEM,
SET_UI_OPTION,
SET_UI_OPTION_FONT_STYLE,
SET_UI_DATA_SORTING,
sGetUiType,
sGetUiOptions,
} from '../reducers/ui.js'
Expand Down Expand Up @@ -67,6 +69,15 @@ export const acSetUiOptionFontStyle = (value) => ({
value,
})

export const acClearUiDataSorting = () => ({
type: CLEAR_UI_DATA_SORTING,
})

export const acSetUiDataSorting = (value) => ({
type: SET_UI_DATA_SORTING,
value,
})

export const acUpdateUiSeriesItem = (value) => ({
type: UPDATE_UI_SERIES_ITEM,
value,
Expand Down
91 changes: 89 additions & 2 deletions src/api/analytics.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,25 @@
import {
Analytics,
VIS_TYPE_PIVOT_TABLE,
layoutGetDimensionItems,
VIS_TYPE_PIVOT_TABLE,
DIMENSION_ID_DATA,
DIMENSION_ID_PERIOD,
DAILY,
WEEKLY,
WEEKS_THIS_YEAR,
} from '@dhis2/analytics'
import { getRelativePeriodTypeUsed } from '../modules/analytics.js'
import {
METHOD_MODIFIED_Z_SCORE,
METHOD_STANDARD_Z_SCORE,
OUTLIER_METHOD_PROP,
OUTLIER_THRESHOLD_PROP,
} from '../components/VisualizationOptions/Options/OutliersForOutlierTable.js'
import { OUTLIER_MAX_RESULTS_PROP } from '../components/VisualizationOptions/Options/OutliersMaxResults.js'
import {
getRelativePeriodTypeUsed,
getOutlierTableDimensionIdHeaderMap,
} from '../modules/analytics.js'
import { getSortingFromVisualization } from '../modules/ui.js'

const periodId = DIMENSION_ID_PERIOD

Expand All @@ -24,6 +36,81 @@ export const apiFetchAnalytics = async (dataEngine, visualization, options) => {
return [new analyticsEngine.response(rawResponse)]
}

export const getAnalyticsRequestForOutlierTable = ({
analyticsEngine,
visualization,
options,
forDownload = false,
}) => {
const dimensionIdHeaderMap = getOutlierTableDimensionIdHeaderMap(options)

const parameters = {
...options,
maxResults: visualization.outlierAnalysis[OUTLIER_MAX_RESULTS_PROP],
algorithm:
visualization.outlierAnalysis[OUTLIER_METHOD_PROP] ===
METHOD_STANDARD_Z_SCORE
? 'Z_SCORE'
: visualization.outlierAnalysis[OUTLIER_METHOD_PROP],
threshold: visualization.outlierAnalysis[OUTLIER_THRESHOLD_PROP],
}

const columns = visualization.columns || []
const headers = []

columns.forEach(({ dimension, items }) => {
parameters[dimension] = items.map(({ id }) => id).join(',')

headers.push(forDownload ? dimension : dimensionIdHeaderMap[dimension])

if (dimension === DIMENSION_ID_DATA) {
headers.push('cocname')
}
})

headers.push('value')

switch (visualization.outlierAnalysis.outlierMethod) {
case METHOD_MODIFIED_Z_SCORE:
headers.push('median', 'modifiedzscore', 'medianabsdeviation')
break
case METHOD_STANDARD_Z_SCORE:
headers.push('mean', 'zscore', 'stddev')
}

headers.push('lowerbound', 'upperbound')

parameters.headers = headers.join(',')

// sorting
const sorting = getSortingFromVisualization(visualization)

if (sorting) {
parameters.orderBy = sorting.dimension
parameters.sortOrder = sorting.direction
}

return new analyticsEngine.request().withParameters(parameters)
}

export const apiFetchAnalyticsForOutlierTable = async (
dataEngine,
visualization,
options
) => {
const analyticsEngine = Analytics.getAnalytics(dataEngine)

const req = getAnalyticsRequestForOutlierTable({
analyticsEngine,
visualization,
options,
})

const rawResponse = await analyticsEngine.aggregate.getOutliersData(req)

return [new analyticsEngine.response(rawResponse)]
}

export const apiFetchAnalyticsForYearOverYear = async (
dataEngine,
visualization,
Expand Down
Loading

0 comments on commit b4a5d53

Please sign in to comment.