Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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: 3 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
language: node_js
node_js:
- 9
cache: yarn
before_install:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you describe the issue you had with travis? It's not that obvious to me from seeing this. (I don't mean to document it just yet, you can reply here 😃 )

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 test suite - Assessments actually fails on Travis (some issue with the snapshot I believe)

On my local system, I managed to resolve this by upgrading Yarn to version 1.9. I tried the same with Travis but it didnt seem to work either.

Should we open an issue for this?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test probably failed because there was a changed made to the component. I'll update the tests

- curl -o- -L https://yarnpkg.com/install.sh | bash
- export PATH="$HOME/.yarn/bin:$PATH"
branches:
except:
- /^no-ci.*$/
Expand Down
18,597 changes: 18,597 additions & 0 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion src/components/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ const Login: React.SFC<LoginProps> = props => {
<Card className="login-card pt-elevation-4">
<div className="login-header">
<h4>
<Icon icon={IconNames.LOCK} />LOGIN
<Icon icon={IconNames.LOCK} />
LOGIN
</h4>
</div>
<div className="login-body">
Expand Down
81 changes: 81 additions & 0 deletions src/components/academy/grading/GradingHistory.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { Popover, PopoverInteractionKind, Position } from '@blueprintjs/core'

import * as React from 'react'

import { GradingOverview } from './gradingShape'

type GradingHistoryProps = {
data: GradingOverview
exp: boolean
grade: boolean
}

/**
* Used to render the marking history in the table that displays GradingOverviews.
* This is a fully fledged component (not SFC) by specification in
* ag-grid.
*
* See {@link https://www.ag-grid.com/example-react-dynamic}
*/
class GradingHistory extends React.Component<GradingHistoryProps, {}> {
constructor(props: GradingHistoryProps) {
super(props)
}

public render() {
const popoverInfo = () => (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't need to be a function, you can store the element as a variable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have refactored this element based off the other suggestions below!

<div className="col-xs-12" style={{ padding: 10 }}>
{(this.props.grade && (
<div>
<p>Initial Grade: {this.props.data.initialGrade}</p>
<p>Grade Adjustments: {this.props.data.gradeAdjustment}</p>
</div>
)) ||
(this.props.exp && (
<div>
<p>Initial XP: {this.props.data.initialXp}</p>
<p>XP Adjustments: {this.props.data.xpAdjustment}</p>
</div>
))}
</div>
)

/** Component to render in table - marks */
const GradingMarks = () => {
if (this.props.data.maxGrade !== 0) {
return (
<div>
{`${this.props.data.currentGrade}`} / {`${this.props.data.maxGrade}`}
</div>
)
} else {
return <div>N/A</div>
}
}

/** Component to render in table - XP */
const GradingExp = () => {
if (this.props.data.currentXp && this.props.data.maxXp) {
return (
<div>
{`${this.props.data.currentXp}`} / {`${this.props.data.maxXp}`}
</div>
)
} else {
return <div>No Exp</div>
}
}

return (
<Popover
content={popoverInfo()}
position={Position.LEFT}
interactionKind={PopoverInteractionKind.HOVER}
>
{(this.props.exp && <GradingExp />) || (this.props.grade && <GradingMarks />)}
</Popover>
)
}
}

export default GradingHistory
11 changes: 9 additions & 2 deletions src/components/academy/grading/GradingNavLink.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { Icon } from '@blueprintjs/core'
import { IconNames } from '@blueprintjs/icons'
import * as React from 'react'

import { NavLink } from 'react-router-dom'

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

public render() {
return (
<NavLink to={`/academy/grading/${this.props.data.submissionId}`} activeClassName="pt-active">
{'Edit grading'}
<NavLink
to={`/academy/grading/${this.props.data.submissionId}`}
activeClassName="pt-active"
target="_blank"
>
<Icon className="grade-edit-icon" iconSize={16} icon={IconNames.ANNOTATION} />
</NavLink>
)
}
Expand Down
127 changes: 94 additions & 33 deletions src/components/academy/grading/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { NonIdealState, Spinner } from '@blueprintjs/core'
import { Colors, InputGroup, NonIdealState, Spinner } from '@blueprintjs/core'
import { IconNames } from '@blueprintjs/icons'
import { ColDef, ColumnApi, GridApi, GridReadyEvent } from 'ag-grid'
import { ColDef, GridApi, GridReadyEvent } from 'ag-grid'
import { AgGridReact } from 'ag-grid-react'
import 'ag-grid/dist/styles/ag-grid.css'
import 'ag-grid/dist/styles/ag-theme-balham.css'
Expand All @@ -12,6 +12,7 @@ import GradingWorkspaceContainer from '../../../containers/academy/grading/Gradi
import { stringParamToInt } from '../../../utils/paramParseHelpers'
import { controlButton } from '../../commons'
import ContentDisplay from '../../commons/ContentDisplay'
import GradingHistory from './GradingHistory'
import GradingNavLink from './GradingNavLink'
import { GradingOverview } from './gradingShape'
import { OwnProps as GradingWorkspaceProps } from './GradingWorkspace'
Expand All @@ -22,6 +23,11 @@ import { OwnProps as GradingWorkspaceProps } from './GradingWorkspace'
*/
type State = {
columnDefs: ColDef[]
filterValue: string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we stick to the original method of filtering? IMO it was much more versatile, as we could filter out individual columns:

image

Copy link
Contributor Author

@rrtheonlyone rrtheonlyone Aug 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a few reasons I went for this change:

  1. Even if it was more versatile, from a UI point of view it was not user-friendly e.g. need to choose from dropdown + input text and it wasn't obvious either (I wasnt even aware of this feature till I saw the source code)
  2. It actually doesn't look good either especially with the new theme I have used.

Could I propose having both methods of filtering available? I can re-enable this filter again. So the graders can go with whatever method is convenient for them!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update: re-enabled the filtering as well!

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, that sounds good

}

type GradingNavLinkProps = {
data: GradingOverview
}

interface IGradingProps
Expand All @@ -42,33 +48,63 @@ export interface IStateProps {
gradingOverviews?: GradingOverview[]
}

/** Component to render in table - marks */
const GradingMarks = (props: GradingNavLinkProps) => {
return <GradingHistory data={props.data} exp={false} grade={true} />
}

/** Component to render in table - XP */
const GradingExp = (props: GradingNavLinkProps) => {
return <GradingHistory data={props.data} exp={true} grade={false} />
}

class Grading extends React.Component<IGradingProps, State> {
private gridApi?: GridApi
private columnApi?: ColumnApi

public constructor(props: IGradingProps) {
super(props)

this.state = {
columnDefs: [
{ headerName: 'Assessment ID', field: 'assessmentId' },
{ headerName: 'Assessment Name', field: 'assessmentName' },
{ headerName: 'Assessment Category', field: 'assessmentCategory' },
{ headerName: 'Category', field: 'assessmentCategory', maxWidth: 150 },
{ headerName: 'Student Name', field: 'studentName' },
{ headerName: 'Auograder grade', field: 'initialGrade' },
{ headerName: 'Grade adjustment', field: 'gradeAdjustment' },
{ headerName: 'Current Grade', field: 'currentGrade' },
{ headerName: 'Maximum Grade', field: 'maxGrade' },
{ headerName: 'XP', field: 'initialXp' },
{ headerName: 'XP adjustment', field: 'xpAdjustment' },
{ headerName: 'Current XP', field: 'currentXp' },
{ headerName: 'Maximum XP', field: 'maxXp' },
{
headerName: 'Grade',
field: '',
cellRendererFramework: GradingMarks,
maxWidth: 100,
cellStyle: params => {
if (params.data.currentGrade < params.data.maxGrade) {
return { backgroundColor: Colors.RED5 }
} else {
return {}
}
}
},
{
headerName: 'XP',
field: '',
cellRendererFramework: GradingExp,
maxWidth: 100
},
{
headerName: 'Edit',
field: '',
cellRendererFramework: GradingNavLink
}
]
cellRendererFramework: GradingNavLink,
maxWidth: 70
},
{ headerName: 'Initial Grade', field: 'initialGrade', hide: true },
{ headerName: 'Grade Adjustment', field: 'gradeAdjustment', hide: true },
{ headerName: 'Initial XP', field: 'initialXp', hide: true },
{ headerName: 'XP Adjustment', field: 'xpAdjustment', hide: true },
{ headerName: 'Current Grade', field: 'currentGrade', hide: true },
{ headerName: 'Max Grade', field: 'maxGrade', hide: true },
{ headerName: 'Current XP', field: 'currentXp', hide: true },
{ headerName: 'Max XP', field: 'maxXp', hide: true }
],

filterValue: ''
}
}

