Ember Quiz Stats - File Upload & Formula

Render question statistics for those question types similar to Essay.
File Upload should include a link to download all submissions too.

QuizSerializer was updated to include the submission ZIP download URL.

Closes CNVS-12988

TEST PLAN
---- ----

  - using your data set from https://gerrit.instructure.com/#/c/35112/
    - verify that the score chart renders with the student scores
    - verify that you get a link to Download All Files for FUpload
      questions
    - verify that the "Attempts: x out of Y" reads correctly based on
      the "responses" field and the total participant count,
      respectively
    - tooltips and chart interactivity like that for Essay

Change-Id: I4a77631491b169106e2eb677b21c1f30f3ca9440
Reviewed-on: https://gerrit.instructure.com/35113
Tested-by: Jenkins <jenkins@instructure.com>
Reviewed-by: Jason Madsen <jmadsen@instructure.com>
QA-Review: Caleb Guanzon <cguanzon@instructure.com>
Product-Review: Ahmad Amireh <ahmad@instructure.com>
This commit is contained in:
Ahmad Amireh 2014-05-19 11:20:50 +03:00
parent ddcffe00ef
commit 076e541cdb
18 changed files with 98 additions and 22 deletions

View File

@ -0,0 +1,2 @@
define [ './essay_controller' ], (Base) ->
Base

View File

@ -0,0 +1,2 @@
define [ './essay_controller' ], (Base) ->
Base

View File

@ -31,7 +31,8 @@ define [
correctMiddleStudentCount: attr() correctMiddleStudentCount: attr()
correctBottomStudentCount: attr() correctBottomStudentCount: attr()
speedGraderUrl: Em.computed.alias('quizStatistics.quiz.speedGraderUrl').readOnly() speedGraderUrl: alias('quizStatistics.quiz.speedGraderUrl').readOnly()
quizSubmissionsZipUrl: alias('quizStatistics.quiz.quizSubmissionsZipUrl').readOnly()
# Helper for calculating the ratio of correct responses for this question. # Helper for calculating the ratio of correct responses for this question.
# #
@ -61,6 +62,10 @@ define [
'fill_in_multiple_blanks' 'fill_in_multiple_blanks'
when 'essay_question' when 'essay_question'
'essay' 'essay'
when 'file_upload_question'
'file_upload'
when 'calculated_question'
'calculated'
else else
'generic' 'generic'
).property('questionType') ).property('questionType')

View File

