Merge pull request #98 from edx/matthugs/add-table-for-bulk-management-operation-history
Add table corresponding to what new history endpoint supports
This commit is contained in:
@@ -487,6 +487,14 @@ export default class Gradebook extends React.Component {
|
||||
buttonType="primary"
|
||||
onClick={this.handleClickImportGrades}
|
||||
/>
|
||||
<Table
|
||||
data={this.props.bulkManagementHistory}
|
||||
columns={[
|
||||
{ key: 'user', label: 'Uploaded By', columnSortable: false },
|
||||
{ key: 'operation', label: 'Operation', columnSortable: false },
|
||||
{ key: 'modified', label: 'Uploaded Date', columnSortable: false },
|
||||
]}
|
||||
/>
|
||||
</div>)}
|
||||
</Tabs>
|
||||
</div>
|
||||
@@ -513,6 +521,7 @@ Gradebook.defaultProps = {
|
||||
tracks: [],
|
||||
bulkImportError: '',
|
||||
showBulkManagement: false,
|
||||
bulkManagementHistory: [],
|
||||
};
|
||||
|
||||
Gradebook.propTypes = {
|
||||
@@ -565,4 +574,9 @@ Gradebook.propTypes = {
|
||||
submitFileUploadFormData: PropTypes.func.isRequired,
|
||||
bulkImportError: PropTypes.string,
|
||||
showBulkManagement: PropTypes.bool,
|
||||
bulkManagementHistory: PropTypes.arrayOf(PropTypes.shape({
|
||||
operation: PropTypes.oneOf(['commit', 'error']),
|
||||
user: PropTypes.string,
|
||||
modified: PropTypes.string,
|
||||
})),
|
||||
};
|
||||
|
||||
@@ -13,7 +13,8 @@ import {
|
||||
} from '../../data/actions/grades';
|
||||
import { fetchCohorts } from '../../data/actions/cohorts';
|
||||
import { fetchTracks } from '../../data/actions/tracks';
|
||||
import hasMastersTrack from '../../data/selectors/tracks';
|
||||
import stateHasMastersTrack from '../../data/selectors/tracks';
|
||||
import getBulkManagementHistory from '../../data/selectors/grades';
|
||||
import { fetchAssignmentTypes } from '../../data/actions/assignmentTypes';
|
||||
import { getRoles } from '../../data/actions/roles';
|
||||
import LmsApiService from '../../data/services/LmsApiService';
|
||||
@@ -53,7 +54,8 @@ const mapStateToProps = (state, ownProps) => (
|
||||
state.grades.bulkManagement.errorMessages ?
|
||||
`Errors while processing: ${state.grades.bulkManagement.errorMessages.join(', ')}` :
|
||||
'',
|
||||
showBulkManagement: hasMastersTrack(state),
|
||||
showBulkManagement: stateHasMastersTrack(state),
|
||||
bulkManagementHistory: getBulkManagementHistory(state),
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -13,6 +13,8 @@ import {
|
||||
START_UPLOAD,
|
||||
UPLOAD_COMPLETE,
|
||||
UPLOAD_ERR,
|
||||
GOT_BULK_HISTORY,
|
||||
BULK_HISTORY_ERR,
|
||||
} from '../constants/actionTypes/grades';
|
||||
import LmsApiService from '../services/LmsApiService';
|
||||
import { headingMapper, sortAlphaAsc } from './utils';
|
||||
@@ -23,6 +25,8 @@ const defaultAssignmentFilter = 'All';
|
||||
const startedCsvUpload = () => ({ type: START_UPLOAD });
|
||||
const finishedCsvUpload = () => ({ type: UPLOAD_COMPLETE });
|
||||
const csvUploadError = data => ({ type: UPLOAD_ERR, data });
|
||||
const gotBulkHistory = data => ({ type: GOT_BULK_HISTORY, data });
|
||||
const bulkHistoryError = () => ({ type: BULK_HISTORY_ERR });
|
||||
|
||||
const startedFetchingGrades = () => ({ type: STARTED_FETCHING_GRADES });
|
||||
const finishedFetchingGrades = () => ({ type: FINISHED_FETCHING_GRADES });
|
||||
@@ -163,7 +167,7 @@ const submitFileUploadFormData = (courseId, formData) => (
|
||||
return LmsApiService.uploadGradeCsv(courseId, formData).then(() => (
|
||||
dispatch(finishedCsvUpload())
|
||||
)).catch((err) => {
|
||||
if (err.status === 200 && err.data.error_messages.length) {
|
||||
if (err.response.status === 200 && err.response.error_messages.length) {
|
||||
const { error_messages: errorMessages, saved, total } = err.data;
|
||||
return dispatch(csvUploadError({ errorMessages, saved, total }));
|
||||
}
|
||||
@@ -172,6 +176,14 @@ const submitFileUploadFormData = (courseId, formData) => (
|
||||
}
|
||||
);
|
||||
|
||||
const fetchBulkUpgradeHistory = courseId => (
|
||||
dispatch =>
|
||||
// todo add loading effect
|
||||
LmsApiService.fetchGradeBulkOperationHistory(courseId).then((response) => {
|
||||
dispatch(gotBulkHistory(response));
|
||||
}).catch(() => dispatch(bulkHistoryError()))
|
||||
);
|
||||
|
||||
export {
|
||||
startedFetchingGrades,
|
||||
finishedFetchingGrades,
|
||||
@@ -188,4 +200,5 @@ export {
|
||||
filterColumns,
|
||||
closeBanner,
|
||||
submitFileUploadFormData,
|
||||
fetchBulkUpgradeHistory,
|
||||
};
|
||||
|
||||
@@ -3,6 +3,8 @@ import {
|
||||
GOT_TRACKS,
|
||||
ERROR_FETCHING_TRACKS,
|
||||
} from '../constants/actionTypes/tracks';
|
||||
import { hasMastersTrack } from '../selectors/tracks';
|
||||
import { fetchBulkUpgradeHistory } from './grades';
|
||||
import LmsApiService from '../services/LmsApiService';
|
||||
|
||||
const startedFetchingTracks = () => ({ type: STARTED_FETCHING_TRACKS });
|
||||
@@ -16,6 +18,9 @@ const fetchTracks = courseId => (
|
||||
.then(response => response.data)
|
||||
.then((data) => {
|
||||
dispatch(gotTracks(data.course_modes));
|
||||
if (hasMastersTrack(data.course_modes)) {
|
||||
dispatch(fetchBulkUpgradeHistory(courseId));
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
dispatch(errorFetchingTracks());
|
||||
|
||||
@@ -15,6 +15,8 @@ const OPEN_BANNER = 'OPEN_BANNER';
|
||||
const START_UPLOAD = 'START_UPLOAD';
|
||||
const UPLOAD_COMPLETE = 'UPLOAD_COMPLETE';
|
||||
const UPLOAD_ERR = 'UPLOAD_ERR';
|
||||
const GOT_BULK_HISTORY = 'GOT_BULK_HISTORY';
|
||||
const BULK_HISTORY_ERR = 'BULK_HISTORY_ERR';
|
||||
|
||||
export {
|
||||
STARTED_FETCHING_GRADES,
|
||||
@@ -31,4 +33,6 @@ export {
|
||||
START_UPLOAD,
|
||||
UPLOAD_COMPLETE,
|
||||
UPLOAD_ERR,
|
||||
GOT_BULK_HISTORY,
|
||||
BULK_HISTORY_ERR,
|
||||
};
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
START_UPLOAD,
|
||||
UPLOAD_COMPLETE,
|
||||
UPLOAD_ERR,
|
||||
GOT_BULK_HISTORY,
|
||||
} from '../constants/actionTypes/grades';
|
||||
|
||||
const initialState = {
|
||||
@@ -22,6 +23,7 @@ const initialState = {
|
||||
prevPage: null,
|
||||
nextPage: null,
|
||||
showSpinner: true,
|
||||
bulkManagement: {},
|
||||
};
|
||||
|
||||
const grades = (state = initialState, action) => {
|
||||
@@ -79,24 +81,35 @@ const grades = (state = initialState, action) => {
|
||||
...state,
|
||||
showSpinner: true,
|
||||
};
|
||||
case UPLOAD_COMPLETE:
|
||||
case UPLOAD_COMPLETE: {
|
||||
const { errorMessages, ...rest } = state.bulkManagement;
|
||||
return {
|
||||
...state,
|
||||
showSpinner: false,
|
||||
bulkManagement: {},
|
||||
bulkManagement: { ...rest },
|
||||
};
|
||||
}
|
||||
case UPLOAD_ERR:
|
||||
return {
|
||||
...state,
|
||||
showSpinner: false,
|
||||
bulkManagement: {
|
||||
...(state.bulkManagement || {}),
|
||||
...state.bulkManagement,
|
||||
...action.data,
|
||||
},
|
||||
};
|
||||
case GOT_BULK_HISTORY:
|
||||
return {
|
||||
...state,
|
||||
bulkManagement: {
|
||||
...state.bulkManagement,
|
||||
history: action.data,
|
||||
},
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
};
|
||||
|
||||
export { initialState as initialGradesState };
|
||||
export default grades;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import grades from './grades';
|
||||
import grades, { initialGradesState as initialState } from './grades';
|
||||
import {
|
||||
STARTED_FETCHING_GRADES,
|
||||
ERROR_FETCHING_GRADES,
|
||||
@@ -8,19 +8,6 @@ import {
|
||||
OPEN_BANNER,
|
||||
} from '../constants/actionTypes/grades';
|
||||
|
||||
const initialState = {
|
||||
results: [],
|
||||
headings: [],
|
||||
startedFetching: false,
|
||||
finishedFetching: false,
|
||||
errorFetching: false,
|
||||
gradeFormat: 'percent',
|
||||
showSuccess: false,
|
||||
prevPage: null,
|
||||
nextPage: null,
|
||||
showSpinner: true,
|
||||
};
|
||||
|
||||
const courseId = 'course-v1:edX+DemoX+Demo_Course';
|
||||
const headingsData = [
|
||||
{ name: 'exam' },
|
||||
|
||||
3
src/data/selectors/grades.js
Normal file
3
src/data/selectors/grades.js
Normal file
@@ -0,0 +1,3 @@
|
||||
const getBulkManagementHistory = state => state.grades.bulkManagement.history;
|
||||
|
||||
export default getBulkManagementHistory;
|
||||
@@ -1,4 +1,13 @@
|
||||
const getTracks = state => state.tracks.results || [];
|
||||
const hasMastersTrack = state => getTracks(state).some(track => track.slug === 'masters');
|
||||
const compose = (...fns) => {
|
||||
const [firstFunc, ...rest] = fns.reverse();
|
||||
return (...args) =>
|
||||
rest.reduce((accum, fn) => fn(accum), firstFunc(...args));
|
||||
};
|
||||
|
||||
export default hasMastersTrack;
|
||||
const getTracks = state => state.tracks.results || [];
|
||||
const trackIsMasters = track => track.slug === 'masters';
|
||||
const hasMastersTrack = tracks => tracks.some(trackIsMasters);
|
||||
const stateHasMastersTrack = compose(hasMastersTrack, getTracks);
|
||||
|
||||
export { hasMastersTrack, trackIsMasters };
|
||||
export default stateHasMastersTrack;
|
||||
|
||||
@@ -72,7 +72,7 @@ class LmsApiService {
|
||||
const trackQueryParam = options.track ? [`track=${options.track}`] : [];
|
||||
const cohortQueryParam = options.cohort ? [`cohort=${options.cohort}`] : [];
|
||||
const queryParams = [...trackQueryParam, ...cohortQueryParam].join('&');
|
||||
const downloadUrl = `${LmsApiService.baseUrl}/api/bulk_grades/course/${courseId}?${queryParams}`;
|
||||
const downloadUrl = `${LmsApiService.baseUrl}/api/bulk_grades/course/${courseId}/?${queryParams}`;
|
||||
return downloadUrl;
|
||||
}
|
||||
|
||||
@@ -87,6 +87,11 @@ class LmsApiService {
|
||||
return Promise.reject(result);
|
||||
});
|
||||
}
|
||||
|
||||
static fetchGradeBulkOperationHistory(courseId) {
|
||||
const url = `${LmsApiService.baseUrl}/api/bulk_grades/course/${courseId}/history/`;
|
||||
return apiClient.get(url).then(response => response.data).catch(() => Promise.reject(Error('unhandled response error')));
|
||||
}
|
||||
}
|
||||
|
||||
export default LmsApiService;
|
||||
|
||||
Reference in New Issue
Block a user