Skip to content

Commit

Permalink
Add api integration for keyfigures,charts
Browse files Browse the repository at this point in the history
  • Loading branch information
roshni73 committed Dec 19, 2024
1 parent 9976005 commit 719a86d
Show file tree
Hide file tree
Showing 5 changed files with 59 additions and 152 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"namespace": "operationalLearning",
"strings": {
"downloadMapTitle": "Operational learning map",
"learningLegendLabel": "Learnings"
"learningLegendLabel": "Learnings",
"learningCountLegendLabel":"Learning count"
}
}
66 changes: 27 additions & 39 deletions app/src/views/OperationalLearning/OperationalLearningMap/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@ import {
adminFillLayerOptions,
getPointCircleHaloPaint,
} from '#utils/map';
import { useRequest } from '#utils/restRequest';

import i18n from './i18n.json';
import styles from './styles.module.css';

const sourceOptions: mapboxgl.GeoJSONSourceRaw = {
type: 'geojson',
};

interface CountryProperties {
country_id: number;
name: string;
Expand All @@ -51,7 +53,6 @@ interface ClickedPoint {
interface Props {
className?: string;
}

const LEARNINGS_LOW_COLOR = COLOR_LIGHT_BLUE;
const LEARNINGS_HIGH_COLOR = COLOR_BLUE;

Expand All @@ -64,50 +65,25 @@ function OperationalLearningMap(props: Props) {
clickedPointProperties,
setClickedPointProperties,
] = useState<ClickedPoint | undefined>();

const {
response: learningStatResponse,
} = useRequest({
url: '/api/v2/ops-learning/stats/',
query: {},
});
const countryResponse = useCountry();
const countryCentroidGeoJson = useMemo(
(): GeoJSON.FeatureCollection<GeoJSON.Geometry> => {
const learning_by_country = [
{
country_name: 'Afghanistan',
country_id: 14,
operation_count: 40,
},
{
country_name: 'Albania',
country_id: 15,
operation_count: 10,
},
{
country_name: 'Argentina',
country_id: 20,
operation_count: 29,
},
{
country_name: 'Australia',
country_id: 22,
operation_count: 11,
},
{
country_name: 'Belgium',
country_id: 30,
operation_count: 222,
},
{
country_name: 'Canada',
country_id: 42,
operation_count: 1,
},
];
const features = countryResponse
?.map((country) => {
const learningList = learning_by_country.find(
(item) => item.country_id === country.id,
const learningList = learningStatResponse?.learning_by_country?.find(
(item: { country_id: number; }) => item.country_id === country.id,
);
if (isNotDefined(learningList)) {
return undefined;
}
const units = learningList.operation_count;
const units = learningList.count;
return {
type: 'Feature' as const,
geometry: country.centroid as {
Expand All @@ -127,10 +103,20 @@ function OperationalLearningMap(props: Props) {
features,
};
},
[countryResponse],
[countryResponse, learningStatResponse],
);
const learningCount = useMemo(() => (
learningStatResponse?.learning_by_country
.filter((country) => country.count > 0)
), [learningStatResponse?.learning_by_country]);

const maxScaleValue = useMemo(() => (
Math.max(
...(learningCount
?.map((activity: { count: number; }) => activity.count)
.filter(isDefined) ?? []),
)), [learningCount]);

const maxScaleValue = 10; // FIX ME
const {
bluePointHaloCirclePaint,
} = useMemo(
Expand Down Expand Up @@ -164,6 +150,7 @@ function OperationalLearningMap(props: Props) {
},
[setClickedPointProperties],
);

return (
<Container
className={_cs(styles.learningMap, className)}
Expand All @@ -181,7 +168,7 @@ function OperationalLearningMap(props: Props) {
value={0}
/>
<NumberOutput
value={100} // FIXME:ADD MAX LEARNINGS
value={100}
/>
</div>
</div>
Expand Down Expand Up @@ -239,6 +226,7 @@ function OperationalLearningMap(props: Props) {
>
<TextOutput
value={clickedPointProperties.feature.properties.operation_count}
label={strings.learningCountLegendLabel}
valueType="number"
/>
</Container>
Expand Down
6 changes: 3 additions & 3 deletions app/src/views/OperationalLearning/i18n.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@
"sourcesUsed": "Sources Used",
"learningExtract": "Learning Extracts",
"sectorsCovered": "Sectors Covered",
"learningBySector": "learnings by sectors",
"learningByRegions": "learnings by regions",
"sourceOvertime": "Sources Overtime"
"learningBySector": "Learning by sectors",
"learningByRegions": "Learning by regions",
"sourceOvertime": "Sources overtime"
}
}
129 changes: 26 additions & 103 deletions app/src/views/OperationalLearning/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,6 @@ type QueryType = Pick<
| 'per_component_validated__in'
| 'search_extracts'
>;

const regionKeySelector = (region: RegionOption) => region.key;
const countryKeySelector = (country: Country) => country.id;
const sectorKeySelector = (d: SecondarySector) => d.key;
Expand All @@ -103,86 +102,7 @@ const perComponentKeySelector = (option: PerComponent) => option.id;
const disasterTypeKeySelector = (type: DisasterType) => type.id;
const disasterTypeLabelSelector = (type: DisasterType) => type.name ?? '?';

const responseData = {
operations_included: 9,
learning_extracts: 6,
sectors_covered: 6,
sources_used: 8,
learning_by_region: [
{
region_name: 'Americas',
region_id: 1,
count: 2,
},
{
region_name: 'Asia Pacific',
region_id: 2,
count: 5,
},
{
region_name: 'Europe',
region_id: 3,
count: 2,
},
],
learning_by_sector: [
{
id: 17,
count: 1,
title: 'health',
},
{
id: 18,
count: 1,
title: 'education',
},
{
id: 19,
count: 3,
title: 'Livelihoods and basic needs',
},
{
id: 20,
count: 4,
title: 'Migration',
},
{
id: 21,
count: 1,
title: 'WASH',
},
{
id: 22,
count: 1,
title: 'Shelter',
},
],
sources_overtime: {
DREF: [
{ year: 2023, count: 1 },
{ year: 2024, count: 3 },
],
'Emergency Appeal': [
{ year: 2023, count: 1 },
{ year: 2024, count: 1 },
],
'International Appeal': [
{ year: 2023, count: 1 },
{ year: 2024, count: 1 },
],
'Forecast Based Action': [
{ year: 2022, count: 1 },
],
},
};

const timeSeriesDataKeys = Object.entries(
responseData.sources_overtime,
).flatMap(([source, entries]) => entries.map((entry) => ({
date: `${entry.year}-01-01`,
value: entry.count,
source,
})));
const timeSeriesDataKeys = ['Learnings'];

const oneYearAgo = new Date();
oneYearAgo.setFullYear(oneYearAgo.getFullYear() - 1);
Expand Down Expand Up @@ -394,14 +314,26 @@ export function Component() {
setQuery(undefined);
}, [resetFilter]);

const {
response: learningStatsResponse,
} = useRequest({
url: '/api/v2/ops-learning/stats/',
query: {
...query,
},
});
const timeSeriesValueSelector = useCallback(
(_: string, date: Date) => {
const entry = timeSeriesDataKeys?.find(
(source) => getFormattedDateKey(source.date) === getFormattedDateKey(date),
);
return entry ? entry.value : undefined;
if (!Array.isArray(learningStatsResponse)) {
return 0;
}
return learningStatsResponse.find(
(source: { date: number; }) => (
getFormattedDateKey(source.date) === getFormattedDateKey(date)
),
)?.count ?? 0;
},
[],
[learningStatsResponse],
);

return (
Expand Down Expand Up @@ -566,45 +498,42 @@ export function Component() {
</>
)}
/>
<div
className={styles.keyFigureCardList}
>
<div className={styles.keyFigureCardList}>
<div className={styles.keyFigureCard}>
<KeyFigure
className={styles.keyFigure}
value={responseData.operations_included}
value={learningStatsResponse?.operations_included}
label={strings.operationsIncluded}
labelClassName={styles.keyFigureDescription}
/>
<div className={styles.separator} />
<KeyFigure
className={styles.keyFigure}
value={responseData.sources_used}
value={learningStatsResponse?.sources_used}
label={strings.sourcesUsed}
labelClassName={styles.keyFigureDescription}
/>
<div className={styles.separator} />
<KeyFigure
className={styles.keyFigure}
value={responseData.learning_extracts}
value={learningStatsResponse?.learning_extracts}
label={strings.learningExtract}
labelClassName={styles.keyFigureDescription}
/>
<div className={styles.separator} />
<KeyFigure
className={styles.keyFigure}
value={responseData.sectors_covered}
value={learningStatsResponse?.sectors_covered}
label={strings.sectorsCovered}
labelClassName={styles.keyFigureDescription}
/>
</div>
</div>

<div
className={styles.learningOverview}
>
<OperationalLearningMap
className={styles.mapConatiner}
className={styles.mapContainer}
/>
<div className={styles.charts}>
<Container
Expand All @@ -614,7 +543,7 @@ export function Component() {
withInternalPadding
>
<BarChart
data={responseData.learning_by_sector}
data={learningStatsResponse?.learning_by_sector}
keySelector={sectorsKeySelector}
valueSelector={sectorsValueSelector}
labelSelector={sectorsLabelSelector}
Expand All @@ -627,7 +556,7 @@ export function Component() {
withInternalPadding
>
<BarChart
data={responseData.learning_by_region}
data={learningStatsResponse?.learning_by_region}
keySelector={regionsKeySelector}
valueSelector={regionValueSelector}
labelSelector={regionLabelSelector}
Expand All @@ -638,12 +567,6 @@ export function Component() {
className={styles.learningChart}
withHeaderBorder
withInternalPadding
footerContent={(
<div className={styles.footer}>
<div className={styles.legend} />
</div>

)}
>
<TimeSeriesChart
className={styles.timeSeriesChart}
Expand Down
7 changes: 1 addition & 6 deletions app/src/views/OperationalLearning/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@
border-radius: var(--go-ui-border-radius-lg);
box-shadow: var(--go-ui-box-shadow-md);
background-color: var(--go-ui-color-white);
padding: var(--go-ui-spacing-md);
padding: var(--go-ui-spacing-xl);

.separator {
flex-shrink: 0;
Expand All @@ -111,11 +111,6 @@
display: grid;
grid-gap: var(--go-ui-spacing-md);
grid-template-columns: 5fr 2fr;

.map-container {
grid-column: 1 / 2;
grid-row: 1 / 2;
}

.charts {
display: grid;
Expand Down

0 comments on commit 719a86d

Please sign in to comment.