Skip to content

Commit 7361855

Browse files
fourcolorsclaydiffrient
authored andcommitted
add list of students to moderated grade page
refs CNVS-22742 Test Plan Given you have an assignment with moderated grading enabled And you are moderating it You should see a list of students that have submitted an assignment And the first mark should show up in the list if a mark has been given If no first mark has been given filler text "Speed Grader" should appear Change-Id: I12fc1985ac72e485c0724f673913ba00b29ef8c5 Reviewed-on: https://gerrit.instructure.com/61933 Tested-by: Jenkins Reviewed-by: Clay Diffrient <cdiffrient@instructure.com> QA-Review: Jahnavi Yetukuri <jyetukuri@instructure.com> Product-Review: Clay Diffrient <cdiffrient@instructure.com>
1 parent c2dbe29 commit 7361855

12 files changed

Lines changed: 257 additions & 21 deletions

File tree

app/coffeescripts/bundles/assignment_moderation.coffee

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,4 @@ require [
44
'jsx/assignments/ModerationApp'
55
], ($, React, ModerationApp) ->
66

7-
ModerationApp = React.createFactory ModerationApp
8-
React.render(React.createElement(ModerationApp), $('#assignment_moderation')[0])
7+
React.render(ModerationApp(student_submissions_url: ENV.URLS.student_submissions_url), $('#assignment_moderation')[0])

app/controllers/assignments_controller.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,11 @@ def show_moderate
181181
add_crumb(@assignment.title, polymorphic_url([@context, @assignment]))
182182
add_crumb(t('Moderate'))
183183

184+
js_env({
185+
:URLS => {
186+
:student_submissions_url => polymorphic_url([:api_v1, @context, @assignment, :submissions]) + "?include[]=user_summary&include[]=provisional_grades"
187+
}})
188+
polymorphic_url([@context, @assignment])
184189
respond_to do |format|
185190
format.html { render }
186191
end
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/** @jsx React.DOM */
2+
3+
define([
4+
'react'
5+
], function (React) {
6+
7+
var MARK_ONE = 0;
8+
var MARK_TWO = 1;
9+
var MARK_THREE = 2;
10+
11+
return React.createClass({
12+
getInitialState () {
13+
return (
14+
{submissions: []}
15+
);
16+
},
17+
componentDidMount () {
18+
this.props.store.addChangeListener(this.handleStoreChange);
19+
},
20+
handleStoreChange () {
21+
this.setState({submissions: this.props.store.submissions});
22+
},
23+
displayName: 'ModeratedStudentList',
24+
selectCheckbox (submission, event) {
25+
// this.actions.updateSubmission(submission);
26+
},
27+
renderSubmissionMark (submission, mark_number) {
28+
if(submission.provisional_grades[mark_number]){
29+
return(
30+
<div className='AssignmentList__Mark'>
31+
<input type='radio' name={"mark_" + submission.id} />
32+
<span>{submission.provisional_grades[mark_number].score}</span>
33+
</div>
34+
);
35+
}else{
36+
return(
37+
<div className='AssignmentList__Mark'>
38+
<span>Speed Grader</span>
39+
</div>
40+
);
41+
}
42+
},
43+
renderFinalGrade (submission) {
44+
if (submission.grade){
45+
return(
46+
<span className='AssignmentList_Grade'>
47+
{submission.score}
48+
</span>
49+
);
50+
}else{
51+
return(
52+
<span className='AssignmentList_Grade'>
53+
-
54+
</span>
55+
);
56+
}
57+
},
58+
render () {
59+
return(
60+
<ul className='AssignmentList'>
61+
{
62+
this.state.submissions.map(function(submission) {
63+
return(
64+
<li className='AssignmentList__Item'>
65+
<div className='AssignmentList__StudentInfo'>
66+
<input checked={submission.isSelected} type="checkbox" onChange={this.selectCheckbox.bind(null, submission)} />
67+
<img className='img-circle AssignmentList_StudentPhoto' src={submission.user.avatar_image_url} />
68+
<span>{submission.user.display_name}</span>
69+
</div>
70+
{this.renderSubmissionMark(submission, MARK_ONE)}
71+
{this.renderSubmissionMark(submission, MARK_TWO)}
72+
{this.renderSubmissionMark(submission, MARK_THREE)}
73+
{this.renderFinalGrade(submission)}
74+
</li>
75+
);
76+
}.bind(this))
77+
}
78+
</ul>
79+
);
80+
}
81+
});
82+
83+
});

app/jsx/assignments/ModerationApp.jsx

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,25 @@
11
/** @jsx React.DOM */
22

33
define([
4-
'react'
5-
], function (React) {
4+
'react',
5+
'./ModeratedStudentList',
6+
'./stores/ModerationStore',
7+
'./actions/ModerationActions'
8+
], function (React, ModeratedStudentList, Store, Actions) {
69

710
return React.createClass({
811
displayName: 'ModerationApp',
9-
12+
componentDidMount () {
13+
this.actions.loadInitialSubmissions(this.props.student_submissions_url);
14+
},
15+
componentWillMount () {
16+
this.store = new Store();
17+
this.actions = new Actions(this.store);
18+
},
1019
render () {
11-
return <div>ModerationApp Placeholder</div>;
20+
return (
21+
<ModeratedStudentList actions={this.actions} store={this.store} />
22+
);
1223
}
1324
});
1425

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/** @jsx React.DOM */
2+
3+
define(['axios'], function(axios){
4+
class ModerationActions {
5+
constructor (store) {
6+
this.store = store;
7+
}
8+
9+
updateSubmission (submission) {
10+
// Update the submission and then update the store
11+
}
12+
13+
loadInitialSubmissions (submissions_url) {
14+
axios.get(submissions_url)
15+
.then(function(response){
16+
this.store.addSubmissions(response.data);
17+
}.bind(this))
18+
.catch(function(response){
19+
console.log('finished');
20+
})
21+
}
22+
}
23+
24+
return ModerationActions;
25+
});

app/jsx/assignments/stores/ModerationStore.jsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ define(['underscore', 'Backbone'], function(_){
55
class ModerationStore {
66
constructor () {
77
this.events = _.extend({}, Backbone.Events);
8-
this.students = [];
8+
this.submissions = [];
99
}
1010

1111
/**
@@ -25,12 +25,12 @@ define(['underscore', 'Backbone'], function(_){
2525
}
2626

2727
/**
28-
* Add students to the store
28+
* Add submissions to the store
2929
*
30-
* @param {Array} students An array of student objects
30+
* @param {Array} submission An array of submission objects
3131
*/
32-
addStudents (students) {
33-
this.students = this._mergeArraysById(this.students, students);
32+
addSubmissions (submissions) {
33+
this.submissions = this._mergeArraysById(this.submissions, submissions);
3434
this.events.trigger('change');
3535
}
3636

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
@import "base/environment";
2+
3+
.assignments{
4+
5+
.AssignmentList{
6+
border-top: 1px solid #F0F0F0;
7+
margin: 0px;
8+
list-style: none;
9+
}
10+
11+
.AssignmentList__Item{
12+
border-bottom: 1px solid #F0F0F0;
13+
align-items: center;
14+
display: flex;
15+
justify-content: space-between;
16+
padding-top: 15px;
17+
padding-bottom: 15px;
18+
}
19+
20+
.AssignmentList__StudentInfo{
21+
display: flex;
22+
align-items: center;
23+
width: 200px;
24+
}
25+
26+
.AssignmentList__StudentInfo input[type='checkbox']{
27+
margin: 0px;
28+
}
29+
30+
.AssignmentList_StudentPhoto{
31+
margin-left: 10px;
32+
margin-right: 10px;
33+
width: 20px;
34+
}
35+
36+
.AssignmentList__Mark{
37+
display: flex;
38+
align-items: center;
39+
}
40+
.AssignmentList__Mark input[type='radio']{
41+
margin: 0px 5px 0px 5px;
42+
}
43+
.AssignmentList_Grade{
44+
width: 50px;
45+
display: flex;
46+
justify-content: center;
47+
}
48+
}

app/views/assignments/show_moderate.html.erb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
content_for :page_title, @assignment.title
33
content_for :right_side, render(:partial => "assignment_sidebar")
44

5+
css_bundle :assignment_moderation
56
js_bundle :assignment_moderation
67
%>
78

8-
<div id="assignment_moderation"></div>
9+
<div id="assignment_moderation"></div>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
define [
2+
'react'
3+
'jsx/assignments/ModeratedStudentList'
4+
'jsx/assignments/stores/ModerationStore'
5+
'jsx/assignments/actions/ModerationActions'
6+
], (React, ModeratedStudentList, Store, Actions) ->
7+
8+
TestUtils = React.addons.TestUtils
9+
10+
module 'ModeratedStudentList',
11+
test "renders mark 1", ->
12+
store = new Store()
13+
score = 10
14+
moderatedStudentList = TestUtils.renderIntoDocument(ModeratedStudentList(store: store))
15+
store.addSubmissions([{id: 1, user: {display_name: 'steve'}, provisional_grades: [{score:score}]}])
16+
firstMark = TestUtils.scryRenderedDOMComponentsWithClass(moderatedStudentList, 'AssignmentList__Mark')[0].getDOMNode().textContent
17+
equal firstMark, score, "renders the first mark"
18+
React.unmountComponentAtNode(moderatedStudentList.getDOMNode().parentNode)
19+
20+
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
define [
2+
"jsx/assignments/actions/ModerationActions"
3+
], (ModerationActions) ->
4+
module "ModerationActions",
5+
test "sets this.store in the constructor", ->
6+
some_store = {data: true}
7+
actions = new ModerationActions(some_store)
8+
ok actions.store.data, "sets the store to true"
9+
10+
#module "ModerationActions#loadInitalSubmissions",
11+
#setup: ->
12+
#@server = sinon.fakeServer.create()
13+
#teardown: ->
14+
#@server.restore()
15+
16+
#test "fetches data from the submissions_url and sets store.addSubmissions with it", ->
17+
#the_data = {}
18+
#actions = new ModerationActions(some_store)
19+
#submission_url = "/something"
20+
#actions.loadInitialSubmissions(submission_url)
21+
#expected = {some_thing: 'here'}
22+
#@server.respond 'get', submission_url, [
23+
#200
24+
#'Content-Type': 'application/json'
25+
#JSON.stringify expected
26+
#]
27+
28+
29+
#equal the_data, expected, "gets data from the url"

0 commit comments

Comments
 (0)