Compare commits
1 Commits
open-relea
...
v1.4.20
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8bc1fc82f2 |
2
.env
2
.env
@@ -13,7 +13,7 @@ ACCESS_TOKEN_COOKIE_NAME=null,
|
||||
CSRF_COOKIE_NAME='csrftoken',
|
||||
NEW_RELIC_APP_ID=null,
|
||||
NEW_RELIC_LICENSE_KEY=null,
|
||||
SITE_NAME='',
|
||||
SITE_NAME=null,
|
||||
MARKETING_SITE_BASE_URL=null,
|
||||
SUPPORT_URL=null,
|
||||
CONTACT_URL=null,
|
||||
|
||||
@@ -13,7 +13,7 @@ CSRF_TOKEN_API_PATH='/csrf/api/v1/token'
|
||||
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
|
||||
ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
|
||||
USER_INFO_COOKIE_NAME='edx-user-info'
|
||||
SITE_NAME=localhost
|
||||
SITE_NAME='edX'
|
||||
|
||||
DATA_API_BASE_URL='http://localhost:8000'
|
||||
// LMS_CLIENT_ID should match the lms DOT client application id your LMS containe
|
||||
|
||||
12
package-lock.json
generated
12
package-lock.json
generated
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@edx/frontend-app-gradebook",
|
||||
"version": "1.4.18",
|
||||
"version": "1.4.20",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
@@ -1280,9 +1280,9 @@
|
||||
"integrity": "sha512-XylKOsWRyQm9sNanZnppRORXTLaL34uThyBQpTFwOGAYvNg9PeYsyTTfLA1FTCh02RV+kiwt/O/y14DR/OqpWg=="
|
||||
},
|
||||
"@edx/brand": {
|
||||
"version": "npm:@edx/brand-edx.org@1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/@edx/brand-edx.org/-/brand-edx.org-1.3.2.tgz",
|
||||
"integrity": "sha512-Bns1aTpcxZXyQcRXRToHTnLivR+CDgV5ME3EK4BkVx1qtL8ck1bOLyZMARrBrCyWHNFLHqeANbHdFmgTjLXR3A=="
|
||||
"version": "npm:@edx/brand-edx.org@1.5.0",
|
||||
"resolved": "https://registry.npmjs.org/@edx/brand-edx.org/-/brand-edx.org-1.5.0.tgz",
|
||||
"integrity": "sha512-VH+z14VCP2/bH0wnNJ9WSFf/iFs5eZv4X8KcJTd0z/yyWnB3bgpbTR1f7tyuWmmVnMzeur+pA0DBKlNC9beYOg=="
|
||||
},
|
||||
"@edx/eslint-config": {
|
||||
"version": "1.1.5",
|
||||
@@ -15011,7 +15011,7 @@
|
||||
"dependencies": {
|
||||
"query-string": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-2.4.2.tgz",
|
||||
"resolved": "http://registry.npmjs.org/query-string/-/query-string-2.4.2.tgz",
|
||||
"integrity": "sha1-fbBmZCCAS6qSrp8miWKFWnYUPfs=",
|
||||
"requires": {
|
||||
"strict-uri-encode": "^1.0.0"
|
||||
@@ -20148,7 +20148,7 @@
|
||||
},
|
||||
"got": {
|
||||
"version": "6.7.1",
|
||||
"resolved": "https://registry.npmjs.org/got/-/got-6.7.1.tgz",
|
||||
"resolved": "http://registry.npmjs.org/got/-/got-6.7.1.tgz",
|
||||
"integrity": "sha1-JAzQV4WpoY5WHcG0S0HHY+8ejbA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@edx/frontend-app-gradebook",
|
||||
"version": "1.4.19",
|
||||
"version": "1.4.20",
|
||||
"description": "edx editable gradebook-ui to manipulate grade overrides on subsections",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<title>Gradebook | <%= process.env.SITE_NAME %></title>
|
||||
<title>Gradebook | edX</title>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="<%=htmlWebpackPlugin.options.FAVICON_URL%>" type="image/x-icon" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -4,11 +4,13 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import initialFilters from '../../data/constants/filters';
|
||||
|
||||
function FilterBadge({ name, value, onClick }) {
|
||||
function FilterBadge({
|
||||
name, value, onClick, showValue,
|
||||
}) {
|
||||
return (
|
||||
<div>
|
||||
<span className="badge badge-info">
|
||||
<span>{`${name}: ${value}`}</span>
|
||||
<span>{name}{showValue && `: ${value}`}</span>
|
||||
<button type="button" className="btn-info" aria-label="Close" onClick={onClick}>
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
@@ -18,6 +20,20 @@ function FilterBadge({ name, value, onClick }) {
|
||||
);
|
||||
}
|
||||
|
||||
FilterBadge.defaultProps = {
|
||||
showValue: true,
|
||||
};
|
||||
|
||||
FilterBadge.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
value: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.bool,
|
||||
]).isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
showValue: PropTypes.bool,
|
||||
};
|
||||
|
||||
function RangeFilterBadge({
|
||||
displayName,
|
||||
filterName1,
|
||||
@@ -46,7 +62,7 @@ RangeFilterBadge.propTypes = {
|
||||
};
|
||||
|
||||
function SingleValueFilterBadge({
|
||||
displayName, filterName, filterValue, handleBadgeClose,
|
||||
displayName, filterName, filterValue, handleBadgeClose, showValue,
|
||||
}) {
|
||||
return (filterValue !== initialFilters[filterName])
|
||||
&& (
|
||||
@@ -54,14 +70,24 @@ function SingleValueFilterBadge({
|
||||
name={displayName}
|
||||
value={filterValue}
|
||||
onClick={handleBadgeClose}
|
||||
showValue={showValue}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
SingleValueFilterBadge.defaultProps = {
|
||||
showValue: true,
|
||||
};
|
||||
|
||||
SingleValueFilterBadge.propTypes = {
|
||||
displayName: PropTypes.string.isRequired,
|
||||
filterName: PropTypes.string.isRequired,
|
||||
filterValue: PropTypes.string.isRequired,
|
||||
filterValue: PropTypes.oneOfType([
|
||||
PropTypes.string,
|
||||
PropTypes.bool,
|
||||
]).isRequired,
|
||||
handleBadgeClose: PropTypes.func.isRequired,
|
||||
showValue: PropTypes.bool,
|
||||
};
|
||||
|
||||
function FilterBadges({
|
||||
@@ -73,6 +99,7 @@ function FilterBadges({
|
||||
assignmentGradeMax,
|
||||
courseGradeMin,
|
||||
courseGradeMax,
|
||||
includeCourseRoleMembers,
|
||||
handleFilterBadgeClose,
|
||||
}) {
|
||||
return (
|
||||
@@ -113,10 +140,17 @@ function FilterBadges({
|
||||
/>
|
||||
<SingleValueFilterBadge
|
||||
displayName="Cohort"
|
||||
filterName="track"
|
||||
filterName="cohort"
|
||||
filterValue={cohort}
|
||||
handleBadgeClose={handleFilterBadgeClose(['cohort'])}
|
||||
/>
|
||||
<SingleValueFilterBadge
|
||||
displayName="Including Course Team Members"
|
||||
filterName="includeCourseRoleMembers"
|
||||
filterValue={includeCourseRoleMembers}
|
||||
showValue={false}
|
||||
handleBadgeClose={handleFilterBadgeClose(['includeCourseRoleMembers'])}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -131,18 +165,13 @@ const mapStateToProps = state => (
|
||||
assignmentGradeMax: state.filters.assignmentGradeMax,
|
||||
courseGradeMin: state.filters.courseGradeMin,
|
||||
courseGradeMax: state.filters.courseGradeMax,
|
||||
includeCourseRoleMembers: state.filters.includeCourseRoleMembers,
|
||||
}
|
||||
);
|
||||
|
||||
const ConnectedFilterBadges = connect(mapStateToProps)(FilterBadges);
|
||||
export default ConnectedFilterBadges;
|
||||
|
||||
FilterBadge.propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
value: PropTypes.string.isRequired,
|
||||
onClick: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
FilterBadges.defaultProps = {
|
||||
assignment: initialFilters.assignmentType,
|
||||
assignmentType: initialFilters.assignmentType,
|
||||
@@ -152,6 +181,7 @@ FilterBadges.defaultProps = {
|
||||
assignmentGradeMax: initialFilters.assignmentGradeMax,
|
||||
courseGradeMin: initialFilters.courseGradeMin,
|
||||
courseGradeMax: initialFilters.courseGradeMax,
|
||||
includeCourseRoleMembers: initialFilters.includeCourseRoleMembers,
|
||||
};
|
||||
|
||||
FilterBadges.propTypes = {
|
||||
@@ -163,5 +193,6 @@ FilterBadges.propTypes = {
|
||||
assignmentGradeMax: PropTypes.string,
|
||||
courseGradeMin: PropTypes.string,
|
||||
courseGradeMax: PropTypes.string,
|
||||
includeCourseRoleMembers: PropTypes.bool,
|
||||
handleFilterBadgeClose: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
Button,
|
||||
Collapsible,
|
||||
Icon,
|
||||
CheckBox,
|
||||
InputSelect,
|
||||
InputText,
|
||||
SearchField,
|
||||
@@ -466,6 +467,15 @@ export default class Gradebook extends React.Component {
|
||||
onChange={this.updateCohorts}
|
||||
/>
|
||||
</Collapsible>
|
||||
<Collapsible title="Include Course Team Members" className="filter-group mb-3">
|
||||
<CheckBox
|
||||
name="include-course-team-members"
|
||||
aria-label="Include Course Team Members"
|
||||
label="Include Course Team Members"
|
||||
checked={this.props.includeCourseRoleMembers}
|
||||
onChange={this.props.updateIncludeCourseRoleMembers}
|
||||
/>
|
||||
</Collapsible>
|
||||
</Drawer>
|
||||
);
|
||||
}
|
||||
@@ -487,6 +497,7 @@ Gradebook.defaultProps = {
|
||||
showSpinner: false,
|
||||
totalUsersCount: null,
|
||||
tracks: [],
|
||||
includeCourseRoleMembers: false,
|
||||
};
|
||||
|
||||
Gradebook.propTypes = {
|
||||
@@ -524,4 +535,6 @@ Gradebook.propTypes = {
|
||||
name: PropTypes.string,
|
||||
})),
|
||||
updateCourseGradeFilter: PropTypes.func.isRequired,
|
||||
includeCourseRoleMembers: PropTypes.bool,
|
||||
updateIncludeCourseRoleMembers: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
@@ -16,7 +16,12 @@ import {
|
||||
import { fetchCohorts } from '../../data/actions/cohorts';
|
||||
import { fetchTracks } from '../../data/actions/tracks';
|
||||
import {
|
||||
initializeFilters, resetFilters, updateAssignmentFilter, updateAssignmentLimits, updateCourseGradeFilter,
|
||||
initializeFilters,
|
||||
resetFilters,
|
||||
updateAssignmentFilter,
|
||||
updateAssignmentLimits,
|
||||
updateCourseGradeFilter,
|
||||
updateIncludeCourseRoleMembers,
|
||||
} from '../../data/actions/filters';
|
||||
import stateHasMastersTrack from '../../data/selectors/tracks';
|
||||
import {
|
||||
@@ -73,6 +78,7 @@ const mapStateToProps = (state, ownProps) => (
|
||||
),
|
||||
courseGradeMin: formatMinCourseGrade(state.filters.courseGradeMin),
|
||||
courseGradeMax: formatMaxCourseGrade(state.filters.courseGradeMax),
|
||||
excludedCourseRoles: state.filters.includeCourseRoleMembers ? '' : 'all',
|
||||
}),
|
||||
grades: state.grades.results,
|
||||
headings: getHeadings(state),
|
||||
@@ -108,6 +114,7 @@ const mapStateToProps = (state, ownProps) => (
|
||||
tracks: state.tracks.results,
|
||||
uploadSuccess: !!(state.grades.bulkManagement
|
||||
&& state.grades.bulkManagement.uploadSuccess),
|
||||
includeCourseRoleMembers: state.filters.includeCourseRoleMembers,
|
||||
}
|
||||
);
|
||||
|
||||
@@ -131,6 +138,7 @@ const mapDispatchToProps = {
|
||||
updateAssignmentFilter,
|
||||
updateAssignmentLimits,
|
||||
updateCourseGradeFilter,
|
||||
updateIncludeCourseRoleMembers,
|
||||
};
|
||||
|
||||
const GradebookPage = connect(
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
import initialFilters from '../constants/filters';
|
||||
import {
|
||||
INITIALIZE_FILTERS, RESET_FILTERS, UPDATE_ASSIGNMENT_FILTER, UPDATE_ASSIGNMENT_LIMITS, UPDATE_COURSE_GRADE_LIMITS,
|
||||
INITIALIZE_FILTERS,
|
||||
RESET_FILTERS,
|
||||
UPDATE_ASSIGNMENT_FILTER,
|
||||
UPDATE_ASSIGNMENT_LIMITS,
|
||||
UPDATE_COURSE_GRADE_LIMITS,
|
||||
UPDATE_INCLUDE_COURSE_ROLE_MEMBERS,
|
||||
} from '../constants/actionTypes/filters';
|
||||
import { getFilters } from '../selectors/filters';
|
||||
import { fetchGrades } from './grades';
|
||||
|
||||
const initializeFilters = ({
|
||||
assignment = initialFilters.assignment,
|
||||
@@ -12,6 +19,7 @@ const initializeFilters = ({
|
||||
assignmentGradeMax = initialFilters.assignmentGradeMax,
|
||||
courseGradeMin = initialFilters.courseGradeMin,
|
||||
courseGradeMax = initialFilters.assignmentGradeMax,
|
||||
includeCourseRoleMembers = initialFilters.includeCourseRoleMembers,
|
||||
}) => ({
|
||||
type: INITIALIZE_FILTERS,
|
||||
data: {
|
||||
@@ -23,6 +31,7 @@ const initializeFilters = ({
|
||||
assignmentGradeMax,
|
||||
courseGradeMin,
|
||||
courseGradeMax,
|
||||
includeCourseRoleMembers,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -50,7 +59,21 @@ const updateCourseGradeFilter = (courseGradeMin, courseGradeMax, courseId) => ({
|
||||
},
|
||||
});
|
||||
|
||||
const updateIncludeCourseRoleMembersFilter = (includeCourseRoleMembers) => ({
|
||||
type: UPDATE_INCLUDE_COURSE_ROLE_MEMBERS,
|
||||
data: {
|
||||
includeCourseRoleMembers,
|
||||
},
|
||||
});
|
||||
|
||||
const updateIncludeCourseRoleMembers = includeCourseRoleMembers => (dispatch, getState) => {
|
||||
dispatch(updateIncludeCourseRoleMembersFilter(includeCourseRoleMembers));
|
||||
const state = getState();
|
||||
const { cohort, track, assignmentType } = getFilters(state);
|
||||
dispatch(fetchGrades(state.grades.courseId, cohort, track, assignmentType));
|
||||
};
|
||||
|
||||
export {
|
||||
initializeFilters, resetFilters, updateAssignmentFilter,
|
||||
updateAssignmentLimits, updateCourseGradeFilter,
|
||||
updateAssignmentLimits, updateCourseGradeFilter, updateIncludeCourseRoleMembers,
|
||||
};
|
||||
|
||||
@@ -141,6 +141,7 @@ const fetchGrades = (
|
||||
assignmentGradeMin: assignmentMin,
|
||||
courseGradeMin,
|
||||
courseGradeMax,
|
||||
includeCourseRoleMembers,
|
||||
} = getFilters(getState());
|
||||
const { id: assignmentId } = assignment || {};
|
||||
const assignmentGradeMax = formatMaxAssignmentGrade(assignmentMax, { assignmentId });
|
||||
@@ -158,6 +159,7 @@ const fetchGrades = (
|
||||
assignmentGradeMin,
|
||||
courseGradeMin: courseGradeMinFormatted,
|
||||
courseGradeMax: courseGradeMaxFormatted,
|
||||
includeCourseRoleMembers,
|
||||
},
|
||||
|
||||
)
|
||||
|
||||
@@ -36,7 +36,7 @@ describe('actions', () => {
|
||||
const expectedCohort = 1;
|
||||
const expectedTrack = 'verified';
|
||||
const expectedAssignmentType = 'Exam';
|
||||
const fetchGradesURL = `${configuration.LMS_BASE_URL}/api/grades/v1/gradebook/${courseId}/?page_size=25&cohort_id=${expectedCohort}&enrollment_mode=${expectedTrack}`;
|
||||
const fetchGradesURL = `${configuration.LMS_BASE_URL}/api/grades/v1/gradebook/${courseId}/?page_size=25&cohort_id=${expectedCohort}&enrollment_mode=${expectedTrack}&excluded_course_roles=all`;
|
||||
const responseData = {
|
||||
next: `${fetchGradesURL}&cursor=2344fda`,
|
||||
previous: null,
|
||||
|
||||
@@ -3,7 +3,8 @@ const RESET_FILTERS = 'RESET_FILTERS';
|
||||
const UPDATE_ASSIGNMENT_FILTER = 'UPDATE_ASSIGNMENT_FILTER';
|
||||
const UPDATE_ASSIGNMENT_LIMITS = 'UPDATE_ASSIGNMENT_LIMITS';
|
||||
const UPDATE_COURSE_GRADE_LIMITS = 'UPDATE_COURSE_GRADE_LIMITS';
|
||||
const UPDATE_INCLUDE_COURSE_ROLE_MEMBERS = 'UPDATE_INCLUDE_COURSE_ROLE_MEMBERS';
|
||||
export {
|
||||
INITIALIZE_FILTERS, RESET_FILTERS, UPDATE_ASSIGNMENT_FILTER,
|
||||
UPDATE_ASSIGNMENT_LIMITS, UPDATE_COURSE_GRADE_LIMITS,
|
||||
UPDATE_ASSIGNMENT_LIMITS, UPDATE_COURSE_GRADE_LIMITS, UPDATE_INCLUDE_COURSE_ROLE_MEMBERS,
|
||||
};
|
||||
|
||||
@@ -7,6 +7,7 @@ const initialFilters = {
|
||||
assignmentGradeMax: '100',
|
||||
courseGradeMin: '0',
|
||||
courseGradeMax: '100',
|
||||
includeCourseRoleMembers: false,
|
||||
};
|
||||
|
||||
export default initialFilters;
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
import { GOT_GRADES, FILTER_BY_ASSIGNMENT_TYPE } from '../constants/actionTypes/grades';
|
||||
|
||||
import {
|
||||
INITIALIZE_FILTERS, UPDATE_ASSIGNMENT_FILTER, UPDATE_ASSIGNMENT_LIMITS, UPDATE_COURSE_GRADE_LIMITS, RESET_FILTERS,
|
||||
INITIALIZE_FILTERS,
|
||||
UPDATE_ASSIGNMENT_FILTER,
|
||||
UPDATE_ASSIGNMENT_LIMITS,
|
||||
UPDATE_COURSE_GRADE_LIMITS,
|
||||
RESET_FILTERS,
|
||||
UPDATE_INCLUDE_COURSE_ROLE_MEMBERS,
|
||||
} from '../constants/actionTypes/filters';
|
||||
|
||||
import initialFilters from '../constants/filters';
|
||||
@@ -70,6 +75,11 @@ const reducer = (state = initialState, action) => {
|
||||
courseGradeMin: action.data.courseGradeMin,
|
||||
courseGradeMax: action.data.courseGradeMax,
|
||||
};
|
||||
case UPDATE_INCLUDE_COURSE_ROLE_MEMBERS:
|
||||
return {
|
||||
...state,
|
||||
includeCourseRoleMembers: action.data.includeCourseRoleMembers,
|
||||
};
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
||||
@@ -36,6 +36,9 @@ class LmsApiService {
|
||||
if (options.courseGradeMax) {
|
||||
queryParams.course_grade_max = options.courseGradeMax;
|
||||
}
|
||||
if (!options.includeCourseRoleMembers) {
|
||||
queryParams.excluded_course_roles = ['all'];
|
||||
}
|
||||
|
||||
const queryParamString = Object.keys(queryParams)
|
||||
.map(attr => `${attr}=${encodeURIComponent(queryParams[attr])}`)
|
||||
@@ -96,7 +99,7 @@ class LmsApiService {
|
||||
|
||||
static getGradeExportCsvUrl(courseId, options = {}) {
|
||||
const queryParams = ['track', 'cohort', 'assignment', 'assignmentType', 'assignmentGradeMax',
|
||||
'assignmentGradeMin', 'courseGradeMin', 'courseGradeMax']
|
||||
'assignmentGradeMin', 'courseGradeMin', 'courseGradeMax', 'excludedCourseRoles']
|
||||
.filter(opt => options[opt]
|
||||
&& options[opt] !== 'All')
|
||||
.map(opt => `${opt}=${encodeURIComponent(options[opt])}`)
|
||||
|
||||
Reference in New Issue
Block a user