From 354299a8b204c4278a5ec139a7a8a5b5504f72d5 Mon Sep 17 00:00:00 2001 From: Petr Stefan Date: Thu, 21 Dec 2017 12:05:56 +0100 Subject: [PATCH 1/4] Show course ID for student SIS groups --- .../SisIntegrationContainer.js | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/containers/SisIntegrationContainer/SisIntegrationContainer.js b/src/containers/SisIntegrationContainer/SisIntegrationContainer.js index e23ce8459..a5d5c4115 100644 --- a/src/containers/SisIntegrationContainer/SisIntegrationContainer.js +++ b/src/containers/SisIntegrationContainer/SisIntegrationContainer.js @@ -92,6 +92,12 @@ class SisIntegrationContainer extends Component { defaultMessage="Name" /> + + + {getLocalizedName(group, locale)} + + + {group.sisCode} + + {group.primaryAdminsIds.map(id => Date: Thu, 21 Dec 2017 16:53:21 +0100 Subject: [PATCH 2/4] Fix loading issue on new students and supervisors --- .../Users/SupervisorsList/SupervisorsList.js | 19 +++++++++--------- src/components/Users/UsersList/UsersList.js | 8 ++------ src/pages/Group/Group.js | 20 ++++++++++++++++--- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/src/components/Users/SupervisorsList/SupervisorsList.js b/src/components/Users/SupervisorsList/SupervisorsList.js index efaab793a..7b74fdf3b 100644 --- a/src/components/Users/SupervisorsList/SupervisorsList.js +++ b/src/components/Users/SupervisorsList/SupervisorsList.js @@ -15,15 +15,16 @@ const SupervisorsList = ({ }) => - {users.map(user => - - )} + {isLoaded && + users.map(user => + + )} {users.length === 0 && isLoaded && diff --git a/src/components/Users/UsersList/UsersList.js b/src/components/Users/UsersList/UsersList.js index a40a45a95..9b58e689f 100644 --- a/src/components/Users/UsersList/UsersList.js +++ b/src/components/Users/UsersList/UsersList.js @@ -13,12 +13,8 @@ const UsersList = ({ users = [], createActions, intl, ...rest }) => const bName = b.name.lastName + ' ' + b.name.firstName; return aName.localeCompare(bName, intl.locale); }) - .map(user => - + .map((user, i) => + )} {users.length === 0 && diff --git a/src/pages/Group/Group.js b/src/pages/Group/Group.js index c9093fca6..f4eb80114 100644 --- a/src/pages/Group/Group.js +++ b/src/pages/Group/Group.js @@ -27,7 +27,7 @@ import { fetchInstanceGroupsIfNeeded, fetchSubgroups } from '../../redux/modules/groups'; -import { fetchGroupsStatsIfNeeded } from '../../redux/modules/stats'; +import { fetchGroupsStats } from '../../redux/modules/stats'; import { fetchSupervisors, fetchStudents } from '../../redux/modules/users'; import { fetchAssignmentsForGroup, @@ -61,6 +61,7 @@ import { getStatusesForLoggedUser } from '../../redux/selectors/stats'; import { getLocalizedName } from '../../helpers/getLocalizedData'; import withLinks from '../../hoc/withLinks'; +import { isReady } from '../../redux/helpers/resourceManager/index'; class Group extends Component { static isAdminOrSupervisorOf = (group, userId) => @@ -86,7 +87,7 @@ class Group extends Component { ? Promise.all([ dispatch(fetchAssignmentsForGroup(groupId)), dispatch(fetchStudents(groupId)), - dispatch(fetchGroupsStatsIfNeeded(groupId)) + dispatch(fetchGroupsStats(groupId)) ]) : Promise.resolve() ]) @@ -105,6 +106,17 @@ class Group extends Component { if (groupId !== newProps.params.groupId) { newProps.loadAsync(newProps.userId, newProps.isSuperAdmin); } + + if (isReady(this.props.group) && isReady(newProps.group)) { + const thisData = this.props.group.toJS().data; + const newData = newProps.group.toJS().data; + if (thisData.supervisors.length !== newData.supervisors.length) { + newProps.refetchSupervisors(); + } + if (thisData.students.length !== newData.students.length) { + newProps.loadAsync(newProps.userId, newProps.isSuperAdmin); + } + } } getBreadcrumbs = () => { @@ -278,6 +290,7 @@ Group.propTypes = { assignExercise: PropTypes.func.isRequired, createGroupExercise: PropTypes.func.isRequired, push: PropTypes.func.isRequired, + refetchSupervisors: PropTypes.func.isRequired, links: PropTypes.object, intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; @@ -322,7 +335,8 @@ const mapDispatchToProps = (dispatch, { params }) => ({ dispatch(assignExercise(params.groupId, exerciseId)), createGroupExercise: () => dispatch(createExercise({ groupId: params.groupId })), - push: url => dispatch(push(url)) + push: url => dispatch(push(url)), + refetchSupervisors: () => dispatch(fetchSupervisors(params.groupId)) }); export default withLinks( From fd205cb15fb15d06d321f309712057f8cc51db5a Mon Sep 17 00:00:00 2001 From: Petr Stefan Date: Fri, 22 Dec 2017 17:33:01 +0100 Subject: [PATCH 3/4] Refresh reference evaluations after resubmit --- src/pages/ReferenceSolution/ReferenceSolution.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/pages/ReferenceSolution/ReferenceSolution.js b/src/pages/ReferenceSolution/ReferenceSolution.js index 36c6a9787..296bdb8fa 100644 --- a/src/pages/ReferenceSolution/ReferenceSolution.js +++ b/src/pages/ReferenceSolution/ReferenceSolution.js @@ -155,7 +155,10 @@ class ReferenceSolution extends Component { } + {bestSubmission && + }
{group} + + {data => + data.lastSubmission + ? + {data.lastSubmission.evaluation.points} + {data.bonusPoints > 0 && + + +{data.bonusPoints} + } + {data.bonusPoints < 0 && + + {data.bonusPoints} + }/{data.maxPoints} + + : } + + {', '} @@ -76,6 +97,7 @@ AssignmentTableRow.propTypes = { }), status: PropTypes.string, userId: PropTypes.string, + bestSubmission: PropTypes.object, links: PropTypes.object }; diff --git a/src/components/Assignments/Assignment/AssignmentsTable/AssignmentsTable.js b/src/components/Assignments/Assignment/AssignmentsTable/AssignmentsTable.js index 6959343dd..96a72d918 100644 --- a/src/components/Assignments/Assignment/AssignmentsTable/AssignmentsTable.js +++ b/src/components/Assignments/Assignment/AssignmentsTable/AssignmentsTable.js @@ -44,11 +44,25 @@ const compareAssignments = (a, b) => { } }; +const displayPoints = (bestSubmissions, assignments) => { + const assignmentIds = assignments + .filter(isReady) + .map(getJsData) + .map(assignment => assignment.id); + return Object.keys(bestSubmissions) + .filter(key => assignmentIds.indexOf(key) >= 0) + .reduce((acc, key) => { + acc = acc || bestSubmissions[key]; + return acc; + }, false); +}; + const AssignmentsTable = ({ assignments = List(), statuses = [], showGroup = true, userId = null, + bestSubmissions = {}, intl: { locale } }) => @@ -68,6 +82,13 @@ const AssignmentsTable = ({ defaultMessage="Group" /> } + {displayPoints(bestSubmissions, assignments) && + } @@ -109,6 +131,7 @@ AssignmentsTable.propTypes = { showGroup: PropTypes.bool, statuses: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), userId: PropTypes.string, + bestSubmissions: PropTypes.object, intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; diff --git a/src/components/Groups/StudentsView/StudentsView.js b/src/components/Groups/StudentsView/StudentsView.js index 563c89f1e..61d0bab82 100644 --- a/src/components/Groups/StudentsView/StudentsView.js +++ b/src/components/Groups/StudentsView/StudentsView.js @@ -14,6 +14,7 @@ const StudentsView = ({ group, statuses = [], assignments, + bestSubmissions, isAdmin = false, intl: { locale } }) => @@ -47,6 +48,7 @@ const StudentsView = ({ assignments={assignments} showGroup={false} statuses={statuses} + bestSubmissions={bestSubmissions} /> @@ -83,6 +85,7 @@ StudentsView.propTypes = { assignments: ImmutablePropTypes.list.isRequired, statuses: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), isAdmin: PropTypes.bool, + bestSubmissions: PropTypes.object, intl: PropTypes.shape({ locale: PropTypes.string.isRequired }).isRequired }; diff --git a/src/pages/Group/Group.js b/src/pages/Group/Group.js index f4eb80114..a1022c291 100644 --- a/src/pages/Group/Group.js +++ b/src/pages/Group/Group.js @@ -62,6 +62,8 @@ import { getStatusesForLoggedUser } from '../../redux/selectors/stats'; import { getLocalizedName } from '../../helpers/getLocalizedData'; import withLinks from '../../hoc/withLinks'; import { isReady } from '../../redux/helpers/resourceManager/index'; +import { fetchBestSubmission } from '../../redux/modules/groupResults'; +import { getBestSubmissionsForLoggedInUser } from '../../redux/selectors/groupResults'; class Group extends Component { static isAdminOrSupervisorOf = (group, userId) => @@ -89,6 +91,13 @@ class Group extends Component { dispatch(fetchStudents(groupId)), dispatch(fetchGroupsStats(groupId)) ]) + : Promise.resolve(), + group.students.indexOf(userId) >= 0 + ? Promise.all( + group.assignments.all.map(assignmentId => + dispatch(fetchBestSubmission(userId, assignmentId)) + ) + ) : Promise.resolve() ]) ), @@ -105,6 +114,7 @@ class Group extends Component { if (groupId !== newProps.params.groupId) { newProps.loadAsync(newProps.userId, newProps.isSuperAdmin); + return; } if (isReady(this.props.group) && isReady(newProps.group)) { @@ -174,6 +184,7 @@ class Group extends Component { publicAssignments = List(), stats, statuses, + bestSubmissions, isStudent, isAdmin, isSuperAdmin, @@ -215,13 +226,13 @@ class Group extends Component {

} - {(isStudent || isSupervisor || isAdmin || isSuperAdmin) && } @@ -287,6 +298,7 @@ Group.propTypes = { loadAsync: PropTypes.func, stats: PropTypes.object, statuses: PropTypes.oneOfType([PropTypes.object, PropTypes.array]), + bestSubmissions: PropTypes.object, assignExercise: PropTypes.func.isRequired, createGroupExercise: PropTypes.func.isRequired, push: PropTypes.func.isRequired, @@ -311,6 +323,7 @@ const mapStateToProps = (state, { params: { groupId } }) => { allAssignments: groupsAllAssignmentsSelector(state, groupId), groupExercises: getExercisesForGroup(state, groupId), statuses: getStatusesForLoggedUser(state, groupId), + bestSubmissions: getBestSubmissionsForLoggedInUser(state), supervisors: supervisorsOfGroupSelector(state, groupId), students: studentsOfGroupSelector(state, groupId), isStudent: isStudentOf(userId, groupId)(state), diff --git a/src/redux/modules/groupResults.js b/src/redux/modules/groupResults.js index 56b277821..73756bdbd 100644 --- a/src/redux/modules/groupResults.js +++ b/src/redux/modules/groupResults.js @@ -52,21 +52,14 @@ const reducer = handleActions( [additionalActionTypes.BEST_SUBMISSION_FULFILLED]: ( state, - { payload = {}, meta: { assignmentId } } + { payload = {}, meta: { assignmentId, userId } } ) => - Object.keys(payload).reduce( - (state, userId) => - state - .setIn( - ['resources', assignmentId, userId, 'data'], - fromJS(payload[userId]) - ) - .setIn( - ['resources', assignmentId, userId, 'state'], - resourceStatus.FULFILLED - ), - state - ) + state + .setIn(['resources', assignmentId, userId, 'data'], fromJS(payload)) + .setIn( + ['resources', assignmentId, userId, 'state'], + resourceStatus.FULFILLED + ) }), initialState ); diff --git a/src/redux/selectors/groupResults.js b/src/redux/selectors/groupResults.js index 78b9df8db..6ff10785f 100644 --- a/src/redux/selectors/groupResults.js +++ b/src/redux/selectors/groupResults.js @@ -1,4 +1,5 @@ import { createSelector } from 'reselect'; +import { loggedInUserIdSelector } from './auth'; export const getGroupResults = state => state.groupResults; export const getBestSubmission = (userId, assignmentId) => @@ -6,8 +7,8 @@ export const getBestSubmission = (userId, assignmentId) => getGroupResults, groupResults => groupResults && - groupResults.getIn(['resources', assignmentId]) !== null && - groupResults.getIn(['resources', assignmentId, userId]) !== null + groupResults.getIn(['resources', assignmentId]) !== null && + groupResults.getIn(['resources', assignmentId, userId]) !== null ? groupResults.getIn(['resources', assignmentId, userId]) : null ); @@ -29,3 +30,16 @@ export const getBestSubmissionsAssoc = (assignments, users) => return submissions; }); + +export const getBestSubmissionsForLoggedInUser = createSelector( + [getGroupResults, loggedInUserIdSelector], + (groupResults, userId) => { + const submissions = {}; + groupResults + .get('resources') + .forEach( + (value, assignmentId) => (submissions[assignmentId] = value.get(userId)) + ); + return submissions; + } +);
+ + )}