Expand Down Expand Up @@ -98,44 +134,69 @@ class Grading extends React.Component<IGradingProps, State> {
(a: GradingOverview) => -a.assessmentId,
(a: GradingOverview) => -a.submissionId
])

const grid = (
<div className="Grading">
<div className="ag-grid-parent ag-theme-balham">
<AgGridReact
gridAutoHeight={true}
enableColResize={true}
enableSorting={true}
enableFilter={true}
columnDefs={this.state.columnDefs}
onGridReady={this.onGridReady}
rowData={data}
/>
<div className="GradingContainer">
<div>
<div className="col-md-6 col-md-offset-3">
<InputGroup
large={false}
leftIcon="filter"
placeholder="Filter..."
value={this.state.filterValue}
onChange={this.handleFilterChange}
/>
</div>
</div>
<div className="ag-grid-export-button">
{controlButton('Export to CSV', IconNames.EXPORT, this.exportCSV)}

<br />

<div className="Grading">
<div className="ag-grid-parent ag-theme-fresh">
<AgGridReact
gridAutoHeight={true}
enableColResize={true}
enableSorting={true}
enableFilter={true}
columnDefs={this.state.columnDefs}
onGridReady={this.onGridReady}
rowData={data}
/>
</div>
<div className="ag-grid-export-button">
{controlButton('Export to CSV', IconNames.EXPORT, this.exportCSV)}
</div>
</div>
</div>
)
return (
<ContentDisplay
loadContentDispatch={this.props.handleFetchGradingOverviews}
display={this.props.gradingOverviews === undefined ? loadingDisplay : grid}
fullWidth={true}
fullWidth={false}
/>
)
}

private handleFilterChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const changeVal = event.target.value
this.setState({ filterValue: changeVal })

if (this.gridApi) {
this.gridApi.setQuickFilter(changeVal)
}
}

private onGridReady = (params: GridReadyEvent) => {
this.gridApi = params.api
this.columnApi = params.columnApi
this.columnApi.autoSizeAllColumns()
this.gridApi.sizeColumnsToFit()
}

private exportCSV = () => {
if (this.gridApi === undefined) {
return
}
this.gridApi.exportDataAsCsv()
this.gridApi.exportDataAsCsv({ allColumns: true })
}
}

Expand Down
7 changes: 6 additions & 1 deletion src/components/assessment/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,12 @@ class Assessment extends React.Component<IAssessmentProps, State> {
<p>
You are about to finalise your submission for the{' '}
{this.state.betchaAssessment.category.toLowerCase()}{' '}
<i>&quot;{this.state.betchaAssessment.title}&quot;</i>.
<i>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a formatter issue?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yup. I believe yarn format changed it as such

&quot;
{this.state.betchaAssessment.title}
&quot;
</i>
.
</p>
<p>
Early submissions grant you additional XP, but{' '}
Expand Down
3 changes: 0 additions & 3 deletions src/sagas/backend.ts
Original file line number Diff line number Diff line change
Expand Up @@ -409,9 +409,6 @@ async function getGradingOverviews(tokens: Tokens): Promise<GradingOverview[] |
assessmentId: overview.assessment.id,
assessmentName: overview.assessment.title,
assessmentCategory: capitalise(overview.assessment.type) as AssessmentCategory,
initialGrade: overview.grade,
currentGrade: overview.grade + (overview.adjustment || 0),
maximumGrade: overview.assessment.maxGrade,
studentId: overview.student.id,
studentName: overview.student.name,
submissionId: overview.id,
Expand Down
12 changes: 12 additions & 0 deletions src/styles/_academy.scss
Original file line number Diff line number Diff line change
Expand Up @@ -194,3 +194,15 @@
margin-top: 20px;
}
}

.ag-header-cell-label {
justify-content: center;
font-weight: bold;
padding-left: 15px;
}

.ag-cell {
padding-top: 4px;
justify-content: center;
text-align: center;
}