@ -124,6 +124,7 @@ define [
quizSubmissions: alias('studentQuizSubmissions') quizSubmissions: alias('studentQuizSubmissions')
takeable: attr() takeable: attr()
takeQuizUrl: attr() takeQuizUrl: attr()
quizSubmissionsZipUrl: attr()
Quiz.SORT_LAST = 'ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ' Quiz.SORT_LAST = 'ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ'

View File

@ -0,0 +1,7 @@
<header>
{{partial "quiz/statistics/questions/essay/header_contents"}}
</header>
<div>
{{partial "quiz/statistics/questions/essay/charts"}}
</div>

View File

@ -5,19 +5,9 @@
</a> </a>
</aside> </aside>
<span class="question-attempts">{{attemptsLabel}}</span> {{partial "quiz/statistics/questions/essay/header_contents"}}
<div class="question-text">
{{{questionText}}}
</div>
</header> </header>
<div> <div>
<section class="correct-answer-ratio-section"> {{partial "quiz/statistics/questions/essay/charts"}}
{{render "quiz/statistics/questions/multiple_choice/correct_pie" controller}}
</section>
<section class="essay-score-chart-section">
{{render "quiz/statistics/questions/essay/score_chart" controller}}
</section>
</div> </div>

View File

@ -0,0 +1,7 @@
<section class="correct-answer-ratio-section">
{{render "quiz/statistics/questions/multiple_choice/correct_pie" controller}}
</section>
<section class="essay-score-chart-section">
{{render "quiz/statistics/questions/essay/score_chart" controller}}
</section>

View File

@ -0,0 +1,5 @@
<span class="question-attempts">{{attemptsLabel}}</span>
<div class="question-text">
{{{questionText}}}
</div>

View File

@ -0,0 +1,13 @@
<header>
<aside class="pull-right">
<a {{bind-attr href=quizSubmissionsZipUrl}} target="_blank">
{{t 'download_submissions' 'Download All Files'}}
</a>
</aside>
{{partial "quiz/statistics/questions/essay/header_contents"}}
</header>
<div>
{{partial "quiz/statistics/questions/essay/charts"}}
</div>

View File

@ -0,0 +1,2 @@
define [ '../questions_view' ], (Base) ->
Base

View File

@ -1,2 +1,2 @@
define [ '../questions_view' ], (BaseView) -> define [ '../questions_view' ], (Base) ->
BaseView Base

View File

@ -0,0 +1,2 @@
define [ '../questions_view' ], (Base) ->
Base

View File

@ -1,2 +1,2 @@
define [ '../questions_view' ], (BaseView) -> define [ '../questions_view' ], (Base) ->
BaseView Base

View File

@ -1,2 +1,2 @@
define [ '../questions_view' ], (BaseView) -> define [ '../questions_view' ], (Base) ->
BaseView.extend {} Base

View File

@ -17,7 +17,7 @@ module Quizzes
:require_lockdown_browser_monitor, :lockdown_browser_monitor_data, :require_lockdown_browser_monitor, :lockdown_browser_monitor_data,
:speed_grader_url, :permissions, :quiz_reports_url, :quiz_statistics_url, :speed_grader_url, :permissions, :quiz_reports_url, :quiz_statistics_url,
:message_students_url, :quiz_submission_html_url, :section_count, :message_students_url, :quiz_submission_html_url, :section_count,
:take_quiz_url, :takeable :take_quiz_url, :takeable, :quiz_submissions_zip_url
def_delegators :@controller, def_delegators :@controller,
:api_v1_course_assignment_group_url, :api_v1_course_assignment_group_url,
@ -29,7 +29,8 @@ module Quizzes
:course_quiz_submission_html_url, :course_quiz_submission_html_url,
:api_v1_course_quiz_submission_users_url, :api_v1_course_quiz_submission_users_url,
:api_v1_course_quiz_submission_users_message_url, :api_v1_course_quiz_submission_users_message_url,
:course_quiz_take_url :course_quiz_take_url,
:course_quiz_quiz_submissions_url
def_delegators :@object, def_delegators :@object,
:context, :context,
@ -145,6 +146,8 @@ module Quizzes
when :submitted_students, :unsubmitted_students then user_may_grade? when :submitted_students, :unsubmitted_students then user_may_grade?
when :quiz_submission then accepts_jsonapi? when :quiz_submission then accepts_jsonapi?
when :quiz_submission_html_url then accepts_jsonapi? when :quiz_submission_html_url then accepts_jsonapi?
when :quiz_submissions_zip_url then
accepts_jsonapi? && user_may_grade? && has_file_uploads?
else true else true
end end
end end
@ -211,6 +214,10 @@ module Quizzes
!!(accepts_jsonapi? || stringify_json_ids?) !!(accepts_jsonapi? || stringify_json_ids?)
end end
def quiz_submissions_zip_url
course_quiz_quiz_submissions_url(quiz.context, quiz.id, zip: 1)
end
private private
def show_speedgrader? def show_speedgrader?
@ -260,5 +267,8 @@ module Quizzes
!!submission_for_current_user !!submission_for_current_user
end end
def has_file_uploads?
quiz.has_file_upload_question?
end
end end
end end

View File

@ -22,7 +22,7 @@ module CanvasQuizStatistics::Analyzers
private private
def answer_present?(response) def answer_present?(response)
response.has_key?(:attachment_ids) && response[:attachment_ids].any? (response[:attachment_ids] || []).any?
end end
end end
end end

View File

@ -12,6 +12,7 @@ describe CanvasQuizStatistics::Analyzers::FileUpload do
it 'should count students who have uploaded an attachment' do it 'should count students who have uploaded an attachment' do
subject.run([ subject.run([
{}, {},
{ attachment_ids: nil },
{ attachment_ids: [] }, { attachment_ids: [] },
{ attachment_ids: ['1'] } { attachment_ids: ['1'] }
])[:responses].should == 1 ])[:responses].should == 1

View File

@ -354,6 +354,35 @@ describe Quizzes::QuizSerializer do
end end
end end
describe "quiz_submissions_zip_url" do
it "includes a url to download all files" do
controller.expects(:accepts_jsonapi?).at_least_once.returns true
serializer.expects(:user_may_grade?).at_least_once.returns true
serializer.expects(:has_file_uploads?).at_least_once.returns true
serializer.as_json[:quiz][:quiz_submissions_zip_url].should ==
'http://example.com/courses/1/quizzes/1/submissions?zip=1'
end
it "doesn't if it's not a JSON-API request" do
controller.expects(:accepts_jsonapi?).at_least_once.returns false
serializer.expects(:user_may_grade?).at_least_once.returns true
serializer.as_json[:quiz].should_not have_key :quiz_submissions_zip_url
end
it "doesn't if the user may not grade" do
controller.expects(:accepts_jsonapi?).at_least_once.returns true
serializer.expects(:user_may_grade?).at_least_once.returns false
serializer.as_json[:quiz].should_not have_key :quiz_submissions_zip_url
end
it "doesn't if the quiz has no file upload questions" do
controller.expects(:accepts_jsonapi?).at_least_once.returns true
serializer.expects(:user_may_grade?).at_least_once.returns true
serializer.expects(:has_file_uploads?).at_least_once.returns false
serializer.as_json[:quiz].should_not have_key :quiz_submissions_zip_url
end
end
describe "permissions" do describe "permissions" do
it "serializes permissions" do it "serializes permissions" do
serializer.as_json[:quiz][:permissions].should == { serializer.as_json[:quiz][:permissions].should == {