Add filters for assignment grades
When assignment grade filters are set, changing which assignment is chosen will also cause the grades to be refreshed from the server (since the subset of learners being viewed may be different). JIRA:EDUCATOR-4541
This commit is contained in:
@@ -16,7 +16,10 @@
|
||||
"jsx-a11y/anchor-is-valid": [ "error", {
|
||||
"components": [ "Link" ],
|
||||
"specialLink": [ "to" ]
|
||||
}]
|
||||
}],
|
||||
// https://github.com/yannickcr/eslint-plugin-react/issues/1754#issuecomment-378838053
|
||||
// tl;dr: this rule is no longer going to cause any user-facing visual weirdness, its original motivation
|
||||
"react/no-did-mount-set-state": "off"
|
||||
},
|
||||
"env": {
|
||||
"jest": true
|
||||
|
||||
@@ -6,6 +6,7 @@ $fa-font-path: "~font-awesome/fonts";
|
||||
|
||||
$input-focus-box-shadow: $input-box-shadow; // hack to get upgrade to paragon 4.0.0 to work
|
||||
@import "~@edx/paragon/src/SearchField/SearchField";
|
||||
@import "~@edx/paragon/src/Collapsible/Collapsible";
|
||||
|
||||
@import "~@edx/frontend-component-footer/src/lib/scss/site-footer";
|
||||
|
||||
|
||||
@@ -2,13 +2,15 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Button,
|
||||
StatefulButton,
|
||||
Collapsible,
|
||||
Icon,
|
||||
InputSelect,
|
||||
InputText,
|
||||
Modal,
|
||||
SearchField,
|
||||
StatefulButton,
|
||||
StatusAlert,
|
||||
Table,
|
||||
Icon,
|
||||
Tabs,
|
||||
} from '@edx/paragon';
|
||||
import queryString from 'query-string';
|
||||
@@ -33,6 +35,8 @@ export default class Gradebook extends React.Component {
|
||||
updateModuleId: null,
|
||||
updateUserId: null,
|
||||
reasonForChange: '',
|
||||
assignmentGradeMin: '',
|
||||
assignmentGradeMax: '',
|
||||
};
|
||||
this.fileFormRef = React.createRef();
|
||||
this.fileInputRef = React.createRef();
|
||||
@@ -44,6 +48,8 @@ export default class Gradebook extends React.Component {
|
||||
this.props.initializeFilters(urlQuery);
|
||||
this.props.getRoles(this.props.courseId);
|
||||
this.overrideReasonInput.focus();
|
||||
const { assignmentGradeMin, assignmentGradeMax } = urlQuery;
|
||||
this.setState({ assignmentGradeMin, assignmentGradeMax });
|
||||
}
|
||||
|
||||
onChange(e) {
|
||||
@@ -131,18 +137,25 @@ export default class Gradebook extends React.Component {
|
||||
const { type, id } = selectedFilterOption || {};
|
||||
const typedValue = { label: assignment, type, id };
|
||||
this.props.updateAssignmentFilter(typedValue);
|
||||
const updatedQueryStrings = this.updateQueryParams('assignment', assignment);
|
||||
this.props.history.push(updatedQueryStrings);
|
||||
}
|
||||
this.updateQueryParams({ assignment: id });
|
||||
this.props.updateGradesIfAssigGradeFiltersSet(
|
||||
this.props.courseId,
|
||||
this.props.selectedCohort,
|
||||
this.props.selectedTrack,
|
||||
this.props.selectedAssignmentType,
|
||||
);
|
||||
};
|
||||
|
||||
updateQueryParams = (queryKey, queryValue) => {
|
||||
updateQueryParams = (queryParams) => {
|
||||
const parsed = queryString.parse(this.props.location.search);
|
||||
if (queryValue) {
|
||||
parsed[queryKey] = queryValue;
|
||||
} else {
|
||||
delete parsed[queryKey];
|
||||
}
|
||||
return `?${queryString.stringify(parsed)}`;
|
||||
Object.keys(queryParams).forEach((key) => {
|
||||
if (queryParams[key]) {
|
||||
parsed[key] = queryParams[key];
|
||||
} else {
|
||||
delete parsed[key];
|
||||
}
|
||||
});
|
||||
this.props.history.push(`?${queryString.stringify(parsed)}`);
|
||||
};
|
||||
|
||||
mapAssignmentTypeEntries = (entries) => {
|
||||
@@ -210,8 +223,7 @@ export default class Gradebook extends React.Component {
|
||||
|
||||
updateAssignmentTypes = (assignmentType) => {
|
||||
this.props.filterAssignmentType(assignmentType);
|
||||
const updatedQueryStrings = this.updateQueryParams('assignmentType', assignmentType);
|
||||
this.props.history.push(updatedQueryStrings);
|
||||
this.updateQueryParams({ assignmentType });
|
||||
}
|
||||
|
||||
updateTracks = (event) => {
|
||||
@@ -226,8 +238,7 @@ export default class Gradebook extends React.Component {
|
||||
selectedTrackSlug,
|
||||
this.props.selectedAssignmentType,
|
||||
);
|
||||
const updatedQueryStrings = this.updateQueryParams('track', selectedTrackSlug);
|
||||
this.props.history.push(updatedQueryStrings);
|
||||
this.updateQueryParams({ track: selectedTrackSlug });
|
||||
};
|
||||
|
||||
updateCohorts = (event) => {
|
||||
@@ -242,8 +253,7 @@ export default class Gradebook extends React.Component {
|
||||
this.props.selectedTrack,
|
||||
this.props.selectedAssignmentType,
|
||||
);
|
||||
const updatedQueryStrings = this.updateQueryParams('cohort', selectedCohortId);
|
||||
this.props.history.push(updatedQueryStrings);
|
||||
this.updateQueryParams({ cohort: selectedCohortId });
|
||||
};
|
||||
|
||||
handleClickExportGrades = () => {
|
||||
@@ -274,6 +284,26 @@ export default class Gradebook extends React.Component {
|
||||
}
|
||||
};
|
||||
|
||||
handleSubmitAssignmentGrade = (event) => {
|
||||
event.preventDefault();
|
||||
const formContents = new FormData(event.target);
|
||||
const assignmentGradeMin = formContents.get('assignmentGradeMin');
|
||||
const assignmentGradeMax = formContents.get('assignmentGradeMax');
|
||||
|
||||
this.props.updateAssignmentLimits(assignmentGradeMin, assignmentGradeMax);
|
||||
this.props.getUserGrades(
|
||||
this.props.courseId,
|
||||
this.props.selectedCohort,
|
||||
this.props.selectedTrack,
|
||||
this.props.selectedAssignmentType,
|
||||
);
|
||||
this.updateQueryParams({ assignmentGradeMin, assignmentGradeMax });
|
||||
};
|
||||
|
||||
handleMinAssigGradeChange = assignmentGradeMin => this.setState({ assignmentGradeMin });
|
||||
|
||||
handleMaxAssigGradeChange = assignmentGradeMax => this.setState({ assignmentGradeMax });
|
||||
|
||||
mapSelectedCohortEntry = (entry) => {
|
||||
const selectedCohortEntry = this.props.cohorts.find(x => x.id === parseInt(entry, 10));
|
||||
if (selectedCohortEntry) {
|
||||
@@ -406,32 +436,70 @@ export default class Gradebook extends React.Component {
|
||||
options={[{ label: 'Percent', value: 'percent' }, { label: 'Absolute', value: 'absolute' }]}
|
||||
onChange={this.props.toggleFormat}
|
||||
/>
|
||||
<div className="student-filters">
|
||||
<span className="label">
|
||||
Assignment Types:
|
||||
</span>
|
||||
<InputSelect
|
||||
name="assignment-types"
|
||||
ariaLabel="Assignment Types"
|
||||
value={this.props.selectedAssignmentType}
|
||||
options={this.mapAssignmentTypeEntries(this.props.assignmentTypes)}
|
||||
onChange={this.updateAssignmentTypes}
|
||||
disabled={this.props.assignmentFilterOptions.length === 0}
|
||||
/>
|
||||
</div>
|
||||
<div className="student-filters">
|
||||
<span className="label">
|
||||
Assignment:
|
||||
</span>
|
||||
<InputSelect
|
||||
name="assignment"
|
||||
ariaLabel="Assignment"
|
||||
value={this.props.selectedAssignment}
|
||||
options={this.getAssignmentFilterOptions()}
|
||||
onChange={this.handleAssignmentFilterChange}
|
||||
disabled={this.props.assignmentFilterOptions.length === 0}
|
||||
/>
|
||||
</div>
|
||||
<Collapsible title="Assignments" isOpen>
|
||||
<div>
|
||||
<div className="student-filters">
|
||||
<span className="label">
|
||||
Assignment Types:
|
||||
</span>
|
||||
<InputSelect
|
||||
name="assignment-types"
|
||||
ariaLabel="Assignment Types"
|
||||
value={this.props.selectedAssignmentType}
|
||||
options={this.mapAssignmentTypeEntries(this.props.assignmentTypes)}
|
||||
onChange={this.updateAssignmentTypes}
|
||||
disabled={this.props.assignmentFilterOptions.length === 0}
|
||||
/>
|
||||
</div>
|
||||
<div className="student-filters">
|
||||
<span className="label">
|
||||
Assignment:
|
||||
</span>
|
||||
<InputSelect
|
||||
name="assignment"
|
||||
ariaLabel="Assignment"
|
||||
value={this.props.selectedAssignment}
|
||||
options={this.getAssignmentFilterOptions()}
|
||||
onChange={this.handleAssignmentFilterChange}
|
||||
disabled={this.props.assignmentFilterOptions.length === 0}
|
||||
/>
|
||||
</div>
|
||||
<p>Grade Range (0% - 100%)</p>
|
||||
<form className="d-flex justify-content-between" onSubmit={this.handleSubmitAssignmentGrade}>
|
||||
<InputText
|
||||
label="Min Grade"
|
||||
name="assignmentGradeMin"
|
||||
type="number"
|
||||
min={0}
|
||||
max={100}
|
||||
step={1}
|
||||
value={this.state.assignmentGradeMin}
|
||||
disabled={!this.props.selectedAssignment}
|
||||
onChange={this.handleMinAssigGradeChange}
|
||||
/>
|
||||
<InputText
|
||||
label="Max Grade"
|
||||
name="assignmentGradeMax"
|
||||
type="number"
|
||||
min={0}
|
||||
max={100}
|
||||
step={1}
|
||||
value={this.state.assignmentGradeMax}
|
||||
disabled={!this.props.selectedAssignment}
|
||||
onChange={this.handleMaxAssigGradeChange}
|
||||
/>
|
||||
<div className="d-flex align-items-center">
|
||||
<Button
|
||||
type="submit"
|
||||
className="btn-outline-secondary"
|
||||
label="Apply"
|
||||
name="assignmentGradeMinMax"
|
||||
disabled={!this.props.selectedAssignment}
|
||||
/>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</Collapsible>
|
||||
<div className="student-filters">
|
||||
<span className="label">
|
||||
Student Groups:
|
||||
@@ -723,6 +791,7 @@ Gradebook.propTypes = {
|
||||
})),
|
||||
filterAssignmentType: PropTypes.func.isRequired,
|
||||
updateAssignmentFilter: PropTypes.func.isRequired,
|
||||
updateAssignmentLimits: PropTypes.func.isRequired,
|
||||
format: PropTypes.string.isRequired,
|
||||
getRoles: PropTypes.func.isRequired,
|
||||
getUserGrades: PropTypes.func.isRequired,
|
||||
@@ -793,4 +862,5 @@ Gradebook.propTypes = {
|
||||
filteredUsersCount: PropTypes.number,
|
||||
showDownloadButtons: PropTypes.bool,
|
||||
initializeFilters: PropTypes.func.isRequired,
|
||||
updateGradesIfAssigGradeFiltersSet: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
@@ -2,21 +2,22 @@ import { connect } from 'react-redux';
|
||||
|
||||
import Gradebook from '../../components/Gradebook';
|
||||
import {
|
||||
fetchGrades,
|
||||
closeBanner,
|
||||
fetchGradeOverrideHistory,
|
||||
fetchGrades,
|
||||
fetchMatchingUserGrades,
|
||||
fetchPrevNextGrades,
|
||||
updateGrades,
|
||||
toggleGradeFormat,
|
||||
filterAssignmentType,
|
||||
closeBanner,
|
||||
submitFileUploadFormData,
|
||||
toggleGradeFormat,
|
||||
updateGrades,
|
||||
updateGradesIfAssigGradeFiltersSet,
|
||||
} from '../../data/actions/grades';
|
||||
import { fetchCohorts } from '../../data/actions/cohorts';
|
||||
import { fetchTracks } from '../../data/actions/tracks';
|
||||
import { initializeFilters, updateAssignmentFilter } from '../../data/actions/filters';
|
||||
import { initializeFilters, updateAssignmentFilter, updateAssignmentLimits } from '../../data/actions/filters';
|
||||
import stateHasMastersTrack from '../../data/selectors/tracks';
|
||||
import { getBulkManagementHistory, getHeadings } from '../../data/selectors/grades';
|
||||
import { getBulkManagementHistory, getHeadings, formatMinAssigGrade, formatMaxAssigGrade } from '../../data/selectors/grades';
|
||||
import { selectableAssignmentLabels } from '../../data/selectors/filters';
|
||||
import { getCohortNameById } from '../../data/selectors/cohorts';
|
||||
import { fetchAssignmentTypes } from '../../data/actions/assignmentTypes';
|
||||
@@ -50,6 +51,8 @@ const mapStateToProps = (state, ownProps) => (
|
||||
selectedCohort: state.filters.cohort,
|
||||
selectedAssignmentType: state.filters.assignmentType,
|
||||
selectedAssignment: (state.filters.assignment || {}).label,
|
||||
selectedMinAssigGrade: state.filters.assignmentGradeMin || 0,
|
||||
selectedMaxAssigGrade: state.filters.assignmentGradeMax || 100,
|
||||
format: state.grades.gradeFormat,
|
||||
showSuccess: state.grades.showSuccess,
|
||||
errorFetchingGradeOverrideHistory: state.grades.errorFetchingOverrideHistory,
|
||||
@@ -65,6 +68,16 @@ const mapStateToProps = (state, ownProps) => (
|
||||
track: state.filters.track,
|
||||
assignment: (state.filters.assignment || {}).id,
|
||||
assignmentType: state.filters.assignmentType,
|
||||
assignmentGradeMin: formatMinAssigGrade(
|
||||
state,
|
||||
(state.filters.assignment || {}).id,
|
||||
state.filters.assignmentGradeMin,
|
||||
),
|
||||
assignmentGradeMax: formatMaxAssigGrade(
|
||||
state,
|
||||
(state.filters.assignment || {}).id,
|
||||
state.filters.assignmentGradeMax,
|
||||
),
|
||||
}),
|
||||
interventionExportUrl:
|
||||
LmsApiService.getInterventionExportCsvUrl(ownProps.match.params.courseId),
|
||||
@@ -98,6 +111,8 @@ const mapDispatchToProps = {
|
||||
submitFileUploadFormData,
|
||||
initializeFilters,
|
||||
updateAssignmentFilter,
|
||||
updateAssignmentLimits,
|
||||
updateGradesIfAssigGradeFiltersSet,
|
||||
};
|
||||
|
||||
const GradebookPage = connect(
|
||||
|
||||
@@ -1,17 +1,21 @@
|
||||
import { INITIALIZE_FILTERS, UPDATE_ASSIGNMENT_FILTER } from '../constants/actionTypes/filters';
|
||||
import { INITIALIZE_FILTERS, UPDATE_ASSIGNMENT_FILTER, UPDATE_ASSIGNMENT_LIMITS } from '../constants/actionTypes/filters';
|
||||
|
||||
const initializeFilters = ({
|
||||
assignment = '',
|
||||
assignmentType = '',
|
||||
track = '',
|
||||
cohort = '',
|
||||
assignmentGradeMin = '',
|
||||
assignmentGradeMax = '',
|
||||
}) => ({
|
||||
type: INITIALIZE_FILTERS,
|
||||
data: {
|
||||
assignment: { label: assignment },
|
||||
assignment: { id: assignment },
|
||||
assignmentType,
|
||||
track,
|
||||
cohort,
|
||||
assignmentGradeMin,
|
||||
assignmentGradeMax,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -20,4 +24,9 @@ const updateAssignmentFilter = assignment => ({
|
||||
data: assignment,
|
||||
});
|
||||
|
||||
export { initializeFilters, updateAssignmentFilter };
|
||||
const updateAssignmentLimits = (minGrade, maxGrade) => ({
|
||||
type: UPDATE_ASSIGNMENT_LIMITS,
|
||||
data: { minGrade, maxGrade },
|
||||
});
|
||||
|
||||
export { initializeFilters, updateAssignmentFilter, updateAssignmentLimits };
|
||||
|
||||
@@ -20,6 +20,8 @@ import {
|
||||
} from '../constants/actionTypes/grades';
|
||||
import LmsApiService from '../services/LmsApiService';
|
||||
import { sortAlphaAsc, formatDateForDisplay } from './utils';
|
||||
import { formatMaxAssigGrade, formatMinAssigGrade } from '../selectors/grades';
|
||||
import { getFilters } from '../selectors/filters';
|
||||
import apiClient from '../apiClient';
|
||||
|
||||
const defaultAssignmentFilter = 'All';
|
||||
@@ -102,9 +104,27 @@ const fetchGrades = (
|
||||
assignmentType,
|
||||
options = {},
|
||||
) => (
|
||||
(dispatch) => {
|
||||
(dispatch, getState) => {
|
||||
dispatch(startedFetchingGrades());
|
||||
return LmsApiService.fetchGradebookData(courseId, options.searchText || null, cohort, track)
|
||||
const {
|
||||
assignment,
|
||||
assignmentGradeMax: assigMax,
|
||||
assignmentGradeMin: assigMin,
|
||||
} = getFilters(getState());
|
||||
const { id: assignmentId } = assignment || {};
|
||||
const assignmentGradeMax = formatMaxAssigGrade(getState(), assignmentId, assigMax);
|
||||
const assignmentGradeMin = formatMinAssigGrade(getState(), assignmentId, assigMin);
|
||||
return LmsApiService.fetchGradebookData(
|
||||
courseId,
|
||||
options.searchText || null,
|
||||
cohort,
|
||||
track,
|
||||
{
|
||||
assignment: assignmentId,
|
||||
assignmentGradeMax,
|
||||
assignmentGradeMin,
|
||||
},
|
||||
)
|
||||
.then(response => response.data)
|
||||
.then((data) => {
|
||||
dispatch(gotGrades({
|
||||
@@ -244,6 +264,24 @@ const fetchBulkUpgradeHistory = courseId => (
|
||||
}).catch(() => dispatch(bulkHistoryError()))
|
||||
);
|
||||
|
||||
const updateGradesIfAssigGradeFiltersSet = (
|
||||
courseId,
|
||||
cohort,
|
||||
track,
|
||||
assignmentType,
|
||||
) => (dispatch, getState) => {
|
||||
const { filters } = getState();
|
||||
const hasAssigGradeFiltersSet = filters.assignmentGradeMax || filters.assignmentGradeMin;
|
||||
if (hasAssigGradeFiltersSet) {
|
||||
dispatch(fetchGrades(
|
||||
courseId,
|
||||
cohort,
|
||||
track,
|
||||
assignmentType,
|
||||
));
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
startedFetchingGrades,
|
||||
finishedFetchingGrades,
|
||||
@@ -262,4 +300,5 @@ export {
|
||||
submitFileUploadFormData,
|
||||
fetchBulkUpgradeHistory,
|
||||
fetchGradeOverrideHistory,
|
||||
updateGradesIfAssigGradeFiltersSet,
|
||||
};
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const INITIALIZE_FILTERS = 'INITIALIZE_FILTERS';
|
||||
const UPDATE_ASSIGNMENT_FILTER = 'UPDATE_ASSIGNMENT_FILTER';
|
||||
const UPDATE_ASSIGNMENT_LIMITS = 'UPDATE_ASSIGNMENT_LIMITS';
|
||||
|
||||
export { INITIALIZE_FILTERS, UPDATE_ASSIGNMENT_FILTER };
|
||||
export { INITIALIZE_FILTERS, UPDATE_ASSIGNMENT_FILTER, UPDATE_ASSIGNMENT_LIMITS };
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { GOT_GRADES, FILTER_BY_ASSIGNMENT_TYPE } from '../constants/actionTypes/grades';
|
||||
|
||||
import { INITIALIZE_FILTERS, UPDATE_ASSIGNMENT_FILTER } from '../constants/actionTypes/filters';
|
||||
import { INITIALIZE_FILTERS, UPDATE_ASSIGNMENT_FILTER, UPDATE_ASSIGNMENT_LIMITS } from '../constants/actionTypes/filters';
|
||||
|
||||
import { getAssignmentsFromResultsSubstate, chooseRelevantAssignmentData } from '../selectors/filters';
|
||||
|
||||
@@ -24,11 +24,11 @@ const reducer = (state = initialState, action) => {
|
||||
};
|
||||
case GOT_GRADES: {
|
||||
const { assignment } = state;
|
||||
const { label, type } = assignment || {};
|
||||
const { id, type } = assignment || {};
|
||||
if (!type) {
|
||||
const relevantAssignment = getAssignmentsFromResultsSubstate(action.grades)
|
||||
.map(chooseRelevantAssignmentData)
|
||||
.find(assig => assig.label === label);
|
||||
.find(assig => assig.id === id);
|
||||
return {
|
||||
...state,
|
||||
track: action.track,
|
||||
@@ -47,6 +47,12 @@ const reducer = (state = initialState, action) => {
|
||||
...state,
|
||||
assignment: action.data,
|
||||
};
|
||||
case UPDATE_ASSIGNMENT_LIMITS:
|
||||
return {
|
||||
...state,
|
||||
assignmentGradeMin: action.data.minGrade,
|
||||
assignmentGradeMax: action.data.maxGrade,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -68,16 +68,31 @@ const headingMapper = (category, label = 'All') => {
|
||||
};
|
||||
};
|
||||
|
||||
const getExampleSectionBreakdown = state => (state.grades.results[0] || {}).section_breakdown || [];
|
||||
|
||||
const getHeadings = (state) => {
|
||||
const filters = getFilters(state) || {};
|
||||
const {
|
||||
assignmentType: selectedAssignmentType,
|
||||
assignment: selectedAssignment,
|
||||
} = filters;
|
||||
const assignments = (state.grades.results[0] || {}).section_breakdown || [];
|
||||
const assignments = getExampleSectionBreakdown(state);
|
||||
const type = selectedAssignmentType || 'All';
|
||||
const assignment = (selectedAssignment || {}).label || 'All';
|
||||
return headingMapper(type, assignment)(assignments);
|
||||
};
|
||||
|
||||
export { getBulkManagementHistory, getHeadings };
|
||||
const formatMaxAssigGrade = (state, assignmentId, percentGrade) => {
|
||||
if (percentGrade === '100') {
|
||||
return null;
|
||||
}
|
||||
return percentGrade;
|
||||
};
|
||||
const formatMinAssigGrade = (state, assignmentId, percentGrade) => {
|
||||
if (percentGrade === '0') {
|
||||
return null;
|
||||
}
|
||||
return percentGrade;
|
||||
};
|
||||
|
||||
export { getBulkManagementHistory, getHeadings, formatMinAssigGrade, formatMaxAssigGrade };
|
||||
|
||||
@@ -5,19 +5,36 @@ class LmsApiService {
|
||||
static baseUrl = configuration.LMS_BASE_URL;
|
||||
static pageSize = 25
|
||||
|
||||
static fetchGradebookData(courseId, searchText, cohort, track) {
|
||||
let gradebookUrl = `${LmsApiService.baseUrl}/api/grades/v1/gradebook/${courseId}/`;
|
||||
|
||||
gradebookUrl += `?page_size=${LmsApiService.pageSize}&`;
|
||||
static fetchGradebookData(courseId, searchText, cohort, track, options = {}) {
|
||||
const queryParams = {};
|
||||
queryParams.page_size = LmsApiService.pageSize;
|
||||
if (searchText) {
|
||||
gradebookUrl += `user_contains=${searchText}&`;
|
||||
queryParams.user_contains = searchText;
|
||||
}
|
||||
if (cohort) {
|
||||
gradebookUrl += `cohort_id=${cohort}&`;
|
||||
queryParams.cohort_id = cohort;
|
||||
}
|
||||
if (track) {
|
||||
gradebookUrl += `enrollment_mode=${track}`;
|
||||
queryParams.enrollment_mode = track;
|
||||
}
|
||||
if (options.assignmentGradeMax || options.assignmentGradeMin) {
|
||||
if (!options.assignment) {
|
||||
throw new Error('Gradebook LMS API requires assignment to be set to filter by min/max assig. grade');
|
||||
}
|
||||
queryParams.assignment = options.assignment;
|
||||
if (options.assignmentGradeMin) {
|
||||
queryParams.assignment_grade_min = options.assignmentGradeMin;
|
||||
}
|
||||
if (options.assignmentGradeMax) {
|
||||
queryParams.assignment_grade_max = options.assignmentGradeMax;
|
||||
}
|
||||
}
|
||||
|
||||
const queryParamString = Object.keys(queryParams)
|
||||
.map(attr => `${attr}=${encodeURIComponent(queryParams[attr])}`)
|
||||
.join('&');
|
||||
const gradebookUrl = `${LmsApiService.baseUrl}/api/grades/v1/gradebook/${courseId}/?${queryParamString}`;
|
||||
|
||||
return apiClient.get(gradebookUrl);
|
||||
}
|
||||
|
||||
@@ -70,7 +87,7 @@ class LmsApiService {
|
||||
}
|
||||
|
||||
static getGradeExportCsvUrl(courseId, options = {}) {
|
||||
const queryParams = ['track', 'cohort', 'assignment', 'assignmentType']
|
||||
const queryParams = ['track', 'cohort', 'assignment', 'assignmentType', 'assignmentGradeMax', 'assignmentGradeMin']
|
||||
.filter(opt => options[opt] &&
|
||||
options[opt] !== 'All')
|
||||
.map(opt => `${opt}=${encodeURIComponent(options[opt])}`)
|
||||
|
||||
Reference in New Issue
Block a user