live assessments api
fixes CNVS-12916 test plan - as a teacher in a course, use the api to create a live assessment aligned with an outcome (see the api docs for how the endpoint works) - create results for some students - ensure that the results and the assessment can be read back using the index endpoints - ensure that the assessment shows up in the web ui at /course/:course_id/outcomes/users/:student_id (click 'Show All Artifacts') - try to create an assessment using the same key as an existing assessment - ensure that the existing assessment is returned (check the id) - fetch results specifying a user id to filter by - ensure that only results for that user are returned Change-Id: I2d09691f772658aea3ccdd36cff2df5835b1f2cd Reviewed-on: https://gerrit.instructure.com/35092 Reviewed-by: Ethan Vizitei <evizitei@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> QA-Review: Steven Shepherd <sshepherd@instructure.com> Product-Review: Derrick Hathaway <derrick@instructure.com>
This commit is contained in:
parent
bb45484387
commit
4ec0d04bb9
|
@ -0,0 +1,30 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module Filters::LiveAssessments
|
||||
protected
|
||||
|
||||
# be sure to have a valid context before calling this
|
||||
def require_assessment
|
||||
id = params.has_key?(:assessment_id) ? params[:assessment_id] : params[:id]
|
||||
|
||||
@assessment = LiveAssessments::Assessment.find(id)
|
||||
reject! 'assessment does not belong to the given context' unless @assessment.context == @context
|
||||
@assessment
|
||||
end
|
||||
end
|
|
@ -0,0 +1,146 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module LiveAssessments
|
||||
# @API LiveAssessments
|
||||
# @beta
|
||||
# Manage live assessments
|
||||
#
|
||||
# @model Assessment
|
||||
# {
|
||||
# "id": "Assessment",
|
||||
# "description": "A simple assessment that collects pass/fail results for a student",
|
||||
# "properties": {
|
||||
# "id": {
|
||||
# "type": "string",
|
||||
# "example": "42",
|
||||
# "description": "A unique identifier for this live assessment"
|
||||
# },
|
||||
# "key": {
|
||||
# "type": "string",
|
||||
# "example": "2014-05-27,outcome_52",
|
||||
# "description": "A client specified unique identifier for the assessment"
|
||||
# },
|
||||
# "title": {
|
||||
# "type": "string",
|
||||
# "example": "May 27th Reading Assessment",
|
||||
# "description": "A human readable title for the assessment"
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
|
||||
class AssessmentsController < ApplicationController
|
||||
before_filter :require_user
|
||||
before_filter :require_context
|
||||
|
||||
# @API Create or find a live assessment
|
||||
# @beta
|
||||
#
|
||||
# Creates or finds a live assessment with
|
||||
#
|
||||
# @example_request
|
||||
# {
|
||||
# "assessments": [{
|
||||
# "key": "2014-05-27-Outcome-52",
|
||||
# "title": "Tuesday's LiveAssessment",
|
||||
# "links": {
|
||||
# "outcome": "1"
|
||||
# }
|
||||
# }]
|
||||
# }
|
||||
#
|
||||
# @example_response
|
||||
# {
|
||||
# "links": {
|
||||
# "assessments.results": "http://example.com/courses/1/live_assessments/5/results"
|
||||
# },
|
||||
# "assessments": [Assessment]
|
||||
# }
|
||||
#
|
||||
def create
|
||||
return unless authorized_action(Assessment.new(context: @context), @current_user, :create)
|
||||
reject! 'missing required key :assessments' unless params[:assessments].is_a?(Array)
|
||||
|
||||
@assessments = []
|
||||
|
||||
Assessment.transaction do
|
||||
params[:assessments].each do |assessment_hash|
|
||||
if assessment_hash[:links] && outcome_id = assessment_hash[:links][:outcome]
|
||||
return unless authorized_action(@context, @current_user, :manage_outcomes)
|
||||
@outcome = @context.linked_learning_outcomes.where(id: outcome_id).first
|
||||
reject! 'outcome must be linked to the context' unless @outcome
|
||||
end
|
||||
|
||||
reject! 'missing required key :title' if assessment_hash[:title].blank?
|
||||
reject! 'missing required key :key' if assessment_hash[:key].blank?
|
||||
assessment = Assessment.find_or_initialize_by_context_id_and_context_type_and_key(@context.id, @context.class.to_s, assessment_hash[:key])
|
||||
assessment.title = assessment_hash[:title]
|
||||
assessment.save!
|
||||
if @outcome
|
||||
criterion = @outcome.data && @outcome.data[:rubric_criterion]
|
||||
mastery_score = criterion && criterion[:mastery_points] / criterion[:points_possible]
|
||||
@outcome.align(assessment, @context, mastery_type: "none", mastery_score: mastery_score)
|
||||
end
|
||||
@assessments << assessment
|
||||
end
|
||||
end
|
||||
|
||||
render json: serialize_jsonapi(@assessments)
|
||||
end
|
||||
|
||||
# @API List live assessments
|
||||
# @beta
|
||||
#
|
||||
# Returns a list of live assessments.
|
||||
#
|
||||
# @example_response
|
||||
# {
|
||||
# "links": {
|
||||
# "assessments.results": "http://example.com/courses/1/live_assessments/{assessments.id}/results"
|
||||
# },
|
||||
# "assessments": [Assessment]
|
||||
# }
|
||||
#
|
||||
def index
|
||||
return unless authorized_action(Assessment.new(context: @context), @current_user, :read)
|
||||
|
||||
@assessments = Assessment.for_context(@context)
|
||||
@assessments = Api.paginate(@assessments, self, polymorphic_url([:api_v1, @context, :live_assessments]))
|
||||
|
||||
render json: serialize_jsonapi(@assessments)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def serialize_jsonapi(assessments)
|
||||
serialized = Canvas::APIArraySerializer.new(assessments, {
|
||||
each_serializer: LiveAssessments::AssessmentSerializer,
|
||||
controller: self,
|
||||
scope: @current_user,
|
||||
root: false,
|
||||
include_root: false
|
||||
}).as_json
|
||||
{
|
||||
links: {
|
||||
'assessments.results' => polymorphic_url([:api_v1, @context]) + '/live_assessments/{assessments.id}/results'
|
||||
},
|
||||
assessments: serialized
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,146 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module LiveAssessments
|
||||
# @API LiveAssessments
|
||||
# @beta
|
||||
# Manage live assessment results
|
||||
#
|
||||
# @model Result
|
||||
# {
|
||||
# "id": "Result",
|
||||
# "description": "A pass/fail results for a student",
|
||||
# "properties": {
|
||||
# "id": {
|
||||
# "type": "string",
|
||||
# "example": "42",
|
||||
# "description": "A unique identifier for this result"
|
||||
# },
|
||||
# "passed": {
|
||||
# "type": "boolean",
|
||||
# "example": true,
|
||||
# "description": "Whether the user passed or not"
|
||||
# },
|
||||
# "assessed_at": {
|
||||
# "type": "datetime",
|
||||
# "example": "2014-13-05T00:01:57-06:00",
|
||||
# "description": "When this result was recorded"
|
||||
# },
|
||||
# "links": {
|
||||
# "example": "{\"user\"=>\"3\", \"assessor\"=>\"42\", \"assessment\"=>\"30\"}",
|
||||
# "description": "Unique identifiers of objects associated with this result"
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
class ResultsController < ApplicationController
|
||||
include Filters::LiveAssessments
|
||||
|
||||
before_filter :require_user
|
||||
before_filter :require_context
|
||||
before_filter :require_assessment
|
||||
|
||||
# @API Create a live assessment results
|
||||
# @beta
|
||||
#
|
||||
# @example_request
|
||||
# {
|
||||
# "results": [{
|
||||
# "passed": false,
|
||||
# "assessed_at": "2014-05-26T14:57:23-07:00",
|
||||
# "links": [
|
||||
# "user": "15"
|
||||
# ]
|
||||
# },{
|
||||
# "passed": true,
|
||||
# "assessed_at": "2014-05-26T13:05:40-07:00",
|
||||
# "links": [
|
||||
# "user": "16"
|
||||
# ]
|
||||
# }]
|
||||
# }
|
||||
#
|
||||
# @example_response
|
||||
# {
|
||||
# "results": [Result]
|
||||
# }
|
||||
#
|
||||
def create
|
||||
return unless authorized_action(@assessment.results.new, @current_user, :create)
|
||||
reject! 'missing required key :results' unless params[:results].is_a?(Array)
|
||||
|
||||
@results = []
|
||||
result_hashes_by_user_id = params[:results].group_by {|result| result[:links] and result[:links][:user]}
|
||||
Result.transaction do
|
||||
result_hashes_by_user_id.each do |user_id, result_hashes|
|
||||
reject! 'missing required key :user' unless user_id
|
||||
@user = @context.users.where(id: user_id).first
|
||||
reject! 'user must be in the context' unless @user
|
||||
|
||||
result_hashes.each do |result_hash|
|
||||
result = @assessment.results.build(
|
||||
user: @user,
|
||||
assessor: @current_user,
|
||||
passed: result_hash[:passed],
|
||||
assessed_at: result_hash[:assessed_at]
|
||||
)
|
||||
result.save!
|
||||
@results << result
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@assessment.send_later_if_production(:generate_submissions_for, @results.map(&:user).uniq)
|
||||
render json: serialize_jsonapi(@results)
|
||||
end
|
||||
|
||||
# @API List live assessment results
|
||||
# @beta
|
||||
#
|
||||
# Returns a list of live assessment results
|
||||
#
|
||||
# @argument user_id [Optional, Integer]
|
||||
# If set, restrict results to those for this user
|
||||
#
|
||||
# @example_response
|
||||
# {
|
||||
# "results": [Result]
|
||||
# }
|
||||
#
|
||||
def index
|
||||
return unless authorized_action(@assessment.results.new, @current_user, :read)
|
||||
@results = @assessment.results
|
||||
@results = @results.for_user(params[:user_id]) if params[:user_id]
|
||||
@results = Api.paginate(@results, self, polymorphic_url([:api_v1, @context, :live_assessment_results], assessment_id: @assessment.id))
|
||||
|
||||
render json: serialize_jsonapi(@results)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def serialize_jsonapi(results)
|
||||
serialized = Canvas::APIArraySerializer.new(results, {
|
||||
each_serializer: LiveAssessments::ResultSerializer,
|
||||
controller: self,
|
||||
scope: @current_user,
|
||||
root: false,
|
||||
include_root: false
|
||||
}).as_json
|
||||
{ results: serialized }
|
||||
end
|
||||
end
|
||||
end
|
|
@ -29,7 +29,7 @@ class ContentTag < ActiveRecord::Base
|
|||
belongs_to :content, :polymorphic => true
|
||||
validates_inclusion_of :content_type, :allow_nil => true, :in => ['Attachment', 'Assignment', 'WikiPage',
|
||||
'ContextModuleSubHeader', 'Quizzes::Quiz', 'ExternalUrl', 'LearningOutcome', 'DiscussionTopic',
|
||||
'Rubric', 'ContextExternalTool', 'LearningOutcomeGroup', 'AssessmentQuestionBank']
|
||||
'Rubric', 'ContextExternalTool', 'LearningOutcomeGroup', 'AssessmentQuestionBank', 'LiveAssessments::Assessment']
|
||||
belongs_to :context, :polymorphic => true
|
||||
validates_inclusion_of :context_type, :allow_nil => true, :in => ['Course', 'LearningOutcomeGroup',
|
||||
'Assignment', 'Account', 'Quizzes::Quiz']
|
||||
|
|
|
@ -26,11 +26,11 @@ class LearningOutcomeResult < ActiveRecord::Base
|
|||
belongs_to :learning_outcome
|
||||
belongs_to :alignment, :class_name => 'ContentTag', :foreign_key => :content_tag_id
|
||||
belongs_to :association_object, :polymorphic => true, :foreign_type => :association_type, :foreign_key => :association_id
|
||||
validates_inclusion_of :association_type, :allow_nil => true, :in => ['Quizzes::Quiz', 'RubricAssociation', 'Assignment']
|
||||
validates_inclusion_of :association_type, :allow_nil => true, :in => ['Quizzes::Quiz', 'RubricAssociation', 'Assignment', 'LiveAssessments::Assessment']
|
||||
belongs_to :artifact, :polymorphic => true
|
||||
validates_inclusion_of :artifact_type, :allow_nil => true, :in => ['Quizzes::QuizSubmission', 'RubricAssessment', 'Submission']
|
||||
validates_inclusion_of :artifact_type, :allow_nil => true, :in => ['Quizzes::QuizSubmission', 'RubricAssessment', 'Submission', 'LiveAssessments::Submission']
|
||||
belongs_to :associated_asset, :polymorphic => true
|
||||
validates_inclusion_of :associated_asset_type, :allow_nil => true, :in => ['AssessmentQuestion', 'Quizzes::Quiz']
|
||||
validates_inclusion_of :associated_asset_type, :allow_nil => true, :in => ['AssessmentQuestion', 'Quizzes::Quiz', 'LiveAssessments::Assessment']
|
||||
belongs_to :context, :polymorphic => true
|
||||
validates_inclusion_of :context_type, :allow_nil => true, :in => ['Course']
|
||||
simply_versioned
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module LiveAssessments
|
||||
class Assessment < ActiveRecord::Base
|
||||
attr_accessible :context, :key, :title
|
||||
|
||||
belongs_to :context, polymorphic: true
|
||||
has_many :submissions, class_name: 'LiveAssessments::Submission'
|
||||
has_many :results, class_name: 'LiveAssessments::Result'
|
||||
|
||||
has_many :learning_outcome_alignments, :as => :content, :class_name => 'ContentTag', :conditions => ['content_tags.tag_type = ? AND content_tags.workflow_state != ?', 'learning_outcome', 'deleted'], :include => :learning_outcome
|
||||
|
||||
validates_presence_of :context_id, :context_type, :key, :title
|
||||
validates_length_of :title, maximum: maximum_string_length
|
||||
validates_length_of :key, maximum: maximum_string_length
|
||||
|
||||
scope :for_context, lambda { |context| where(:context_id => context, :context_type => context.class.to_s) }
|
||||
|
||||
set_policy do
|
||||
given { |user, session| self.cached_context_grants_right?(user, session, :manage_assignments) }
|
||||
can :create and can :update
|
||||
|
||||
given { |user, session| self.cached_context_grants_right?(user, session, :view_all_grades) }
|
||||
can :read
|
||||
end
|
||||
|
||||
def generate_submissions_for(users)
|
||||
# if we aren't aligned, we don't need submissions
|
||||
return unless learning_outcome_alignments.any?
|
||||
Assessment.transaction do
|
||||
users.each do |user|
|
||||
submission = submissions.find_or_initialize_by_user_id(user.id)
|
||||
|
||||
user_results = results.for_user(user).all
|
||||
next unless user_results.any?
|
||||
submission.possible = user_results.count
|
||||
submission.score = user_results.count(&:passed)
|
||||
submission.assessed_at = user_results.map(&:assessed_at).max
|
||||
submission.save!
|
||||
|
||||
# it's likely that there is only one alignment per assessment, but we
|
||||
# have to deal with any number of them
|
||||
learning_outcome_alignments.each do |alignment|
|
||||
submission.create_outcome_result(alignment)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,23 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module LiveAssessments
|
||||
def self.table_name_prefix
|
||||
"live_assessments_"
|
||||
end
|
||||
end
|
|
@ -0,0 +1,40 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module LiveAssessments
|
||||
class Result < ActiveRecord::Base
|
||||
attr_accessible :user, :assessor, :passed, :assessed_at
|
||||
|
||||
belongs_to :assessor, class_name: 'User'
|
||||
belongs_to :user
|
||||
belongs_to :assessment, class_name: 'LiveAssessments::Assessment'
|
||||
|
||||
validates_presence_of :assessor_id, :assessment_id, :assessed_at
|
||||
validates_inclusion_of :passed, :in => [true, false]
|
||||
|
||||
scope :for_user, lambda { |user| where(:user_id => user) }
|
||||
|
||||
set_policy do
|
||||
given { |user, session| self.assessment.grants_right?(user, session, :update) }
|
||||
can :create
|
||||
|
||||
given { |user, session| self.assessment.grants_right?(user, session, :read) }
|
||||
can :read
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,62 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module LiveAssessments
|
||||
class Submission < ActiveRecord::Base
|
||||
attr_accessible :user, :assessment, :possible, :score, :assessed_at
|
||||
|
||||
belongs_to :user
|
||||
belongs_to :assessment, class_name: 'LiveAssessments::Assessment'
|
||||
|
||||
validates_presence_of :user, :assessment
|
||||
|
||||
def create_outcome_result(alignment)
|
||||
# we don't delete results right now
|
||||
# when we do, we'll need to start cleaning up outcome results when all the results are deleted. bail until then.
|
||||
return if possible == 0
|
||||
|
||||
outcome_result = alignment.learning_outcome_results.find_or_initialize_by_user_id(user.id)
|
||||
outcome_result.title = "#{user.name}, #{assessment.title}"
|
||||
outcome_result.context = assessment.context
|
||||
outcome_result.associated_asset = assessment
|
||||
outcome_result.artifact = self
|
||||
outcome_result.assessed_at = assessed_at
|
||||
|
||||
outcome_result.score = score
|
||||
outcome_result.possible = possible
|
||||
outcome_result.percent = score.to_f / possible.to_f
|
||||
|
||||
if alignment.mastery_score
|
||||
outcome_result.mastery = outcome_result.percent >= alignment.mastery_score
|
||||
else
|
||||
outcome_result.mastery = nil
|
||||
end
|
||||
|
||||
# map actual magic marker result to outcome rubric criterion if we have one
|
||||
# this is a hack. the rollups and gradebooks should handle explicit mastery
|
||||
# this only works because we set mastery_score based on the rubric in the first place
|
||||
criterion = alignment.learning_outcome.data && alignment.learning_outcome.data[:rubric_criterion]
|
||||
if criterion
|
||||
outcome_result.possible = criterion[:points_possible]
|
||||
outcome_result.score = outcome_result.percent * outcome_result.possible
|
||||
end
|
||||
|
||||
outcome_result.save!
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,25 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module LiveAssessments
|
||||
class AssessmentSerializer < Canvas::APISerializer
|
||||
root :assessment
|
||||
|
||||
attributes :id, :key, :title
|
||||
end
|
||||
end
|
|
@ -0,0 +1,31 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
module LiveAssessments
|
||||
class ResultSerializer < Canvas::APISerializer
|
||||
root :result
|
||||
|
||||
attributes :id, :passed, :assessed_at
|
||||
|
||||
def_delegators :@object, :user
|
||||
|
||||
has_one :user, embed: :ids, embed_in_root: true
|
||||
has_one :assessor, class_name: 'User', embed: :ids, embed_in_root: true
|
||||
has_one :assessment, embed: :ids, embed_in_root: true
|
||||
end
|
||||
end
|
|
@ -1431,6 +1431,22 @@ routes.draw do
|
|||
get "polls/:poll_id/poll_sessions/:poll_session_id/poll_submissions/:id", :action => :show, :path_name => 'poll_submission'
|
||||
end
|
||||
|
||||
scope(:controller => 'live_assessments/assessments') do
|
||||
%w(course).each do |context|
|
||||
prefix = "#{context}s/:#{context}_id"
|
||||
get "#{prefix}/live_assessments", :action => :index, :path_name => "#{context}_live_assessments"
|
||||
post "#{prefix}/live_assessments", :action => :create, :path_name => "#{context}_live_assessment_create"
|
||||
end
|
||||
end
|
||||
|
||||
scope(:controller => 'live_assessments/results') do
|
||||
%w(course).each do |context|
|
||||
prefix = "#{context}s/:#{context}_id"
|
||||
get "#{prefix}/live_assessments/:assessment_id/results", :action => :index, :path_name => "#{context}_live_assessment_results"
|
||||
post "#{prefix}/live_assessments/:assessment_id/results", :action => :create, :path_name => "#{context}_live_assessment_result_create"
|
||||
end
|
||||
end
|
||||
|
||||
scope(:controller => :outcome_groups_api) do
|
||||
def og_routes(context)
|
||||
prefix = (context == "global" ? context : "#{context}s/:#{context}_id")
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
class CreateLiveAssessments < ActiveRecord::Migration
|
||||
tag :predeploy
|
||||
|
||||
def self.up
|
||||
create_table :live_assessments_assessments do |t|
|
||||
t.string :key, null: false
|
||||
t.string :title, null: false
|
||||
t.integer :context_id, limit: 8, null: false
|
||||
t.string :context_type, null: false
|
||||
t.timestamps
|
||||
end
|
||||
add_index :live_assessments_assessments, [:context_id, :context_type, :key], unique: true, name: 'index_live_assessments'
|
||||
|
||||
create_table :live_assessments_submissions do |t|
|
||||
t.integer :user_id, limit: 8, null: false
|
||||
t.integer :assessment_id, limit: 8, null: false
|
||||
t.float :possible
|
||||
t.float :score
|
||||
t.datetime :assessed_at
|
||||
t.timestamps
|
||||
end
|
||||
add_index :live_assessments_submissions, [:assessment_id, :user_id], unique: true
|
||||
|
||||
create_table :live_assessments_results do |t|
|
||||
t.integer :user_id, limit: 8, null: false
|
||||
t.integer :assessor_id, limit: 8, null: false
|
||||
t.integer :assessment_id, limit: 8, null: false
|
||||
t.boolean :passed, null: false
|
||||
t.datetime :assessed_at, null: false
|
||||
end
|
||||
add_index :live_assessments_results, [:assessment_id, :user_id]
|
||||
|
||||
add_foreign_key :live_assessments_submissions, :live_assessments_assessments, column: :assessment_id
|
||||
add_foreign_key :live_assessments_submissions, :users
|
||||
add_foreign_key :live_assessments_results, :users, column: :assessor_id
|
||||
add_foreign_key :live_assessments_results, :live_assessments_assessments, column: :assessment_id
|
||||
end
|
||||
|
||||
def self.down
|
||||
drop_table :live_assessments_results
|
||||
drop_table :live_assessments_submissions
|
||||
drop_table :live_assessments_assessments
|
||||
end
|
||||
end
|
|
@ -0,0 +1,108 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../api_spec_helper')
|
||||
|
||||
describe LiveAssessments::AssessmentsController, type: :request do
|
||||
let(:assessment_course) { course(active_all: true) }
|
||||
let(:teacher) { assessment_course.teachers.first }
|
||||
let(:student) { course_with_student(course: assessment_course).user }
|
||||
let(:outcome) do
|
||||
outcome = assessment_course.created_learning_outcomes.create!(:description => 'this is a test outcome', :short_description => 'test outcome')
|
||||
assessment_course.root_outcome_group.add_outcome(outcome)
|
||||
assessment_course.root_outcome_group.save!
|
||||
assessment_course.reload
|
||||
outcome
|
||||
end
|
||||
let(:unrelated_outcome) {course_with_teacher.course.created_learning_outcomes.create!(description: 'this outcome is in a different course', short_description: 'unrelated outcome')}
|
||||
let(:assessment_hash) {{key: '2014-05-28-Outcome-52', title: 'a test assessment'}}
|
||||
|
||||
describe 'POST create' do
|
||||
def create_assessments(params, opts={})
|
||||
api_call_as_user(opts[:user] || teacher,
|
||||
:post,
|
||||
"/api/v1/courses/#{assessment_course.id}/live_assessments",
|
||||
{ controller: 'live_assessments/assessments', action: 'create', format: 'json', course_id: assessment_course.id.to_s },
|
||||
{ assessments: params }, {}, opts)
|
||||
end
|
||||
|
||||
context "as a teacher" do
|
||||
it "creates an assessment" do
|
||||
create_assessments([assessment_hash])
|
||||
data = json_parse
|
||||
assessment = LiveAssessments::Assessment.find(data['assessments'][0]['id'])
|
||||
assessment.key.should == assessment_hash[:key]
|
||||
assessment.title.should == assessment_hash[:title]
|
||||
end
|
||||
|
||||
it "aligns an assessment when given an outcome" do
|
||||
create_assessments([assessment_hash.merge(links: {outcome: outcome.id})])
|
||||
data = json_parse
|
||||
assessment = LiveAssessments::Assessment.find(data['assessments'][0]['id'])
|
||||
assessment.learning_outcome_alignments.count.should == 1
|
||||
assessment.learning_outcome_alignments.first.learning_outcome.should == outcome
|
||||
end
|
||||
|
||||
it "won't align an unrelated outcome" do
|
||||
create_assessments([assessment_hash.merge(links: {outcome: unrelated_outcome.id})], expected_status: 400)
|
||||
end
|
||||
|
||||
it 'returns an existing assessment with the same key' do
|
||||
assessment = LiveAssessments::Assessment.create!(assessment_hash.merge(context: assessment_course))
|
||||
create_assessments([assessment_hash])
|
||||
data = json_parse
|
||||
data['assessments'].count.should == 1
|
||||
data['assessments'][0]['id'].should == assessment.id.to_s
|
||||
end
|
||||
end
|
||||
|
||||
context "as a student" do
|
||||
it "is unauthorized" do
|
||||
create_assessments([assessment_hash], user: student, expected_status: 401)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET index' do
|
||||
def index_assessments(opts={})
|
||||
api_call_as_user(opts[:user] || teacher,
|
||||
:get,
|
||||
"/api/v1/courses/#{assessment_course.id}/live_assessments",
|
||||
{ controller: 'live_assessments/assessments', action: 'index', format: 'json', course_id: assessment_course.id.to_s },
|
||||
{}, {}, opts)
|
||||
end
|
||||
|
||||
context 'as a teacher' do
|
||||
it 'returns all the assessments for the context' do
|
||||
LiveAssessments::Assessment.create!(assessment_hash.merge(context: assessment_course))
|
||||
LiveAssessments::Assessment.create!(assessment_hash.merge(context: assessment_course, key: 'another assessment'))
|
||||
index_assessments
|
||||
data = json_parse
|
||||
data['assessments'].count.should == 2
|
||||
data['assessments'][0]['key'].should == assessment_hash[:key]
|
||||
data['assessments'][1]['key'].should == 'another assessment'
|
||||
end
|
||||
end
|
||||
|
||||
context 'as a student' do
|
||||
it 'is unauthorized' do
|
||||
index_assessments(user: student, expected_status: 401)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,138 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../api_spec_helper')
|
||||
|
||||
describe LiveAssessments::ResultsController, type: :request do
|
||||
let(:assessment_course) { course(active_all: true) }
|
||||
let(:teacher) { assessment_course.teachers.first }
|
||||
let(:student) { course_with_student(course: assessment_course).user }
|
||||
let(:another_student) { course_with_student(course: assessment_course).user }
|
||||
let(:outcome) do
|
||||
outcome = assessment_course.created_learning_outcomes.create!(:description => 'this is a test outcome', :short_description => 'test outcome')
|
||||
assessment_course.root_outcome_group.add_outcome(outcome)
|
||||
assessment_course.root_outcome_group.save!
|
||||
assessment_course.reload
|
||||
outcome
|
||||
end
|
||||
let(:assessment) do
|
||||
assessment = LiveAssessments::Assessment.create!(context: assessment_course, key: '2014-05-28-Outcome-1', title: 'an assessment')
|
||||
outcome.align(assessment, assessment_course, mastery_type: 'none', mastery_score: 0.6)
|
||||
assessment
|
||||
end
|
||||
|
||||
let(:assessed_at) { Time.now - 1.day }
|
||||
def result_hashes
|
||||
[student, another_student].map do |s|
|
||||
{passed: true, assessed_at: assessed_at, links: {user: s.id}}
|
||||
end
|
||||
end
|
||||
|
||||
describe 'POST create' do
|
||||
def create_results(params, opts={})
|
||||
api_call_as_user(opts[:user] || teacher,
|
||||
:post,
|
||||
"/api/v1/courses/#{assessment_course.id}/live_assessments/#{assessment.id}/results",
|
||||
{ controller: 'live_assessments/results', action: 'create', format: 'json', course_id: assessment_course.id.to_s, assessment_id: assessment.id.to_s },
|
||||
{ results: params }, {}, opts)
|
||||
end
|
||||
|
||||
context "as a teacher" do
|
||||
it "creates results" do
|
||||
create_results(result_hashes)
|
||||
data = json_parse
|
||||
data['results'].count.should == 2
|
||||
results = data['results'].map { |r| result = LiveAssessments::Result.find(r['id']) }
|
||||
results.each do |r|
|
||||
r.assessor.should == teacher
|
||||
r.assessment.should == assessment
|
||||
r.passed.should == true
|
||||
r.assessed_at.to_i.should == assessed_at.to_i
|
||||
end
|
||||
results.map(&:user).should == [student, another_student]
|
||||
end
|
||||
|
||||
it 'generates submissions' do
|
||||
LiveAssessments::Assessment.any_instance.expects(:generate_submissions_for).with([student, another_student])
|
||||
create_results(result_hashes)
|
||||
end
|
||||
|
||||
it 'requires user to be in the context' do
|
||||
create_results([result_hashes[0].merge(links: {user: user_model.id})], expected_status: 400)
|
||||
end
|
||||
end
|
||||
|
||||
context "as a student" do
|
||||
it "is unauthorized" do
|
||||
create_results([], user: student, expected_status: 401)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET index' do
|
||||
def index_results(params, opts={})
|
||||
api_call_as_user(opts[:user] || teacher,
|
||||
:get,
|
||||
"/api/v1/courses/#{assessment_course.id}/live_assessments/#{assessment.id}/results",
|
||||
{ controller: 'live_assessments/results', action: 'index', format: 'json', course_id: assessment_course.id.to_s, assessment_id: assessment.id.to_s },
|
||||
params, {}, opts)
|
||||
end
|
||||
|
||||
context 'as a teacher' do
|
||||
it 'returns all the results for the assessment' do
|
||||
results = [student, another_student].map do |s|
|
||||
assessment.results.create!(user: s, assessor: teacher, passed: true, assessed_at: assessed_at)
|
||||
end
|
||||
index_results({})
|
||||
data = json_parse
|
||||
results = data['results']
|
||||
results.count.should == 2
|
||||
results.each do |r|
|
||||
r['links']['assessor'].should == teacher.id.to_s
|
||||
r['links']['assessment'].should == assessment.id.to_s
|
||||
r['passed'].should == true
|
||||
Time.parse(r['assessed_at']).to_i.should == assessed_at.to_i
|
||||
end
|
||||
results.map{|h|h['links']['user']}.should == [student.id.to_s, another_student.id.to_s]
|
||||
end
|
||||
|
||||
it 'filters the results by user' do
|
||||
results = [student, another_student].map do |s|
|
||||
assessment.results.create!(user: s, assessor: teacher, passed: true, assessed_at: assessed_at)
|
||||
end
|
||||
index_results({user_id: another_student.id})
|
||||
data = json_parse
|
||||
results = data['results']
|
||||
results.count.should == 1
|
||||
results.each do |r|
|
||||
r['links']['assessor'].should == teacher.id.to_s
|
||||
r['links']['assessment'].should == assessment.id.to_s
|
||||
r['passed'].should == true
|
||||
Time.parse(r['assessed_at']).to_i.should == assessed_at.to_i
|
||||
end
|
||||
results.map{|h|h['links']['user']}.should == [another_student.id.to_s]
|
||||
end
|
||||
end
|
||||
|
||||
context 'as a student' do
|
||||
it 'is unauthorized' do
|
||||
index_results({}, user: student, expected_status: 401)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,91 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
|
||||
|
||||
describe LiveAssessments::Assessment do
|
||||
let(:assessment_context) { course(active_all: true) }
|
||||
let(:assessment_user) { course_with_student(course: assessment_context, active_all: true).user }
|
||||
let(:assessor) { assessment_context.teachers.first }
|
||||
let(:another_assessment_user) { course_with_student(course: assessment_context, active_all: true).user }
|
||||
let(:assessment) { LiveAssessments::Assessment.create!(context: assessment_context, key: 'test key', title: 'test title') }
|
||||
let(:outcome) do
|
||||
outcome = assessment_context.created_learning_outcomes.create!(:description => 'this is a test outcome', :short_description => 'test outcome')
|
||||
assessment_context.root_outcome_group.add_outcome(outcome)
|
||||
assessment_context.root_outcome_group.save!
|
||||
assessment_context.reload
|
||||
outcome
|
||||
end
|
||||
let(:another_outcome) do
|
||||
outcome = assessment_context.created_learning_outcomes.create!(:description => 'this is another test outcome', :short_description => 'test outcome 2')
|
||||
assessment_context.root_outcome_group.add_outcome(outcome)
|
||||
assessment_context.root_outcome_group.save!
|
||||
assessment_context.reload
|
||||
outcome
|
||||
end
|
||||
|
||||
describe '#generate_submissions_for' do
|
||||
it "doesn't do anything without aligned outcomes" do
|
||||
assessment.generate_submissions_for(assessment_user)
|
||||
assessment.submissions.count.should == 0
|
||||
end
|
||||
|
||||
it "doesn't create a submission for users with no results" do
|
||||
outcome.align(assessment, assessment_context, mastery_type: 'none', mastery_score: 0.6)
|
||||
assessment.results.create!(user: assessment_user, assessor: assessor, passed: true, assessed_at: Time.now)
|
||||
assessment.generate_submissions_for([assessment_user, another_assessment_user])
|
||||
assessment.submissions.count.should == 1
|
||||
end
|
||||
|
||||
it "creates a submission for each given user" do
|
||||
outcome.align(assessment, assessment_context, mastery_type: 'none', mastery_score: 0.6)
|
||||
assessment.results.create!(user: assessment_user, assessor: assessor, passed: true, assessed_at: Time.now)
|
||||
assessment.results.create!(user: another_assessment_user, assessor: assessor, passed: false, assessed_at: Time.now)
|
||||
assessment.generate_submissions_for([assessment_user, another_assessment_user])
|
||||
assessment.submissions.count.should == 2
|
||||
assessment.submissions[0].possible.should == 1
|
||||
assessment.submissions[0].score.should == 1
|
||||
assessment.submissions[1].possible.should == 1
|
||||
assessment.submissions[1].score.should == 0
|
||||
end
|
||||
|
||||
it "updates existing submission" do
|
||||
outcome.align(assessment, assessment_context, mastery_type: 'none', mastery_score: 0.6)
|
||||
assessment.results.create!(user: assessment_user, assessor: assessor, passed: true, assessed_at: Time.now)
|
||||
assessment.generate_submissions_for([assessment_user])
|
||||
assessment.submissions.count.should == 1
|
||||
submission = assessment.submissions.first
|
||||
submission.possible.should == 1
|
||||
submission.score.should == 1
|
||||
assessment.results.create!(user: assessment_user, assessor: assessor, passed: false, assessed_at: Time.now)
|
||||
assessment.generate_submissions_for([assessment_user])
|
||||
assessment.submissions.count.should == 1
|
||||
submission.reload.possible.should == 2
|
||||
submission.score.should == 1
|
||||
end
|
||||
|
||||
it "creates outcome results for each alignment" do
|
||||
alignment1 = outcome.align(assessment, assessment_context, mastery_type: 'none', mastery_score: 0.6)
|
||||
alignment2 = another_outcome.align(assessment, assessment_context, mastery_type: 'none', mastery_score: 0.5)
|
||||
LiveAssessments::Submission.any_instance.expects(:create_outcome_result).with(alignment1)
|
||||
LiveAssessments::Submission.any_instance.expects(:create_outcome_result).with(alignment2)
|
||||
assessment.results.create!(user: assessment_user, assessor: assessor, passed: true, assessed_at: Time.now)
|
||||
assessment.generate_submissions_for([assessment_user])
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,93 @@
|
|||
#
|
||||
# Copyright (C) 2014 Instructure, Inc.
|
||||
#
|
||||
# This file is part of Canvas.
|
||||
#
|
||||
# Canvas is free software: you can redistribute it and/or modify it under
|
||||
# the terms of the GNU Affero General Public License as published by the Free
|
||||
# Software Foundation, version 3 of the License.
|
||||
#
|
||||
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
||||
# details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License along
|
||||
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
require File.expand_path(File.dirname(__FILE__) + '/../../spec_helper.rb')
|
||||
|
||||
describe LiveAssessments::Submission do
|
||||
let(:assessment_context) { course(active_all: true) }
|
||||
let(:outcome) do
|
||||
outcome = assessment_context.created_learning_outcomes.create!(:description => 'this is a test outcome', :short_description => 'test outcome')
|
||||
assessment_context.root_outcome_group.add_outcome(outcome)
|
||||
assessment_context.root_outcome_group.save!
|
||||
assessment_context.reload
|
||||
outcome
|
||||
end
|
||||
let(:alignment) { outcome.align(assessment, assessment_context, mastery_type: 'none') }
|
||||
let(:assessment_user) { course_with_student(course: assessment_context, active_all: true).user }
|
||||
let(:assessment) { LiveAssessments::Assessment.create(context: assessment_context, key: 'test key', title: 'test title') }
|
||||
let(:submission) { LiveAssessments::Submission.create(user: assessment_user, assessment: assessment, possible: 10, score: 5, assessed_at: Time.now) }
|
||||
|
||||
describe '#create_outcome_result' do
|
||||
it 'does not create a result when no points are possible' do
|
||||
# we can probably create a meaningful result with no points
|
||||
# possible, but we don't now so that's what we test
|
||||
submission.possible = 0
|
||||
submission.create_outcome_result(alignment)
|
||||
result = alignment.learning_outcome_results.count.should == 0
|
||||
end
|
||||
|
||||
it 'creates an outcome result' do
|
||||
submission.create_outcome_result(alignment)
|
||||
result = alignment.learning_outcome_results.first
|
||||
result.should_not be_nil
|
||||
result.title.should == "#{assessment_user.name}, #{assessment.title}"
|
||||
result.context.should == assessment.context
|
||||
result.artifact.should == submission
|
||||
result.assessed_at.to_i.should == submission.assessed_at.to_i
|
||||
result.score.should == submission.score
|
||||
result.possible.should == submission.possible
|
||||
result.percent.should == 0.5
|
||||
result.mastery.should be_nil
|
||||
end
|
||||
|
||||
it 'updates an existing outcome result' do
|
||||
submission.create_outcome_result(alignment)
|
||||
result = alignment.learning_outcome_results.first
|
||||
result.percent.should == 0.5
|
||||
submission.score = 80
|
||||
submission.possible = 100
|
||||
submission.create_outcome_result(alignment)
|
||||
alignment.learning_outcome_results.count.should == 1
|
||||
result.reload.percent.should == 0.8
|
||||
end
|
||||
|
||||
it "scales the score to the outcome rubric criterion if present" do
|
||||
outcome.data = {rubric_criterion: {mastery_points: 3, points_possible: 5}}
|
||||
outcome.save!
|
||||
submission.create_outcome_result(alignment)
|
||||
result = alignment.learning_outcome_results.first
|
||||
result.percent.should == 0.5
|
||||
result.score.should == 2.5
|
||||
end
|
||||
|
||||
context 'alignment has a mastery score' do
|
||||
it 'sets mastery based on percent passed' do
|
||||
alignment.mastery_score = 0.6
|
||||
alignment.save!
|
||||
submission.create_outcome_result(alignment)
|
||||
result = alignment.learning_outcome_results.first
|
||||
result.mastery.should be_false
|
||||
submission.score = 80
|
||||
submission.possible = 100
|
||||
submission.create_outcome_result(alignment)
|
||||
alignment.learning_outcome_results.count.should == 1
|
||||
result.reload.mastery.should be_true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue