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

PADV-1050 - Integrate Metric Services #45

Merged
merged 1 commit into from
Mar 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,4 @@ ACCOUNT_PROFILE_URL='http://localhost:1995/u'
MFE_CONFIG_API_URL='http://localhost:18000/api/mfe_config/v1'
INSTITUTION_PORTAL_PATH=''
COURSE_OPERATIONS_API_V2_BASE_URL='http://localhost:18000/pearson_course_operation/api/v2'
COURSE_OPERATIONS_API_METRICS_BASE_URL='http://localhost:18000/pearson_course_operation/api/metrics'
sergivalero20 marked this conversation as resolved.
Show resolved Hide resolved
26 changes: 18 additions & 8 deletions src/features/Students/StudentsMetrics/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,34 @@ import React, { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { Card, CardGrid, ToggleButton } from '@edx/paragon';
import { ToggleButtonGroup } from 'react-paragon-topaz';
import { fetchMetricsData } from 'features/Students/data/thunks';
import { fetchClassesMetricsData, fetchStudentsMetricsData } from 'features/Students/data/thunks';
import { daysWeek } from 'features/constants';

import 'features/Students/StudentsMetrics/index.scss';

const StudentsMetrics = () => {
const dispatch = useDispatch();
const stateMetrics = useSelector((state) => state.students.metrics.data);
const institution = useSelector((state) => state.main.selectedInstitution);
const studentsMetrics = useSelector((state) => state.students.studentsMetrics.data);
const classesMetrics = useSelector((state) => state.students.classesMetrics.data);

useEffect(() => {
dispatch(fetchMetricsData());
}, [dispatch]);
if (Object.keys(institution).length > 0) {
dispatch(fetchStudentsMetricsData(institution.id, daysWeek));
dispatch(fetchClassesMetricsData(institution.id, daysWeek));
}
}, [institution, dispatch]);

return (
<div className="container-cards d-flex flex-column">
<ToggleButtonGroup type="radio" defaultValue={1} name="period">
<ToggleButton id="tbg-radio-1" value={1} variant="outline-primary">
This week
</ToggleButton>
<ToggleButton id="tbg-radio-2" value={2} variant="outline-primary">
<ToggleButton id="tbg-radio-2" value={2} variant="outline-primary" disabled> {/* Temporarily disabled */}
Next week
</ToggleButton>
<ToggleButton id="tbg-radio-3" value={3} variant="outline-primary">
<ToggleButton id="tbg-radio-3" value={3} variant="outline-primary" disabled> {/* Temporarily disabled */}
Next month
</ToggleButton>
</ToggleButtonGroup>
Expand All @@ -39,15 +45,19 @@ const StudentsMetrics = () => {
title="New students registered"
/>
<Card.Section>
<div className="card-number">{stateMetrics.newStudentsRegistered}</div>
<p className="card-number">
{studentsMetrics.numberOfEnrollments ? studentsMetrics.numberOfEnrollments : '-'}
</p>
</Card.Section>
</Card>
<Card className="card-green">
<Card.Header
title="Classes scheduled"
/>
<Card.Section>
<div className="card-number">{stateMetrics.classesScheduled}</div>
<p className="card-number">
{classesMetrics.numberOfClassesCreated ? classesMetrics.numberOfClassesCreated : '-'}
</p>
</Card.Section>
</Card>
</CardGrid>
Expand Down
17 changes: 15 additions & 2 deletions src/features/Students/StudentsPage/_test_/index.test.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,19 @@ const mockStore = {
num_pages: 1,
current_page: 1,
},
metrics: {
data: [],
classesMetrics: {
data: [
{
numberOfClassesCreated: 10,
},
],
},
studentsMetrics: {
data: [
{
numberOfEnrollments: 2,
},
],
},
classes: {
data: [],
Expand Down Expand Up @@ -75,6 +86,8 @@ describe('StudentsPage', () => {
expect(component.container).toHaveTextContent('Instructor 2');
expect(component.container).toHaveTextContent('active');
expect(component.container).toHaveTextContent('pending');
expect(component.container).toHaveTextContent('10');
expect(component.container).toHaveTextContent('2');
});
});
});
75 changes: 65 additions & 10 deletions src/features/Students/data/_test_/redux.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {
fetchStudentsData,
fetchCoursesData,
fetchClassesData,
fetchMetricsData,
fetchClassesMetricsData,
fetchStudentsMetricsData,
} from 'features/Students/data/thunks';
import { updateCurrentPage, updateFilters } from 'features/Students/data/slice';
import { executeThunk } from 'test-utils';
Expand Down Expand Up @@ -170,24 +171,78 @@ describe('Students redux tests', () => {
.toEqual('error');
});

