Skip to content

Commit

Permalink
feat: Updated completions and engagments tabs in analytics according …
Browse files Browse the repository at this point in the history
…to updates on the backend.
  • Loading branch information
saleem-latif committed Sep 23, 2024
1 parent aefd6bd commit 3401429
Show file tree
Hide file tree
Showing 6 changed files with 297 additions and 69 deletions.
2 changes: 2 additions & 0 deletions src/components/AdvanceAnalyticsV2/data/hooks/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import { advanceAnalyticsQueryKeys } from '../constants';
import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';

export { default as useEnterpriseEnrollmentsData } from './useEnterpriseEnrollmentsData';
export { default as useEnterpriseEngagementData } from './useEnterpriseEngagementData';
export { default as useEnterpriseCompletionsData } from './useEnterpriseCompletionsData';

export const useEnterpriseAnalyticsData = ({
enterpriseCustomerUUID,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useQuery } from '@tanstack/react-query';
import _ from 'lodash';

import { useMemo } from 'react';
import { ANALYTICS_TABS, generateKey } from '../constants';
import { applyGranularity, applyCalculation } from '../utils';
import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';

/**
Applies data transformations to the response data.
Apply transformations to the response data based on the granularity and calculation. Data transformation is applied
only on the items with the allowed enrollment types.
* @param {object} response - The response data returned from the API.
* @param {OpUnitType} granularity - Granularity of the data. e.g. `day`, `week`, `month`, `quarter`, `year`.
* @param {String} calculation - Calculation to apply on the data. e.g.
* `total`, `running_total`, `moving_average_3_periods`, `moving_average_7_periods`.
* @param {Array} allowedEnrollTypes - Allowed enrollment types to consider. e.g. [`certificate`, `audit`].
*/
const applyDataTransformations = (response, granularity, calculation, allowedEnrollTypes = ['certificate', 'audit']) => {
const modifiedResponse = _.cloneDeep(response);
if (modifiedResponse?.data?.completionsOverTime) {
let completionsOverTime = [];
for (let i = 0; i < allowedEnrollTypes.length; i++) {
const data = applyGranularity(
modifiedResponse.data.completionsOverTime.filter(
completion => completion.enrollType === allowedEnrollTypes[i],

Check warning on line 28 in src/components/AdvanceAnalyticsV2/data/hooks/useEnterpriseCompletionsData.js

View check run for this annotation

Codecov / codecov/patch

src/components/AdvanceAnalyticsV2/data/hooks/useEnterpriseCompletionsData.js#L28

Added line #L28 was not covered by tests
),
'passedDate',
'completionCount',
granularity,
);
completionsOverTime = completionsOverTime.concat(
applyCalculation(data, 'completionCount', calculation),
);
}

modifiedResponse.data.completionsOverTime = completionsOverTime;
}

return modifiedResponse;
};

/**
Fetches enterprise completion data.
* @param {String} enterpriseCustomerUUID - UUID of the enterprise customer.
* @param {Date} startDate - Start date for the data.
* @param {Date} endDate - End date for the data.
* @param {OpUnitType} granularity - Granularity of the data. e.g. `day`, `week`, `month`, `quarter`, `year`.
* @param {String} calculation - Calculation to apply on the data. e.g.
* `total`, `running_total`, `moving_average_3_periods`, `moving_average_7_periods`.
* @param {object} queryOptions - Additional options for the query.
*/
const useEnterpriseCompletionsData = ({
enterpriseCustomerUUID,
startDate,
endDate,
granularity = undefined,
calculation = undefined,

Check warning on line 61 in src/components/AdvanceAnalyticsV2/data/hooks/useEnterpriseCompletionsData.js

View check run for this annotation

Codecov / codecov/patch

src/components/AdvanceAnalyticsV2/data/hooks/useEnterpriseCompletionsData.js#L60-L61

Added lines #L60 - L61 were not covered by tests
queryOptions = {},
}) => {
const requestOptions = { startDate, endDate };
const response = useQuery({
queryKey: generateKey('completions', enterpriseCustomerUUID, requestOptions),
queryFn: () => EnterpriseDataApiService.fetchAdminAnalyticsData(
enterpriseCustomerUUID,
ANALYTICS_TABS.COMPLETIONS,
requestOptions,
),
staleTime: 0.5 * (1000 * 60 * 60), // 30 minutes. The time in milliseconds after data is considered stale.
cacheTime: 0.75 * (1000 * 60 * 60), // 45 minutes. Cache data will be garbage collected after this duration.
keepPreviousData: true,
...queryOptions,
});

return useMemo(() => applyDataTransformations(
response,
granularity,
calculation,
), [response, granularity, calculation]);
};

export default useEnterpriseCompletionsData;
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { useQuery } from '@tanstack/react-query';
import _ from 'lodash';

import { useMemo } from 'react';
import { ANALYTICS_TABS, generateKey } from '../constants';
import { applyGranularity, applyCalculation } from '../utils';
import EnterpriseDataApiService from '../../../../data/services/EnterpriseDataApiService';

/**
Applies data transformations to the response data.
Apply transformations to the response data based on the granularity and calculation. Data transformation is applied
only on the items with the allowed enrollment types.
* @param {object} response - The response data returned from the API.
* @param {OpUnitType} granularity - Granularity of the data. e.g. `day`, `week`, `month`, `quarter`, `year`.
* @param {String} calculation - Calculation to apply on the data. e.g.
* `total`, `running_total`, `moving_average_3_periods`, `moving_average_7_periods`.
* @param {Array} allowedEnrollTypes - Allowed enrollment types to consider. e.g. [`certificate`, `audit`].
*/
const applyDataTransformations = (response, granularity, calculation, allowedEnrollTypes = ['certificate', 'audit']) => {
const modifiedResponse = _.cloneDeep(response);
if (modifiedResponse?.data?.engagementOverTime) {
let engagementOverTime = [];
for (let i = 0; i < allowedEnrollTypes.length; i++) {
const data = applyGranularity(
modifiedResponse.data.engagementOverTime.filter(
engagement => engagement.enrollType === allowedEnrollTypes[i],

Check warning on line 28 in src/components/AdvanceAnalyticsV2/data/hooks/useEnterpriseEngagementData.js

View check run for this annotation

Codecov / codecov/patch

src/components/AdvanceAnalyticsV2/data/hooks/useEnterpriseEngagementData.js#L28

Added line #L28 was not covered by tests
),
'activityDate',
'learningTimeHours',
granularity,
);
engagementOverTime = engagementOverTime.concat(
applyCalculation(data, 'learningTimeHours', calculation),
);
}

modifiedResponse.data.engagementOverTime = engagementOverTime;
}

return modifiedResponse;
};

/**
Fetches enterprise engagement data.
* @param {String} enterpriseCustomerUUID - UUID of the enterprise customer.
* @param {Date} startDate - Start date for the data.
* @param {Date} endDate - End date for the data.
* @param {OpUnitType} granularity - Granularity of the data. e.g. `day`, `week`, `month`, `quarter`, `year`.
* @param {String} calculation - Calculation to apply on the data. e.g.
* `total`, `running_total`, `moving_average_3_periods`, `moving_average_7_periods`.
* @param {object} queryOptions - Additional options for the query.
*/
const useEnterpriseEngagementData = ({
enterpriseCustomerUUID,
startDate,
endDate,
granularity = undefined,
calculation = undefined,

Check warning on line 61 in src/components/AdvanceAnalyticsV2/data/hooks/useEnterpriseEngagementData.js

View check run for this annotation

Codecov / codecov/patch

src/components/AdvanceAnalyticsV2/data/hooks/useEnterpriseEngagementData.js#L60-L61

Added lines #L60 - L61 were not covered by tests
queryOptions = {},
}) => {
const requestOptions = { startDate, endDate };
const response = useQuery({
queryKey: generateKey('engagements', enterpriseCustomerUUID, requestOptions),
queryFn: () => EnterpriseDataApiService.fetchAdminAnalyticsData(
enterpriseCustomerUUID,
ANALYTICS_TABS.ENGAGEMENTS,
requestOptions,
),
staleTime: 0.5 * (1000 * 60 * 60), // 30 minutes. The time in milliseconds after data is considered stale.
cacheTime: 0.75 * (1000 * 60 * 60), // 45 minutes. Cache data will be garbage collected after this duration.
keepPreviousData: true,
...queryOptions,
});

return useMemo(() => applyDataTransformations(
response,
granularity,
calculation,
), [response, granularity, calculation]);
};

export default useEnterpriseEngagementData;
94 changes: 61 additions & 33 deletions src/components/AdvanceAnalyticsV2/tabs/Completions.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from 'react';
import React, { useMemo } from 'react';
import { useIntl } from '@edx/frontend-platform/i18n';
import PropTypes from 'prop-types';
import dayjs from 'dayjs';
import Header from '../Header';
import { ANALYTICS_TABS, CHART_TYPES, chartColorMap } from '../data/constants';
import { ANALYTICS_TABS, chartColorMap } from '../data/constants';
import AnalyticsTable from './AnalyticsTable';
import ChartWrapper from '../charts/ChartWrapper';
import { useEnterpriseAnalyticsData } from '../data/hooks';
import DownloadCSV from '../DownloadCSV';
import { constructChartHoverTemplate } from '../data/utils';
import { useEnterpriseCompletionsData } from '../data/hooks';
import DownloadCSVButton from '../DownloadCSVButton';
import { constructChartHoverTemplate, modifyDataToIntroduceEnrollTypeCount } from '../data/utils';

const Completions = ({
startDate, endDate, granularity, calculation, enterpriseId,
Expand All @@ -16,7 +17,7 @@ const Completions = ({

const {
isFetching, isError, data,
} = useEnterpriseAnalyticsData({
} = useEnterpriseCompletionsData({
enterpriseCustomerUUID: enterpriseId,
key: ANALYTICS_TABS.COMPLETIONS,
startDate,
Expand All @@ -25,6 +26,48 @@ const Completions = ({
calculation,
});

const completionsOverTimeForCSV = useMemo(() => {
const completionsOverTime = modifyDataToIntroduceEnrollTypeCount(
data?.completionsOverTime,
'passedDate',
'completionCount',
);
return completionsOverTime.map(({ activityDate, certificate, audit }) => ({
activity_date: dayjs.utc(activityDate).toISOString().split('T')[0],
certificate,
audit,
}));
}, [data]);

const topCoursesByCompletionForCSV = useMemo(() => {
const topCoursesByCompletions = modifyDataToIntroduceEnrollTypeCount(
data?.topCoursesByCompletions,
'courseKey',
'completionCount',
);
return topCoursesByCompletions.map(({
courseKey, courseTitle, certificate, audit,
}) => ({

Check warning on line 50 in src/components/AdvanceAnalyticsV2/tabs/Completions.jsx

View check run for this annotation

Codecov / codecov/patch

src/components/AdvanceAnalyticsV2/tabs/Completions.jsx#L50

Added line #L50 was not covered by tests
course_key: courseKey,
course_title: courseTitle,
certificate,
audit,
}));
}, [data]);

const topSubjectsByCompletionsForCSV = useMemo(() => {
const topSubjectsByCompletions = modifyDataToIntroduceEnrollTypeCount(
data?.topSubjectsByCompletions,
'courseSubject',
'completionCount',
);
return topSubjectsByCompletions.map(({ courseSubject, certificate, audit }) => ({
course_subject: courseSubject,
certificate,
audit,
}));
}, [data]);

return (
<div className="tab-completions mt-4">
<div className="completions-over-time-chart-container mb-4">
Expand All @@ -40,14 +83,9 @@ const Completions = ({
description: 'Subtitle for the completions over time chart.',
})}
DownloadCSVComponent={(
<DownloadCSV
enterpriseId={enterpriseId}
startDate={startDate}
endDate={endDate}
activeTab={ANALYTICS_TABS.COMPLETIONS}
granularity={granularity}
calculation={calculation}
chartType={CHART_TYPES.COMPLETIONS_OVER_TIME}
<DownloadCSVButton
jsonData={completionsOverTimeForCSV}
csvFileName={`Completions over time - ${startDate} - ${endDate} (${granularity} ${calculation})`}
/>
)}
/>
Expand All @@ -58,7 +96,7 @@ const Completions = ({
chartProps={{
data: data?.completionsOverTime,
xKey: 'passedDate',
yKey: 'count',
yKey: 'completionCount',
colorKey: 'enrollType',
colorMap: chartColorMap,
xAxisTitle: '',
Expand Down Expand Up @@ -88,14 +126,9 @@ const Completions = ({
description: 'Subtitle for the top 10 courses by completions chart.',
})}
DownloadCSVComponent={(
<DownloadCSV
enterpriseId={enterpriseId}
startDate={startDate}
endDate={endDate}
activeTab={ANALYTICS_TABS.COMPLETIONS}
granularity={granularity}
calculation={calculation}
chartType={CHART_TYPES.TOP_COURSES_BY_COMPLETIONS}
<DownloadCSVButton
jsonData={topCoursesByCompletionForCSV}
csvFileName={`Top Courses by Completion - ${startDate} - ${endDate} (${granularity} ${calculation})`}
/>
)}
/>
Expand All @@ -106,7 +139,7 @@ const Completions = ({
chartProps={{
data: data?.topCoursesByCompletions,
xKey: 'courseKey',
yKey: 'count',
yKey: 'completionCount',
colorKey: 'enrollType',
colorMap: chartColorMap,
yAxisTitle: intl.formatMessage({
Expand Down Expand Up @@ -139,14 +172,9 @@ const Completions = ({
description: 'Subtitle for the top 10 subjects by completion chart.',
})}
DownloadCSVComponent={(
<DownloadCSV
enterpriseId={enterpriseId}
startDate={startDate}
endDate={endDate}
activeTab={ANALYTICS_TABS.COMPLETIONS}
granularity={granularity}
calculation={calculation}
chartType={CHART_TYPES.TOP_SUBJECTS_BY_COMPLETIONS}
<DownloadCSVButton
jsonData={topSubjectsByCompletionsForCSV}
csvFileName={`Top Subjects by Completion - ${startDate} - ${endDate} (${granularity} ${calculation})`}
/>
)}
/>
Expand All @@ -157,7 +185,7 @@ const Completions = ({
chartProps={{
data: data?.topSubjectsByCompletions,
xKey: 'courseSubject',
yKey: 'count',
yKey: 'completionCount',
colorKey: 'enrollType',
colorMap: chartColorMap,
yAxisTitle: intl.formatMessage({
Expand Down
Loading

0 comments on commit 3401429

Please sign in to comment.