Skip to content

Commit f950cbd

Browse files
authored
Implement XP (#334)
* Add XP to profile (duplicate) (#323) * Add maxGrade and xp to state * Pass props to Profile from State * Remove IS_XP_IMPLEMENTED * Unhide betcha * Use xp and maxgrade for Profile * Update tests and format * Xp assessment (#320) * Update IAssessmentOverview Sticking to how the backend specifies the props, since it's already camel case * Remove maxGrade transform (IAssessmentOverview) * Use maxGrade in assesment cards * Update mock assessment data * Add grade and xp into assessment overview * Hide grade and xp for unopened assessments * Show open date for unopened assessments * Format and update tests * Add XP for grading overview (#321) * Update backend parsing Fix lint error * Modify grading shape * Use new properties for grading overview table * Update mock grading api * Update table field * Update text for editing grading * Sort data by assessmentId, then submissionId * Format file * Improve UI/Functionality of Grading Table (#332) * Improved Table UI and made functionality more intuitive * Improved UI for grading table and made functionality more intutive * Added comments and ran yarn format * Ran Yarn format to fix all linting issues * Formating Change and Fixing Conflicts * Upgraded yarn version on TRAVIS * Attempt to remove cache yarn * Clean up formatting (again) * Fix rebase error * Used blueprintJS for Color instead of hex code * Modified constant to original value * Reenabled sorting for columns && refactored history to show popup over the grade/exp itself * Update formatting - prettify * Fix bug in filter * Fix CSV export - show all required details * update formatting * Centralise and vertically align content in grading table * Fixed formatting for scss file * Fix bug in grade display * Centralised filter input * Add XP adjustment input to Grading Workspace (#335) * Add parsing for new grading schema * Add xp props to GradingQuestion * Fix props in GradingWorkspace * Update mock data * Update mock data props * Add props to saga * Fix rebase errors * Reorder props in type * Add xp adjustment to action * Rename dispatch function arguments * Modify adjustment-related props * Add xp OwnProps Renamed a few usage of the props as well * Add xp to validation and submit dispatch call * Rename more props usage * Duplicate some columns for XP Most importantly, the numeric input * Remove comment * Pass in xp props to GradingEditor * Add Final XP column * Add xp and grade adjustment to mock backend * Remove old TODO comment * Use correct arguments for backend grading submit * Use correct arguments for postGrading * Format files * Fix runtime errors - input error when min === max - error message - hasUnsavedChanges returning true because of xp checking against grade * Format file * Revert travis to master's version * Show programming solution (#336) * Add additional types for solution prop * Udpate mock data * Use markdown to show solution * Pass solution to GradingEditor * Use code tag for solution instead * Add parsing of solution in saga * Add failsafes for solution value and the maxGrade value as well. This is something that only happens in staging, but could very well happen in prod as well. * Use pre for solution And fix a bug with xpAdjustment * Add word wrapping to solution * Fix wrapping for pre tag It was not speicific to the pre tag * Format files
1 parent 49ea383 commit f950cbd

File tree

22 files changed

+19412
-206
lines changed

22 files changed

+19412
-206
lines changed

package-lock.json

Lines changed: 18597 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/actions/interpreter.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { SourceError, Value } from 'js-slang/dist/types'
33
import * as actionTypes from './actionTypes'
44
import { WorkspaceLocation } from './workspaces'
55

6-
// TODO fix this immediately after location
7-
// is implemented completely
86
export const handleConsoleLog = (logString: string, workspaceLocation: WorkspaceLocation) => ({
97
type: actionTypes.HANDLE_CONSOLE_LOG,
108
payload: { logString, workspaceLocation }

src/actions/session.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,16 @@ export const submitGrading: ActionCreator<actionTypes.IAction> = (
7676
submissionId: number,
7777
questionId: number,
7878
comment: string,
79-
adjustment: number = 0
79+
gradeAdjustment: number = 0,
80+
xpAdjustment: number = 0
8081
) => ({
8182
type: actionTypes.SUBMIT_GRADING,
8283
payload: {
8384
submissionId,
8485
questionId,
8586
comment,
86-
adjustment
87+
gradeAdjustment,
88+
xpAdjustment
8789
}
8890
})
8991

src/components/Login.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ const Login: React.SFC<LoginProps> = props => {
3232
<Card className="login-card pt-elevation-4">
3333
<div className="login-header">
3434
<h4>
35-
<Icon icon={IconNames.LOCK} />LOGIN
35+
<Icon icon={IconNames.LOCK} />
36+
LOGIN
3637
</h4>
3738
</div>
3839
<div className="login-body">

src/components/academy/grading/GradingEditor.tsx

Lines changed: 99 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,30 +16,41 @@ export type DispatchProps = {
1616
submissionId: number,
1717
questionId: number,
1818
comment: string,
19-
adjustment: number | undefined
19+
gradeAdjustment: number | undefined,
20+
xpAdjustment: number | undefined
2021
) => void
2122
}
2223

2324
export type OwnProps = {
24-
adjustment: number
2525
comment: string
26-
initialGrade: number
27-
maximumGrade: number
26+
solution: number | string | null
2827
questionId: number
2928
submissionId: number
29+
initialGrade: number
30+
gradeAdjustment: number
31+
maxGrade: number
32+
initialXp: number
33+
xpAdjustment: number
34+
maxXp: number
3035
}
3136

3237
/**
3338
* Keeps track of the current editor state,
3439
* as well as the grade adjustment in the numeric input.
3540
*
36-
* @prop adjustmentInput a potentially null string. this property being null
41+
* @prop gradeAdjustmentInput a potentially null string which defines the
42+
* result for the number grade input. This property being null
43+
* will show the hint text in the NumericInput. This property is a string
44+
* so as to allow input such as the '-' character.
45+
* @prop xpAdjustmentInput a potentially null string which defines the
46+
* result for the number grade input. This property being null
3747
* will show the hint text in the NumericInput. This property is a string
3848
* so as to allow input such as the '-' character.
3949
*/
4050
type State = {
4151
mdeState: ReactMdeTypes.MdeState
42-
adjustmentInput: string | null
52+
gradeAdjustmentInput: string | null
53+
xpAdjustmentInput: string | null
4354
}
4455

4556
class GradingEditor extends React.Component<GradingEditorProps, State> {
@@ -51,7 +62,8 @@ class GradingEditor extends React.Component<GradingEditorProps, State> {
5162
mdeState: {
5263
markdown: props.comment
5364
},
54-
adjustmentInput: props.adjustment.toString()
65+
gradeAdjustmentInput: props.gradeAdjustment.toString(),
66+
xpAdjustmentInput: props.xpAdjustment.toString()
5567
}
5668
/**
5769
* The markdown-to-html converter for the editor.
@@ -80,14 +92,25 @@ class GradingEditor extends React.Component<GradingEditorProps, State> {
8092
message={'You have changes that may not be saved. Are you sure you want to leave?'}
8193
/>
8294
) : null}
95+
{this.props.solution !== null ? (
96+
<div className="grading-editor-solution">
97+
<pre> {this.props.solution.toString()} </pre>
98+
</div>
99+
) : null}
83100
<div className="grading-editor-input-parent">
84101
<table>
85102
<tbody>
86103
<tr>
87104
<th> {`Auto-grader's grade:`} </th>
88105
<td>
89106
<Text>
90-
{this.props.initialGrade} / {this.props.maximumGrade}
107+
{this.props.initialGrade} / {this.props.maxGrade}
108+
</Text>
109+
</td>
110+
<th> {`Auto-grader's XP:`} </th>
111+
<td>
112+
<Text>
113+
{this.props.initialXp} / {this.props.maxXp}
91114
</Text>
92115
</td>
93116
</tr>
@@ -96,13 +119,34 @@ class GradingEditor extends React.Component<GradingEditorProps, State> {
96119
<td>
97120
<NumericInput
98121
className="grading-adjustment-input"
99-
onValueChange={this.onAdjustmentInputChange}
100-
value={this.state.adjustmentInput || ''}
122+
onValueChange={this.onGradeAdjustmentInputChange}
123+
value={this.state.gradeAdjustmentInput || ''}
101124
buttonPosition={Position.RIGHT}
102125
fill={true}
103126
placeholder="Adjust grades relatively here"
104127
min={0 - this.props.initialGrade}
105-
max={this.props.maximumGrade - this.props.initialGrade}
128+
max={
129+
this.props.maxGrade > this.props.initialGrade
130+
? this.props.maxGrade - this.props.initialGrade
131+
: undefined
132+
}
133+
/>
134+
</td>
135+
<th> {`Your adjustment:`} </th>
136+
<td>
137+
<NumericInput
138+
className="grading-adjustment-input"
139+
onValueChange={this.onXpAdjustmentInputChange}
140+
value={this.state.xpAdjustmentInput || ''}
141+
buttonPosition={Position.RIGHT}
142+
fill={true}
143+
placeholder="Adjust XP relatively here"
144+
min={0 - this.props.initialXp}
145+
max={
146+
this.props.maxXp > this.props.initialXp
147+
? this.props.maxXp - this.props.initialXp
148+
: undefined
149+
}
106150
/>
107151
</td>
108152
</tr>
@@ -111,8 +155,16 @@ class GradingEditor extends React.Component<GradingEditorProps, State> {
111155
<td>
112156
<Text>
113157
{this.props.initialGrade +
114-
(stringParamToInt(this.state.adjustmentInput || undefined) || 0)}{' '}
115-
/ {this.props.maximumGrade}
158+
(stringParamToInt(this.state.gradeAdjustmentInput || undefined) || 0)}{' '}
159+
/ {this.props.maxGrade}
160+
</Text>
161+
</td>
162+
<th> {`Final XP:`} </th>
163+
<td>
164+
<Text>
165+
{this.props.initialXp +
166+
(stringParamToInt(this.state.xpAdjustmentInput || undefined) || 0)}{' '}
167+
/ {this.props.maxXp}
116168
</Text>
117169
</td>
118170
</tr>
@@ -146,32 +198,54 @@ class GradingEditor extends React.Component<GradingEditorProps, State> {
146198
}
147199

148200
private onClickSaveButton = () => {
149-
const adjustmentInput = stringParamToInt(this.state.adjustmentInput || undefined) || undefined
150-
const grade = this.props.initialGrade + (adjustmentInput || 0)
151-
if (grade < 0 || grade > this.props.maximumGrade) {
201+
const gradeAdjustmentInput =
202+
stringParamToInt(this.state.gradeAdjustmentInput || undefined) || undefined
203+
const grade = this.props.initialGrade + (gradeAdjustmentInput || 0)
204+
const xpAdjustmentInput =
205+
stringParamToInt(this.state.xpAdjustmentInput || undefined) || undefined
206+
const xp = this.props.initialXp + (xpAdjustmentInput || 0)
207+
if (grade < 0 || grade > this.props.maxGrade) {
152208
showWarningMessage(
153-
`Grade ${grade.toString()} is out of bounds. Maximum grade is ${this.props.maximumGrade.toString()}.`
209+
`Grade ${grade.toString()} is out of bounds. Maximum grade is ${this.props.maxGrade.toString()}.`
210+
)
211+
} else if (xp < 0 || xp > this.props.maxXp) {
212+
showWarningMessage(
213+
`XP ${xp.toString()} is out of bounds. Maximum xp is ${this.props.maxXp.toString()}.`
154214
)
155215
} else {
156216
this.props.handleGradingSave(
157217
this.props.submissionId,
158218
this.props.questionId,
159219
this.state.mdeState.markdown!,
160-
adjustmentInput
220+
gradeAdjustmentInput,
221+
xpAdjustmentInput
161222
)
162223
}
163224
}
164225

165226
/**
166-
* Handles changes in the NumericInput, and updates the local State.
227+
* Handles changes in the grade NumericInput, and updates the local State.
228+
*
229+
* @param valueAsNumber an unused parameter, as we use strings for the input. @see State
230+
* @param valueAsString a string that contains the input. To be parsed by another function.
231+
*/
232+
private onGradeAdjustmentInputChange = (valueAsNumber: number, valueAsString: string | null) => {
233+
this.setState({
234+
...this.state,
235+
gradeAdjustmentInput: valueAsString
236+
})
237+
}
238+
239+
/**
240+
* Handles changes in the XP NumericInput, and updates the local State.
167241
*
168242
* @param valueAsNumber an unused parameter, as we use strings for the input. @see State
169243
* @param valueAsString a string that contains the input. To be parsed by another function.
170244
*/
171-
private onAdjustmentInputChange = (valueAsNumber: number, valueAsString: string | null) => {
245+
private onXpAdjustmentInputChange = (valueAsNumber: number, valueAsString: string | null) => {
172246
this.setState({
173247
...this.state,
174-
adjustmentInput: valueAsString
248+
xpAdjustmentInput: valueAsString
175249
})
176250
}
177251

@@ -183,10 +257,12 @@ class GradingEditor extends React.Component<GradingEditorProps, State> {
183257
}
184258

185259
private hasUnsavedChanges = () => {
186-
const adjustmentInput = stringParamToInt(this.state.adjustmentInput || undefined)
260+
const gradeAdjustmentInput = stringParamToInt(this.state.gradeAdjustmentInput || undefined)
261+
const xpAdjustmentInput = stringParamToInt(this.state.xpAdjustmentInput || undefined)
187262
return (
188263
this.props.comment !== this.state.mdeState.markdown ||
189-
this.props.adjustment !== adjustmentInput
264+
this.props.gradeAdjustment !== gradeAdjustmentInput ||
265+
this.props.xpAdjustment !== xpAdjustmentInput
190266
)
191267
}
192268

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { Popover, PopoverInteractionKind, Position } from '@blueprintjs/core'
2+
3+
import * as React from 'react'
4+
5+
import { GradingOverview } from './gradingShape'
6+
7+
type GradingHistoryProps = {
8+
data: GradingOverview
9+
exp: boolean
10+
grade: boolean
11+
}
12+
13+
/**
14+
* Used to render the marking history in the table that displays GradingOverviews.
15+
* This is a fully fledged component (not SFC) by specification in
16+
* ag-grid.
17+
*
18+
* See {@link https://www.ag-grid.com/example-react-dynamic}
19+
*/
20+
class GradingHistory extends React.Component<GradingHistoryProps, {}> {
21+
constructor(props: GradingHistoryProps) {
22+
super(props)
23+
}
24+
25+
public render() {
26+
const popoverInfo = () => (
27+
<div className="col-xs-12" style={{ padding: 10 }}>
28+
{(this.props.grade && (
29+
<div>
30+
<p>Initial Grade: {this.props.data.initialGrade}</p>
31+
<p>Grade Adjustments: {this.props.data.gradeAdjustment}</p>
32+
</div>
33+
)) ||
34+
(this.props.exp && (
35+
<div>
36+
<p>Initial XP: {this.props.data.initialXp}</p>
37+
<p>XP Adjustments: {this.props.data.xpAdjustment}</p>
38+
</div>
39+
))}
40+
</div>
41+
)
42+
43+
/** Component to render in table - marks */
44+
const GradingMarks = () => {
45+
if (this.props.data.maxGrade !== 0) {
46+
return (
47+
<div>
48+
{`${this.props.data.currentGrade}`} / {`${this.props.data.maxGrade}`}
49+
</div>
50+
)
51+
} else {
52+
return <div>N/A</div>
53+
}
54+
}
55+
56+
/** Component to render in table - XP */
57+
const GradingExp = () => {
58+
if (this.props.data.currentXp && this.props.data.maxXp) {
59+
return (
60+
<div>
61+
{`${this.props.data.currentXp}`} / {`${this.props.data.maxXp}`}
62+
</div>
63+
)
64+
} else {
65+
return <div>No Exp</div>
66+
}
67+
}
68+
69+
return (
70+
<Popover
71+
content={popoverInfo()}
72+
position={Position.LEFT}
73+
interactionKind={PopoverInteractionKind.HOVER}
74+
>
75+
{(this.props.exp && <GradingExp />) || (this.props.grade && <GradingMarks />)}
76+
</Popover>
77+
)
78+
}
79+
}
80+
81+
export default GradingHistory

src/components/academy/grading/GradingNavLink.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
import { Icon } from '@blueprintjs/core'
2+
import { IconNames } from '@blueprintjs/icons'
13
import * as React from 'react'
4+
25
import { NavLink } from 'react-router-dom'
36

47
import { GradingOverview } from './gradingShape'
@@ -21,8 +24,12 @@ class GradingNavLink extends React.Component<GradingNavLinkProps, {}> {
2124

2225
public render() {
2326
return (
24-
<NavLink to={`/academy/grading/${this.props.data.submissionId}`} activeClassName="pt-active">
25-
{'Add comments'}
27+
<NavLink
28+
to={`/academy/grading/${this.props.data.submissionId}`}
29+
activeClassName="pt-active"
30+
target="_blank"
31+
>
32+
<Icon className="grade-edit-icon" iconSize={16} icon={IconNames.ANNOTATION} />
2633
</NavLink>
2734
)
2835
}

src/components/academy/grading/GradingWorkspace.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,12 +184,16 @@ class GradingWorkspace extends React.Component<GradingWorkspaceProps> {
184184
/* Render an editor with the xp given to the current question. */
185185
body: (
186186
<GradingEditor
187-
adjustment={props.grading![questionId].grade.adjustment}
188187
comment={props.grading![questionId].grade.comment}
189-
initialGrade={props.grading![questionId].grade.grade}
190-
maximumGrade={props.grading![questionId].maximumGrade}
188+
solution={props.grading![questionId].question.solution}
191189
questionId={props.grading![questionId].question.id}
192190
submissionId={props.submissionId}
191+
initialGrade={props.grading![questionId].grade.grade}
192+
gradeAdjustment={props.grading![questionId].grade.gradeAdjustment}
193+
maxGrade={props.grading![questionId].maxGrade}
194+
initialXp={props.grading![questionId].grade.xp}
195+
xpAdjustment={props.grading![questionId].grade.xpAdjustment}
196+
maxXp={props.grading![questionId].maxXp}
193197
/>
194198
)
195199
},

0 commit comments

Comments
 (0)