Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
6772416
Scaffold GradingOverview function
remo5000 Jul 25, 2018
95ed081
Change GradingOverview to fit backend call
remo5000 Jul 26, 2018
2971518
Modify mock data to fit new GradingOverview
remo5000 Jul 26, 2018
4ee7a74
Modify link in table
remo5000 Jul 26, 2018
11b4b73
Edit table rendering
remo5000 Jul 26, 2018
9cd6e5d
Add scaffold function getGrading
remo5000 Jul 26, 2018
76c7d7b
Add conversion for grading
remo5000 Jul 26, 2018
2489f56
Modify gradingShape to fit backend
remo5000 Jul 26, 2018
d160197
Fix syntax and import errors for saga
remo5000 Jul 26, 2018
4bb7f93
Fix grading usage in component
remo5000 Jul 26, 2018
398f209
Modify mock gradings
remo5000 Jul 26, 2018
168f6f2
Fix imports for castBackend
remo5000 Jul 26, 2018
c6aa1bb
Scaffold semantic changes to GradingEditor
remo5000 Jul 26, 2018
c127eaa
Fix a minor naming error for GradingEditor
remo5000 Jul 26, 2018
56a7a72
Modify props for GradingEditor
remo5000 Jul 26, 2018
bef826b
Remove StateProps for GradingEditor
remo5000 Jul 26, 2018
21c5873
Change props passed to GradingEditor
remo5000 Jul 26, 2018
eebb6da
Remove actions related to updating xp and comments
remo5000 Jul 26, 2018
f984702
Remove gradingCommentsValue & gradingXP from state
remo5000 Jul 26, 2018
9f95f5b
Rename action arguments and handler
remo5000 Jul 26, 2018
0c46589
Add Prompt when there are unsaved changes
remo5000 Jul 26, 2018
9af5142
Add visual indication for unsaved work
remo5000 Jul 26, 2018
e5bde16
Use minimal button
remo5000 Jul 26, 2018
0f27fb6
Add id props to action and component
remo5000 Jul 27, 2018
32d02fc
Remove SAVE_GRADING_INPUT from index saga
remo5000 Jul 27, 2018
aedd5fd
Add scaffold function for postGrading & takeEvery
remo5000 Jul 27, 2018
6de0e76
Pass submissionId to GradingEditor
remo5000 Jul 27, 2018
4e55c19
Finish up saga for saving
remo5000 Jul 27, 2018
a27cda6
Copy saga to mocks
remo5000 Jul 27, 2018
c14d989
Update action name to SUBMIT_GRADING
remo5000 Jul 27, 2018
b3da4d5
Rename action in sagas
remo5000 Jul 27, 2018
159e0f2
Rename action in container
remo5000 Jul 27, 2018
a84b2d7
Format files
remo5000 Jul 27, 2018
1ccf471
Refactor FETCH_GRADING_OVERVIEWS saga
remo5000 Jul 30, 2018
45c9b89
Refactor FETCH_GRADING saga
remo5000 Jul 30, 2018
38e506a
Refactor SUBMIT_GRADING saga
remo5000 Jul 30, 2018
11268ab
Move postGrading
remo5000 Jul 30, 2018
52d514c
Refactor postGrading
remo5000 Jul 30, 2018
6b96677
Add getGradingOverviews again
remo5000 Jul 30, 2018
f7714c0
Add getGrading method again
remo5000 Jul 30, 2018
3f439a6
Fix imports
remo5000 Jul 30, 2018
799895f
Fix response param parsing for getGradingOverviews
remo5000 Jul 30, 2018
3c0ef15
Fix response parsing for getGrading
remo5000 Jul 30, 2018
fb686db
Fix constructor for GradingEditor
remo5000 Jul 30, 2018
056efee
Format file
remo5000 Jul 30, 2018
b7785d8
Use string for adjustmentInput
remo5000 Aug 2, 2018
8f0eef0
Format file
remo5000 Aug 2, 2018
9f9a4b3
Remove failsafe for adjustment
remo5000 Aug 2, 2018
b6c9007
Make some requested changes
remo5000 Aug 2, 2018
77abac3
Remove redundant mapStateToProps
remo5000 Aug 2, 2018
517c411
Add review changes for saga
remo5000 Aug 2, 2018
8b50f92
Add documentation for castLibrary
remo5000 Aug 2, 2018
81d2df2
Format files
remo5000 Aug 2, 2018
7c4d25f
Add warning for out of bound grading
remo5000 Aug 2, 2018
9b3fa99
Add documentation
remo5000 Aug 2, 2018
7a6bfde
Format file
remo5000 Aug 2, 2018
ce0e929
Update request body for postGrading
remo5000 Aug 3, 2018
33ec404
Remove grade parameter from submitGrading
remo5000 Aug 3, 2018
7ba31a2
Remove grade for saga function
remo5000 Aug 3, 2018
0e475f4
Make review changes
remo5000 Aug 3, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions src/actions/actionTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,10 @@ export const EVAL_EDITOR = 'EVAL_EDITOR'
export const EVAL_REPL = 'EVAL_REPL'
export const PLAYGROUND_EXTERNAL_SELECT = 'PLAYGROUND_EXTERNAL_SELECT '
export const RESET_WORKSPACE = 'RESET_WORKSPACE'
export const SAVE_GRADING_INPUT = 'SAVE_GRADING_INPUT'
export const SEND_REPL_INPUT_TO_OUTPUT = 'SEND_REPL_INPUT_TO_OUTPUT'
export const UPDATE_CURRENT_ASSESSMENT_ID = 'UPDATE_CURRENT_ASSESSMENT_ID'
export const UPDATE_CURRENT_SUBMISSION_ID = 'UPDATE_CURRENT_SUBMISSION_ID'
export const UPDATE_EDITOR_VALUE = 'UPDATE_EDITOR_VALUE'
export const UPDATE_GRADING_COMMENTS_VALUE = 'UPDATE_GRADING_COMMENTS_VALUE'
export const UPDATE_GRADING_XP = 'UPDATE_GRADING_XP'
export const UPDATE_HAS_UNSAVED_CHANGES = 'UPDATE_HAS_UNSAVED_CHANGES'
export const UPDATE_REPL_VALUE = 'UPDATE_REPL_VALUE'

