Skip to content

Commit

Permalink
Merge pull request #7 from edx/schen/cohorts
Browse files Browse the repository at this point in the history
Create the cohorts and track dropdown for filtering students
  • Loading branch information
schenedx authored Nov 14, 2018
2 parents 2c890e5 + a4dc135 commit 231685e
Show file tree
Hide file tree
Showing 15 changed files with 351 additions and 40 deletions.
29 changes: 23 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"font-awesome": "^4.7.0",
"history": "^4.7.2",
"prop-types": "^15.5.10",
"query-string": "^6.2.0",
"react": "^16.2.0",
"react-dom": "^16.2.0",
"react-redux": "^5.0.7",
Expand Down
9 changes: 9 additions & 0 deletions src/components/Gradebook/gradebook.scss
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
.student-filters{
display: flex;
.label{
padding-top: 30px;
}
.form-group{
margin-left: 10px;
}
}
.gbook {
overflow-x: scroll;

Expand Down
126 changes: 105 additions & 21 deletions src/components/Gradebook/index.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import emailPropType from 'email-prop-type';
import { Button, Modal, SearchField, Table } from '@edx/paragon';
import { Button, Modal, SearchField, Table, InputSelect } from '@edx/paragon';
import queryString from 'query-string';


export default class Gradebook extends React.Component {
Expand All @@ -21,7 +22,14 @@ export default class Gradebook extends React.Component {
}

componentDidMount() {
this.props.getUserGrades(this.props.match.params.courseId);
const urlQuery = queryString.parse(this.props.location.search);
this.props.getUserGrades(
this.props.match.params.courseId,
urlQuery.cohort,
urlQuery.track,
);
this.props.getTracks(this.props.match.params.courseId);
this.props.getCohorts(this.props.match.params.courseId);
}

sortAlphaDesc = (gradeRowA, gradeRowB) => {
Expand Down Expand Up @@ -223,6 +231,75 @@ export default class Gradebook extends React.Component {
},
]);
}
updateQueryParams = (queryKey, queryValue) => {
const parsed = queryString.parse(this.props.location.search);
parsed[queryKey] = queryValue;
return `?${queryString.stringify(parsed)}`;
};

mapCohortsEntries = (entries) => {
let mapped = entries.map(entry => ({
id: entry.id,
label: entry.name,
}));
mapped.unshift({id:0, label:'Cohorts'});
return mapped;
};

mapTracksEntries = (entries) => {
let mapped = entries.map(entry => ({
id: entry.slug,
label: entry.name,
}));
mapped.unshift({ label:'Tracks' });
return mapped;
};

updateTracks = (event) => {
const selectedTrackItem = this.props.tracks.find(x=>x.name===event);
let selectedTrackSlug = null;
if(selectedTrackItem) {
selectedTrackSlug = selectedTrackItem.slug;
}
this.props.getUserGrades(
this.props.match.params.courseId,
this.props.selectedCohort,
selectedTrackSlug,
);
const updatedQueryStrings = this.updateQueryParams('track', selectedTrackSlug)
this.props.history.push(updatedQueryStrings);
};

updateCohorts = (event) => {
const selectedCohortItem = this.props.cohorts.find(x=>x.name===event);
let selectedCohortId = null;
if(selectedCohortItem) {
selectedCohortId = selectedCohortItem.id;
}
this.props.getUserGrades(
this.props.match.params.courseId,
selectedCohortId,
this.props.selectedTrack,
);
const updatedQueryStrings = this.updateQueryParams('cohort', selectedCohortId)
this.props.history.push(updatedQueryStrings);
};

mapSelectedCohortEntry = (entry) => {
const selectedCohortEntry = this.props.cohorts.find(x => x.id === parseInt(entry, 10));
if (selectedCohortEntry) {
return selectedCohortEntry.name;
}
return 'Cohorts';
};

mapSelectedTrackEntry = (entry) => {
const selectedTrackEntry = this.props.tracks.find(x => x.slug === entry);
if (selectedTrackEntry) {
return selectedTrackEntry.name;
}
return 'Tracks';
};

render() {
return (
Expand Down Expand Up @@ -294,21 +371,44 @@ export default class Gradebook extends React.Component {
type="radio"
name="category"
value="exam"
onClick={() => this.setState({ headings: this.mapHeadingsExam(this.props.results[0]) })}
onClick={() => this.setState({ headings: this.mapHeadingsExam(this.props.results[0]) })}
/>
Exam
</label>
</span>
</div>
{(this.props.tracks.length > 0 || this.props.cohorts.length > 0) &&
<div className="student-filters">
<span className="label">
Student Groups:
</span>
{this.props.tracks.length > 0 &&
<InputSelect
name='Tracks'
value={this.mapSelectedTrackEntry(this.props.selectedTrack)}
options={this.mapTracksEntries(this.props.tracks)}
onChange={this.updateTracks}
/>
}
{this.props.cohorts.length > 0 &&
<InputSelect
name='Cohorts'
value={this.mapSelectedCohortEntry(this.props.selectedCohort)}
options={this.mapCohortsEntries(this.props.cohorts)}
onChange={this.updateCohorts}
/>
}
</div>
}
</div>
<div>
<div style={{ marginLeft: '10px', marginBottom: '10px' }}>
<a href="https://www.google./com">Download Grade Report</a>
</div>
<SearchField
onSubmit={value => this.props.searchForUser(this.props.match.params.courseId, value)}
onSubmit={value => this.props.searchForUser(this.props.match.params.courseId, value, this.props.selectedCohort, this.props.selectedTrack)}
onChange={filterValue => this.setState({ filterValue })}
onClear={() => this.props.getUserGrades(this.props.match.params.courseId)}
onClear={() => this.props.getUserGrades(this.props.match.params.courseId, this.props.selectedCohort, this.props.selectedTrack)}
value={this.state.filterValue}
/>
</div>
Expand Down Expand Up @@ -360,19 +460,3 @@ export default class Gradebook extends React.Component {
}
}

// CommentDetails.defaultProps = {
// id: null,
// postId: null,
// name: '',
// email: '[email protected]',
// body: '',
// };

// CommentDetails.propTypes = {
// id: PropTypes.number,
// postId: PropTypes.number,
// name: PropTypes.string,
// email: emailPropType,
// body: PropTypes.string,
// };

20 changes: 16 additions & 4 deletions src/containers/GradebookPage/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,32 @@ import { connect } from 'react-redux';

import Gradebook from '../../components/Gradebook';
import { fetchGrades, fetchMatchingUserGrades, updateGrades } from '../../data/actions/grades';
import { fetchCohorts } from '../../data/actions/cohorts';
import { fetchTracks } from '../../data/actions/tracks';

const mapStateToProps = state => (
{
grades: state.grades.results,
tracks: state.tracks.results,
cohorts: state.cohorts.results,
selectedTrack: state.grades.selectedTrack,
selectedCohort: state.grades.selectedCohort,
}
);

const mapDispatchToProps = dispatch => (
{
getUserGrades: (courseId) => {
dispatch(fetchGrades(courseId));
getUserGrades: (courseId, cohort, track) => {
dispatch(fetchGrades(courseId, cohort, track));
},
searchForUser: (courseId, searchText) => {
dispatch(fetchMatchingUserGrades(courseId, searchText));
searchForUser: (courseId, searchText, cohort, track) => {
dispatch(fetchMatchingUserGrades(courseId, searchText, cohort, track));
},
getCohorts: (courseId) => {
dispatch(fetchCohorts(courseId));
},
getTracks: (courseId) => {
dispatch(fetchTracks(courseId));
},
updateGrades: (courseId, updateData) => {
dispatch(updateGrades(courseId, updateData));
Expand Down
31 changes: 31 additions & 0 deletions src/data/actions/cohorts.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
STARTED_FETCHING_COHORTS,
GOT_COHORTS,
ERROR_FETCHING_COHORTS,
} from '../constants/actionTypes/cohorts';
import LmsApiService from '../services/LmsApiService';

const startedFetchingCohorts = () => ({ type: STARTED_FETCHING_COHORTS });
const errorFetchingCohorts = () => ({ type: ERROR_FETCHING_COHORTS });
const gotCohorts = cohorts => ({ type: GOT_COHORTS, cohorts });

const fetchCohorts = courseId => (
(dispatch) => {
dispatch(startedFetchingCohorts());
return LmsApiService.fetchCohorts(courseId)
.then(response => response.data)
.then((data) => {
dispatch(gotCohorts(data.cohorts));
})
.catch((error) => {
dispatch(errorFetchingCohorts());
});
}
);

export {
fetchCohorts,
startedFetchingCohorts,
gotCohorts,
errorFetchingCohorts,
};
19 changes: 12 additions & 7 deletions src/data/actions/grades.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ import LmsApiService from '../services/LmsApiService';
const startedFetchingGrades = () => ({ type: STARTED_FETCHING_GRADES });
const finishedFetchingGrades = () => ({ type: FINISHED_FETCHING_GRADES });
const errorFetchingGrades = () => ({ type: ERROR_FETCHING_GRADES });
const gotGrades = grades => ({ type: GOT_GRADES, grades });
const gotGrades = (grades, cohort, track) => ({
type: GOT_GRADES,
grades,
cohort,
track,
});

const gradeUpdateRequest = () => ({ type: GRADE_UPDATE_REQUEST });
const gradeUpdateSuccess = responseData => ({
Expand All @@ -24,13 +29,13 @@ const gradeUpdateFailure = error => ({
payload: { error },
});

const fetchGrades = courseId => (
const fetchGrades = (courseId, cohort, track) => (
(dispatch) => {
dispatch(startedFetchingGrades());
return LmsApiService.fetchGradebookData(courseId)
return LmsApiService.fetchGradebookData(courseId, null, cohort, track)
.then(response => response.data)
.then((data) => {
dispatch(gotGrades(data.results));
dispatch(gotGrades(data.results, cohort, track));
dispatch(finishedFetchingGrades());
})
.catch((error) => {
Expand All @@ -39,13 +44,13 @@ const fetchGrades = courseId => (
}
);

const fetchMatchingUserGrades = (courseId, searchText) => (
const fetchMatchingUserGrades = (courseId, searchText, cohort, track) => (
(dispatch) => {
dispatch(startedFetchingGrades());
return LmsApiService.fetchGradebookData(courseId, searchText)
return LmsApiService.fetchGradebookData(courseId, searchText, cohort, track)
.then(response => response.data)
.then((data) => {
dispatch(gotGrades(data.results));
dispatch(gotGrades(data.results, cohort, track));
dispatch(finishedFetchingGrades());
})
.catch((error) => {
Expand Down
31 changes: 31 additions & 0 deletions src/data/actions/tracks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import {
STARTED_FETCHING_TRACKS,
GOT_TRACKS,
ERROR_FETCHING_TRACKS,
} from '../constants/actionTypes/tracks';
import LmsApiService from '../services/LmsApiService';

const startedFetchingTracks = () => ({ type: STARTED_FETCHING_TRACKS });
const errorFetchingTracks = () => ({ type: ERROR_FETCHING_TRACKS });
const gotTracks = tracks => ({ type: GOT_TRACKS, tracks });

const fetchTracks = courseId => (
(dispatch) => {
dispatch(startedFetchingTracks());
return LmsApiService.fetchTracks(courseId)
.then(response => response.data)
.then((data) => {
dispatch(gotTracks(data.course_modes));
})
.catch((error) => {
dispatch(errorFetchingTracks());
});
}
);

export {
fetchTracks,
startedFetchingTracks,
gotTracks,
errorFetchingTracks,
};
Loading

0 comments on commit 231685e

Please sign in to comment.