test('successful fetch metrics data', async () => {
const mockResponse = {
classesScheduled: '71%',
newStudentsRegistered: '367',
};
test('successful fetch classes metrics data', async () => {
const classesMetricsApiUrl = `${process.env.COURSE_OPERATIONS_API_METRICS_BASE_URL}/classes-number/`;
const mockResponse = { numberOfClassesCreated: 71 };
axiosMock.onGet(classesMetricsApiUrl)
.reply(200, mockResponse);

expect(store.getState().students.metrics.status)
expect(store.getState().students.classesMetrics.status)
.toEqual('loading');

await executeThunk(fetchMetricsData(), store.dispatch, store.getState);
await executeThunk(fetchClassesMetricsData(1, 2), store.dispatch, store.getState);

expect(store.getState().students.metrics.data)
expect(store.getState().students.classesMetrics.data)
.toEqual(mockResponse);

expect(store.getState().students.metrics.status)
expect(store.getState().students.classesMetrics.status)
.toEqual('success');
});

test('failed fetch classes metrics data', async () => {
const classesMetricsApiUrl = `${process.env.COURSE_OPERATIONS_API_METRICS_BASE_URL}/classes-number/`;

axiosMock.onGet(classesMetricsApiUrl)
.reply(500);

expect(store.getState().students.classesMetrics.status)
.toEqual('loading');

await executeThunk(fetchClassesMetricsData(1, 2), store.dispatch, store.getState);

expect(store.getState().students.classesMetrics.data)
.toEqual([]);

expect(store.getState().students.classesMetrics.status)
.toEqual('error');
});

test('successful fetch students metrics data', async () => {
const studentsMetricsApiUrl = `${process.env.COURSE_OPERATIONS_API_METRICS_BASE_URL}/students-number/`;
const mockResponse = { numberOfEnrollments: 20 };
axiosMock.onGet(studentsMetricsApiUrl)
.reply(200, mockResponse);

expect(store.getState().students.studentsMetrics.status)
.toEqual('loading');

await executeThunk(fetchStudentsMetricsData(1, 2), store.dispatch, store.getState);

expect(store.getState().students.studentsMetrics.data)
.toEqual(mockResponse);

expect(store.getState().students.studentsMetrics.status)
.toEqual('success');
});

test('failed fetch students metrics data', async () => {
const studentsMetricsApiUrl = `${process.env.COURSE_OPERATIONS_API_METRICS_BASE_URL}/students-number/`;

axiosMock.onGet(studentsMetricsApiUrl)
.reply(500);

expect(store.getState().students.studentsMetrics.status)
.toEqual('loading');

await executeThunk(fetchStudentsMetricsData(1, 2), store.dispatch, store.getState);

expect(store.getState().students.studentsMetrics.data)
.toEqual([]);

expect(store.getState().students.studentsMetrics.status)
.toEqual('error');
});

test('update current page', () => {
const newPage = 2;
const intialState = store.getState().students.table;
Expand Down
31 changes: 23 additions & 8 deletions src/features/Students/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,18 +24,33 @@ function handleEnrollments(data, courseId) {
);
}

function getMetricsStudents() {
const metricsData = {
data: {
new_students_registered: '367',
classes_scheduled: '71%',
},
function getStudentsMetrics(institutionId, days) {
const params = {
institution_id: institutionId,
days,
};
return metricsData;

return getAuthenticatedHttpClient().get(
`${getConfig().COURSE_OPERATIONS_API_METRICS_BASE_URL}/students-number/`,
{ params },
);
}

function getClassesMetrics(institutionId, days) {
const params = {
institution_id: institutionId,
days,
};

return getAuthenticatedHttpClient().get(
`${getConfig().COURSE_OPERATIONS_API_METRICS_BASE_URL}/classes-number/`,
{ params },
);
}

export {
getStudentbyInstitutionAdmin,
handleEnrollments,
getMetricsStudents,
getStudentsMetrics,
getClassesMetrics,
};
38 changes: 27 additions & 11 deletions src/features/Students/data/slice.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ const initialState = {
classes: {
...initialStateService,
},
metrics: {
classesMetrics: {
...initialStateService,
},
studentsMetrics: {
...initialStateService,
},
filters: {},
Expand Down Expand Up @@ -66,15 +69,25 @@ export const studentsSlice = createSlice({
fetchClassesDataFailed: (state) => {
state.classes.status = RequestStatus.ERROR;
},
fetchMetricsDataRequest: (state) => {
state.metrics.status = RequestStatus.LOADING;
fetchClassesMetricsDataRequest: (state) => {
state.classesMetrics.status = RequestStatus.LOADING;
},
fetchClassesMetricsDataSuccess: (state, { payload }) => {
state.classesMetrics.status = RequestStatus.SUCCESS;
state.classesMetrics.data = payload;
},
fetchClassesMetricsDataFailed: (state) => {
state.classesMetrics.status = RequestStatus.ERROR;
},
fetchStudentsMetricsDataRequest: (state) => {
state.studentsMetrics.status = RequestStatus.LOADING;
},
fetchMetricsDataSuccess: (state, { payload }) => {
state.metrics.status = RequestStatus.SUCCESS;
state.metrics.data = payload;
fetchStudentsMetricsDataSuccess: (state, { payload }) => {
state.studentsMetrics.status = RequestStatus.SUCCESS;
state.studentsMetrics.data = payload;
},
fetchMetricsDataFailed: (state) => {
state.metrics.status = RequestStatus.ERROR;
fetchStudentsMetricsDataFailed: (state) => {
state.studentsMetrics.status = RequestStatus.ERROR;
},
},
});
Expand All @@ -91,9 +104,12 @@ export const {
fetchClassesDataRequest,
fetchClassesDataSuccess,
fetchClassesDataFailed,
fetchMetricsDataRequest,
fetchMetricsDataSuccess,
fetchMetricsDataFailed,
fetchClassesMetricsDataRequest,
fetchClassesMetricsDataSuccess,
fetchClassesMetricsDataFailed,
fetchStudentsMetricsDataRequest,
fetchStudentsMetricsDataSuccess,
fetchStudentsMetricsDataFailed,
} = studentsSlice.actions;

export const { reducer } = studentsSlice;
42 changes: 32 additions & 10 deletions src/features/Students/data/thunks.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,18 @@ import {
fetchClassesDataRequest,
fetchClassesDataSuccess,
fetchClassesDataFailed,
fetchMetricsDataRequest,
fetchMetricsDataSuccess,
fetchMetricsDataFailed,
fetchClassesMetricsDataRequest,
fetchClassesMetricsDataSuccess,
fetchClassesMetricsDataFailed,
fetchStudentsMetricsDataRequest,
fetchStudentsMetricsDataSuccess,
fetchStudentsMetricsDataFailed,
} from 'features/Students/data/slice';
import { getStudentbyInstitutionAdmin, getMetricsStudents } from 'features/Students/data/api';
import {
getClassesMetrics,
getStudentsMetrics,
getStudentbyInstitutionAdmin,
} from 'features/Students/data/api';
import { getCoursesByInstitution, getClassesByInstitution } from 'features/Common/data/api';

function fetchStudentsData(id, currentPage, filtersData) {
Expand Down Expand Up @@ -59,15 +66,29 @@ function fetchClassesData(id, courseName) {
};
}

function fetchMetricsData() {
function fetchClassesMetricsData(institutionId, days) {
return async (dispatch) => {
dispatch(fetchClassesMetricsDataRequest());

try {
const response = camelCaseObject(await getClassesMetrics(institutionId, days));
dispatch(fetchClassesMetricsDataSuccess(response.data));
} catch (error) {
dispatch(fetchClassesMetricsDataFailed());
logError(error);
}
};
}

function fetchStudentsMetricsData(institutionId, days) {
return async (dispatch) => {
dispatch(fetchMetricsDataRequest());
dispatch(fetchStudentsMetricsDataRequest());

try {
const response = camelCaseObject(await getMetricsStudents());
dispatch(fetchMetricsDataSuccess(response.data));
const response = camelCaseObject(await getStudentsMetrics(institutionId, days));
dispatch(fetchStudentsMetricsDataSuccess(response.data));
} catch (error) {
dispatch(fetchMetricsDataFailed());
dispatch(fetchStudentsMetricsDataFailed());
logError(error);
}
};
Expand All @@ -77,5 +98,6 @@ export {
fetchStudentsData,
fetchCoursesData,
fetchClassesData,
fetchMetricsData,
fetchClassesMetricsData,
fetchStudentsMetricsData,
};
Loading