Expand All @@ -60,6 +57,7 @@ export const SET_TOKENS = 'SET_TOKENS'
export const SET_USERNAME = 'SET_USERNAME'
export const SUBMIT_ANSWER = 'SUBMIT_ANSWER'
export const SUBMIT_ASSESSMENT = 'SUBMIT_ASSESSMENT'
export const SUBMIT_GRADING = 'SUBMIT_GRADING'
export const UPDATE_HISTORY_HELPERS = 'UPDATE_HISTORY_HELPERS'
export const UPDATE_ASSESSMENT_OVERVIEWS = 'UPDATE_ASSESSMENT_OVERVIEWS'
export const UPDATE_ASSESSMENT = 'UPDATE_ASSESSMENT'
Expand Down
16 changes: 16 additions & 0 deletions src/actions/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,22 @@ export const submitAssessment: ActionCreator<actionTypes.IAction> = (id: number)
payload: id
})

export const submitGrading: ActionCreator<actionTypes.IAction> = (
submissionId: number,
questionId: number,
grade: number,
comment: string,
adjustment: number = 0
) => ({
type: actionTypes.SUBMIT_GRADING,
payload: {
submissionId,
questionId,
comment,
adjustment
}
})

export const updateHistoryHelpers: ActionCreator<actionTypes.IAction> = (loc: string) => ({
type: actionTypes.UPDATE_HISTORY_HELPERS,
payload: loc
Expand Down
23 changes: 0 additions & 23 deletions src/actions/workspaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,29 +193,6 @@ export const updateCurrentSubmissionId = (submissionId: number, questionId: numb
}
})

export const updateGradingCommentsValue: ActionCreator<actionTypes.IAction> = (
newComments: string
) => ({
type: actionTypes.UPDATE_GRADING_COMMENTS_VALUE,
payload: newComments
})

export const updateGradingXP: ActionCreator<actionTypes.IAction> = (newXP: number) => ({
type: actionTypes.UPDATE_GRADING_XP,
payload: newXP
})

export const saveGradingInput: ActionCreator<actionTypes.IAction> = (
gradingCommentsValue: string,
gradingXP: number | undefined
) => ({
type: actionTypes.SAVE_GRADING_INPUT,
payload: {
gradingCommentsValue,
gradingXP
}
})

export const updateHasUnsavedChanges: ActionCreator<actionTypes.IAction> = (
workspaceLocation: WorkspaceLocation,
hasUnsavedChanges: boolean
Expand Down
107 changes: 72 additions & 35 deletions src/components/academy/grading/GradingEditor.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,45 @@
import { ButtonGroup, Icon, NumericInput, Position } from '@blueprintjs/core'
import { ButtonGroup, Icon, Intent, NumericInput, Position } from '@blueprintjs/core'
import { IconNames } from '@blueprintjs/icons'
import * as React from 'react'
import ReactMde, { ReactMdeTypes } from 'react-mde'
import { Prompt } from 'react-router'
import * as Showdown from 'showdown'

import { showWarningMessage } from '../../../utils/notification'
import { stringParamToInt } from '../../../utils/paramParseHelpers'
import { controlButton } from '../../commons'

type GradingEditorProps = DispatchProps & OwnProps & StateProps
type GradingEditorProps = DispatchProps & OwnProps

export type DispatchProps = {
handleGradingCommentsChange: (s: string) => void
handleGradingXPChange: (i: number | undefined) => void
handleGradingInputSave: (s: string, i: number | undefined) => void
handleGradingSave: (
submissionId: number,
questionId: number,
comments: string,
adjustment: number | undefined
) => void
}

export type OwnProps = {
maximumXP: number
}

export type StateProps = {
gradingCommentsValue: string
gradingXP: number | undefined
adjustment: number
comments: string
initialGrade: number
maximumGrade: number
questionId: number
submissionId: number
}

/**
* Keeps track of the current editor state,
* as well as the XP in the numeric input.
* as well as the grade adjustment in the numeric input.
*
* XP can be undefined to show the hint text.
* @prop adjustmentInput a potentially null string. this property being null
* will show the hint text in the NumericInput. This property is a string
* so as to allow input such as the '-' character.
*/
type State = {
mdeState: ReactMdeTypes.MdeState
XPInput: number | undefined
adjustmentInput: string | null
}

class GradingEditor extends React.Component<GradingEditorProps, State> {
Expand All @@ -40,9 +49,9 @@ class GradingEditor extends React.Component<GradingEditorProps, State> {
super(props)
this.state = {
mdeState: {
markdown: this.props.gradingCommentsValue
markdown: props.comments
},
XPInput: this.props.gradingXP
adjustmentInput: props.adjustment.toString()
}
/**
* The markdown-to-html converter for the editor.
Expand All @@ -56,29 +65,27 @@ class GradingEditor extends React.Component<GradingEditorProps, State> {
})
}

/**
* Update the redux state's grading comments value, using the latest
* value in the local state.
*/
public componentWillUnmount() {
this.props.handleGradingCommentsChange(this.state.mdeState.markdown!)
this.props.handleGradingXPChange(this.state.XPInput)
}

public render() {
const hasUnsavedChanges = this.hasUnsavedChanges()
const saveButtonOpts = hasUnsavedChanges ? { intent: Intent.WARNING, minimal: true } : {}
return (
<>
{hasUnsavedChanges ? (
<Prompt
message={'You have changes that may not be saved. Are you sure you want to leave?'}
/>
) : null}
<div className="grading-editor-input-parent">
<ButtonGroup fill={true}>
<NumericInput
onValueChange={this.onXPInputChange}
value={this.state.XPInput}
onValueChange={this.onAdjustmentInputChange}
value={this.state.adjustmentInput || ''}
buttonPosition={Position.LEFT}
placeholder="XP here"
min={0}
max={this.props.maximumXP}
placeholder="Adjust grades relatively here"
min={0 - this.props.initialGrade}
max={this.props.maximumGrade - this.props.initialGrade}
/>
{controlButton('Save', IconNames.FLOPPY_DISK, this.onClickSaveButton)}
{controlButton('Save', IconNames.FLOPPY_DISK, this.onClickSaveButton, saveButtonOpts)}
</ButtonGroup>
</div>
<div className="react-mde-parent">
Expand Down Expand Up @@ -107,18 +114,48 @@ class GradingEditor extends React.Component<GradingEditorProps, State> {
}

private onClickSaveButton = () => {
this.props.handleGradingInputSave(this.state.mdeState.markdown!, this.state.XPInput)
const adjustmentInput = stringParamToInt(this.state.adjustmentInput || undefined) || undefined
const grade = this.props.initialGrade + (adjustmentInput || 0)
if (grade < 0 || grade > this.props.maximumGrade) {
showWarningMessage(
`Grade ${grade.toString()} is out of bounds. Maximum grade is ${this.props.maximumGrade.toString()}.`
)
} else {
this.props.handleGradingSave(
this.props.submissionId,
this.props.questionId,
this.state.mdeState.markdown!,
adjustmentInput
)
}
}

private onXPInputChange = (newValue: number) => {
/**
* Handles changes in the NumericInput, and updates the local State.
*
* @param valueAsNumber an unused parameter, as we use strings for the input. @see State
* @param valueAsString a string that contains the input. To be parsed by another function.
*/
private onAdjustmentInputChange = (valueAsNumber: number, valueAsString: string | null) => {
this.setState({
...this.state,
XPInput: newValue
adjustmentInput: valueAsString
})
}

private handleValueChange = (mdeState: ReactMdeTypes.MdeState) => {
this.setState({ mdeState })
this.setState({
...this.state,
mdeState
})
}

private hasUnsavedChanges = () => {
const adjustmentInput = stringParamToInt(this.state.adjustmentInput || undefined)
return (
this.props.comments !== this.state.mdeState.markdown ||
this.props.adjustment !== adjustmentInput
)
}

private generateMarkdownPreview = (markdown: string) =>
Expand Down
2 changes: 1 addition & 1 deletion src/components/academy/grading/GradingNavLink.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class GradingNavLink extends React.Component<GradingNavLinkProps, {}> {
public render() {
return (
<NavLink to={`/academy/grading/${this.props.data.submissionId}`} activeClassName="pt-active">
{this.props.data.graded ? 'Done' : 'Not Graded'}
{'Add comments'}
</NavLink>
)
}
Expand Down
11 changes: 10 additions & 1 deletion src/components/academy/grading/GradingWorkspace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,16 @@ class GradingWorkspace extends React.Component<GradingWorkspaceProps> {
label: `Grading: Question ${questionId}`,
icon: IconNames.TICK,
/* Render an editor with the xp given to the current question. */
body: <GradingEditor maximumXP={props.grading![questionId].maximumXP} />
body: (
<GradingEditor
adjustment={props.grading![questionId].grade.adjustment}
comments={props.grading![questionId].grade.comment}
initialGrade={props.grading![questionId].grade.grade}
maximumGrade={props.grading![questionId].maximumGrade}
questionId={questionId}
submissionId={props.submissionId}
/>
)
},
{
label: `Task ${questionId}`,
Expand Down
12 changes: 7 additions & 5 deletions src/components/academy/grading/gradingShape.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ import { AssessmentCategory, IQuestion, MCQChoice } from '../../assessment/asses
* for a particular assessment. Used for display in the UI.
*/
export type GradingOverview = {
adjustments: number
assessmentId: number
assessmentName: string
assessmentCategory: AssessmentCategory
currentXP: number
graded: boolean
maximumXP: number
initialGrade: number
currentGrade: number
maximumGrade: number
studentId: number
studentName: string
submissionId: number
Expand All @@ -28,10 +29,11 @@ export type Grading = GradingQuestion[]
*/
export type GradingQuestion = {
question: IAnsweredQuestion
maximumXP: number
maximumGrade: number
grade: {
xp: number
adjustment: number
comment: string
grade: number
}
}

Expand Down
10 changes: 6 additions & 4 deletions src/components/academy/grading/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,13 @@ class Grading extends React.Component<IGradingProps, State> {
{ headerName: 'Assessment Category', field: 'assessmentCategory' },
{ headerName: 'Student ID', field: 'studentId' },
{ headerName: 'Student Name', field: 'studentName' },
{ headerName: 'Current XP', field: 'currentXP' },
{ headerName: 'Maximum XP', field: 'maximumXP' },
{ headerName: 'Initial Grade', field: 'initialGrade' },
{ headerName: 'Adjustments', field: 'adjustments' },
{ headerName: 'Current Grade', field: 'currentGrade' },
{ headerName: 'Maximum Grade', field: 'maximumGrade' },
{
headerName: 'Graded',
field: 'graded',
headerName: 'Edit',
field: '',
cellRendererFramework: GradingNavLink
}
]
Expand Down
23 changes: 5 additions & 18 deletions src/containers/academy/grading/GradingEditorContainer.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,15 @@
import { connect, MapDispatchToProps, MapStateToProps } from 'react-redux'
import { connect, MapDispatchToProps } from 'react-redux'
import { bindActionCreators, Dispatch } from 'redux'

import { saveGradingInput, updateGradingCommentsValue, updateGradingXP } from '../../../actions'
import GradingEditor, {
DispatchProps,
StateProps
} from '../../../components/academy/grading/GradingEditor'
import { IState } from '../../../reducers/states'

const mapStateToProps: MapStateToProps<StateProps, {}, IState> = state => {
return {
gradingCommentsValue: state.workspaces.grading.gradingCommentsValue,
gradingXP: state.workspaces.grading.gradingXP
}
}
import { submitGrading } from '../../../actions'
import GradingEditor, { DispatchProps } from '../../../components/academy/grading/GradingEditor'

const mapDispatchToProps: MapDispatchToProps<DispatchProps, {}> = (dispatch: Dispatch<any>) =>
bindActionCreators<DispatchProps>(
{
handleGradingCommentsChange: updateGradingCommentsValue,
handleGradingXPChange: updateGradingXP,
handleGradingInputSave: saveGradingInput
handleGradingSave: submitGrading
},
dispatch
)

export default connect(mapStateToProps, mapDispatchToProps)(GradingEditor)
export default connect(null, mapDispatchToProps)(GradingEditor)
27 changes: 27 additions & 0 deletions src/mocks/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { call, put, select, takeEvery } from 'redux-saga/effects'

import * as actions from '../actions'
import * as actionTypes from '../actions/actionTypes'
import { Grading, GradingQuestion } from '../components/academy/grading/gradingShape'
import { IQuestion } from '../components/assessment/assessmentShape'
import { IState } from '../reducers/states'
import { history } from '../utils/history'
Expand Down Expand Up @@ -74,4 +75,30 @@ export function* mockBackendSaga(): SagaIterator {
yield put(actions.updateAssessment(newAssessment))
yield call(showSuccessMessage, 'Saved!', 1000)
})

yield takeEvery(actionTypes.SUBMIT_GRADING, function*(action) {
const {
submissionId,
questionId,
grade,
comment,
adjustment
} = (action as actionTypes.IAction).payload
// Now, update the grade for the question in the Grading in the store
const grading: Grading = yield select((state: IState) =>
state.session.gradings.get(submissionId)
)
const newGrading = grading.slice().map((gradingQuestion: GradingQuestion) => {
if (gradingQuestion.question.id === questionId) {
gradingQuestion.grade = {
adjustment,
comment,
grade
}
}
return gradingQuestion
})
yield put(actions.updateGrading(submissionId, newGrading))
yield call(showSuccessMessage, 'Saved!', 1000)
})
}
Loading