bring ab_guid to assignments
closes EVAL-3393 closes EVAL-3394 closes EVAL-3395 flag=none [fsc-timeout=100] test plan: - all tests pass - create an assignment with an ab_guid - using the assignments api, verify that the ab_guid is returned in the show response when `?include[]=ab_guid is passed` - using the assignments api, verify that the ab_guid is returned in the index response when `?include[]=ab_guid is passed` - using the assignments api, verify that you are able to update the ab_guid Change-Id: Ic4e8d78a6b4dfb112168ec68bd7f6e117a8030f5 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/324884 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Reviewed-by: Spencer Olson <solson@instructure.com> Reviewed-by: Kai Bjorkman <kbjorkman@instructure.com> QA-Review: Cameron Ray <cameron.ray@instructure.com> Product-Review: Cameron Ray <cameron.ray@instructure.com> Migration-Review: Jacob Burroughs <jburroughs@instructure.com>
This commit is contained in:
parent
e748efc22e
commit
4f3317ccd8
|
@ -661,6 +661,12 @@
|
|||
# "example": true,
|
||||
# "type": "boolean"
|
||||
# },
|
||||
# "ab_guid": {
|
||||
# "description": "(Optional) The academic benchmark(s) associated with the assignment or the assignment's rubric. Only included if 'ab_guid' is included in the 'include' parameter.",
|
||||
# "example": ["ABCD","EFGH"],
|
||||
# "type": "array",
|
||||
# "items": {"type": "string"}
|
||||
# },
|
||||
# "annotatable_attachment_id": {
|
||||
# "description": "The id of the attachment to be annotated by students. Relevant only if submission_types includes 'student_annotation'.",
|
||||
# "type": "integer"
|
||||
|
@ -758,7 +764,7 @@ class AssignmentsApiController < ApplicationController
|
|||
|
||||
# @API List assignments
|
||||
# Returns the paginated list of assignments for the current course or assignment group.
|
||||
# @argument include[] [String, "submission"|"assignment_visibility"|"all_dates"|"overrides"|"observed_users"|"can_edit"|"score_statistics"]
|
||||
# @argument include[] [String, "submission"|"assignment_visibility"|"all_dates"|"overrides"|"observed_users"|"can_edit"|"score_statistics"|"ab_guid"]
|
||||
# Optional information to include with each assignment:
|
||||
# submission:: The current user's current +Submission+
|
||||
# assignment_visibility:: An array of ids of students who can see the assignment
|
||||
|
@ -767,6 +773,7 @@ class AssignmentsApiController < ApplicationController
|
|||
# observed_users:: An array of submissions for observed users
|
||||
# can_edit:: an extra Boolean value will be included with each +Assignment+ (and +AssignmentDate+ if +all_dates+ is supplied) to indicate whether the caller can edit the assignment or date. Moderated grading and closed grading periods may restrict a user's ability to edit an assignment.
|
||||
# score_statistics:: An object containing min, max, and mean score on this assignment. This will not be included for students if there are less than 5 graded assignments or if disabled by the instructor. Only valid if 'submission' is also included.
|
||||
# ab_guid:: An array of guid strings for academic benchmarks
|
||||
# @argument search_term [String]
|
||||
# The partial title of the assignments to match and return.
|
||||
# @argument override_assignment_dates [Boolean]
|
||||
|
@ -1021,6 +1028,10 @@ class AssignmentsApiController < ApplicationController
|
|||
ActiveRecord::Associations.preload(assignments, :score_statistic)
|
||||
end
|
||||
|
||||
if include_params.include?("ab_guid")
|
||||
ActiveRecord::Associations.preload(assignments, rubric: { learning_outcome_alignments: :learning_outcome })
|
||||
end
|
||||
|
||||
mc_status = setup_master_course_restrictions(assignments, context)
|
||||
|
||||
assignments.map do |assignment|
|
||||
|
@ -1047,6 +1058,7 @@ class AssignmentsApiController < ApplicationController
|
|||
preloaded_user_content_attachments: preloaded_attachments,
|
||||
include_can_edit: include_params.include?("can_edit"),
|
||||
include_score_statistics: include_params.include?("score_statistics"),
|
||||
include_ab_guid: include_params.include?("ab_guid"),
|
||||
master_course_status: mc_status)
|
||||
end
|
||||
end
|
||||
|
@ -1054,7 +1066,7 @@ class AssignmentsApiController < ApplicationController
|
|||
|
||||
# @API Get a single assignment
|
||||
# Returns the assignment with the given id.
|
||||
# @argument include[] [String, "submission"|"assignment_visibility"|"overrides"|"observed_users"|"can_edit"|"score_statistics"]
|
||||
# @argument include[] [String, "submission"|"assignment_visibility"|"overrides"|"observed_users"|"can_edit"|"score_statistics"|"ab_guid"]
|
||||
# Associations to include with the assignment. The "assignment_visibility" option
|
||||
# requires that the Differentiated Assignments course feature be turned on. If
|
||||
# "observed_users" is passed, submissions for observed users will also be included.
|
||||
|
@ -1098,7 +1110,8 @@ class AssignmentsApiController < ApplicationController
|
|||
include_can_edit: included_params.include?("can_edit"),
|
||||
include_score_statistics: included_params.include?("score_statistics"),
|
||||
include_can_submit: included_params.include?("can_submit"),
|
||||
include_webhook_info: included_params.include?("webhook_info")
|
||||
include_webhook_info: included_params.include?("webhook_info"),
|
||||
include_ab_guid: included_params.include?("ab_guid")
|
||||
}
|
||||
|
||||
result_json = if use_quiz_json?
|
||||
|
|
|
@ -770,6 +770,11 @@ class Assignment < ActiveRecord::Base
|
|||
true
|
||||
end
|
||||
|
||||
def ab_guid_through_rubric
|
||||
# ab_guid is an academic benchmark guid - it can be saved on the assignmenmt itself, or accessed through this association
|
||||
rubric&.learning_outcome_alignments&.map { |loa| loa.learning_outcome.vendor_guid }&.compact || []
|
||||
end
|
||||
|
||||
def update_student_submissions(updating_user)
|
||||
graded_at = Time.zone.now
|
||||
submissions.graded.preload(:user).find_each do |s|
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
#
|
||||
# Copyright (C) 2023 - present 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/>.
|
||||
|
||||
class AddAbGuidToAssignments < ActiveRecord::Migration[7.0]
|
||||
tag :predeploy
|
||||
|
||||
def change
|
||||
add_column :assignments, :ab_guid, :string, array: true, default: [], null: false, if_not_exists: true
|
||||
end
|
||||
end
|
|
@ -458,6 +458,10 @@ module Api::V1::Assignment
|
|||
(submission.nil? || submission.attempts_left.nil? || submission.attempts_left > 0)
|
||||
end
|
||||
|
||||
if opts[:include_ab_guid]
|
||||
hash["ab_guid"] = assignment.ab_guid.presence || assignment.ab_guid_through_rubric
|
||||
end
|
||||
|
||||
hash["restrict_quantitative_data"] = assignment.restrict_quantitative_data?(user, true) || false
|
||||
|
||||
hash
|
||||
|
@ -734,6 +738,14 @@ module Api::V1::Assignment
|
|||
assignment.assignment_group = assignment.context.assignment_groups.where(id: ag_id).first
|
||||
end
|
||||
|
||||
if update_params.key?("ab_guid")
|
||||
assignment.ab_guid.clear
|
||||
ab_guids = update_params.delete("ab_guid").presence
|
||||
Array(ab_guids).each do |guid|
|
||||
assignment.ab_guid << guid if guid.present?
|
||||
end
|
||||
end
|
||||
|
||||
if update_params.key?("group_category_id") && !assignment.group_category_deleted_with_submissions?
|
||||
gc_id = update_params.delete("group_category_id").presence
|
||||
assignment.group_category = assignment.context.group_categories.where(id: gc_id).first
|
||||
|
@ -1151,7 +1163,8 @@ module Api::V1::Assignment
|
|||
{ "allowed_extensions" => strong_anything },
|
||||
{ "integration_data" => strong_anything },
|
||||
{ "external_tool_tag_attributes" => strong_anything },
|
||||
({ "submission_types" => strong_anything } if should_update_submission_types)
|
||||
({ "submission_types" => strong_anything } if should_update_submission_types),
|
||||
{ "ab_guid" => strong_anything },
|
||||
].compact
|
||||
end
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ describe AssignmentsApiController, type: :request do
|
|||
include Api::V1::Submission
|
||||
include LtiSpecHelper
|
||||
|
||||
specs_require_sharding
|
||||
|
||||
context "locked api item" do
|
||||
let(:item_type) { "assignment" }
|
||||
|
||||
|
@ -103,6 +105,18 @@ describe AssignmentsApiController, type: :request do
|
|||
expect(json.first).to have_key("in_closed_grading_period")
|
||||
end
|
||||
|
||||
it "includes ab_guid in returned json when included[]='ab_guid' is passed" do
|
||||
@course.assignments.create!(title: "Example Assignment")
|
||||
json = api_get_assignments_index_from_course(@course, include: ["ab_guid"])
|
||||
expect(json.first).to have_key("ab_guid")
|
||||
end
|
||||
|
||||
it "does not include ab_guid in returned json when included[]='ab_guid' is not passed" do
|
||||
@course.assignments.create!(title: "Example Assignment")
|
||||
json = api_get_assignments_index_from_course(@course)
|
||||
expect(json.first).not_to have_key("ab_guid")
|
||||
end
|
||||
|
||||
it "includes due_date_required in returned json" do
|
||||
@course.assignments.create!(title: "Example Assignment")
|
||||
json = api_get_assignments_index_from_course(@course)
|
||||
|
@ -285,8 +299,6 @@ describe AssignmentsApiController, type: :request do
|
|||
end
|
||||
|
||||
describe "sharding" do
|
||||
specs_require_sharding
|
||||
|
||||
before do
|
||||
@shard1.activate do
|
||||
account = Account.create!
|
||||
|
@ -3776,6 +3788,71 @@ describe AssignmentsApiController, type: :request do
|
|||
expect(@assignment.submission_types).to eq "not_graded"
|
||||
end
|
||||
|
||||
it "leaves ab_guid alone if not included in update params" do
|
||||
@assignment = @course.assignments.create!(
|
||||
name: "some assignment",
|
||||
points_possible: 15,
|
||||
submission_types: "online_text_entry",
|
||||
grading_type: "percent",
|
||||
ab_guid: ["a", "b"]
|
||||
)
|
||||
api_update_assignment_call(@course, @assignment, title: "new title")
|
||||
expect(response).to be_successful
|
||||
expect(@assignment.reload.ab_guid).to eq ["a", "b"]
|
||||
end
|
||||
|
||||
it "updates ab_guid if included in update params" do
|
||||
@assignment = @course.assignments.create!(
|
||||
name: "some assignment",
|
||||
points_possible: 15,
|
||||
submission_types: "online_text_entry",
|
||||
grading_type: "percent",
|
||||
ab_guid: ["a", "b"]
|
||||
)
|
||||
api_update_assignment_call(@course, @assignment, ab_guid: ["c", "d"])
|
||||
expect(response).to be_successful
|
||||
expect(@assignment.reload.ab_guid).to eq ["c", "d"]
|
||||
end
|
||||
|
||||
it "updates ab_guid to empty array if included in update params and empty" do
|
||||
@assignment = @course.assignments.create!(
|
||||
name: "some assignment",
|
||||
points_possible: 15,
|
||||
submission_types: "online_text_entry",
|
||||
grading_type: "percent",
|
||||
ab_guid: ["a", "b"]
|
||||
)
|
||||
api_update_assignment_call(@course, @assignment, ab_guid: [])
|
||||
expect(response).to be_successful
|
||||
expect(@assignment.reload.ab_guid).to eq []
|
||||
end
|
||||
|
||||
it "updates ab_guid to empty array if included in update params and is empty string" do
|
||||
@assignment = @course.assignments.create!(
|
||||
name: "some assignment",
|
||||
points_possible: 15,
|
||||
submission_types: "online_text_entry",
|
||||
grading_type: "percent",
|
||||
ab_guid: ["a", "b"]
|
||||
)
|
||||
api_update_assignment_call(@course, @assignment, ab_guid: "")
|
||||
expect(response).to be_successful
|
||||
expect(@assignment.reload.ab_guid).to eq []
|
||||
end
|
||||
|
||||
it "updates ab_guid to a single element array if a string is passed in" do
|
||||
@assignment = @course.assignments.create!(
|
||||
name: "some assignment",
|
||||
points_possible: 15,
|
||||
submission_types: "online_text_entry",
|
||||
grading_type: "percent",
|
||||
ab_guid: ["a", "b"]
|
||||
)
|
||||
api_update_assignment_call(@course, @assignment, ab_guid: "c")
|
||||
expect(response).to be_successful
|
||||
expect(@assignment.reload.ab_guid).to eq ["c"]
|
||||
end
|
||||
|
||||
describe "annotatable attachment" do
|
||||
before(:once) do
|
||||
@assignment = @course.assignments.create!(name: "Some Assignment")
|
||||
|
@ -6428,6 +6505,67 @@ describe AssignmentsApiController, type: :request do
|
|||
expect(json).not_to have_key("can_submit")
|
||||
end
|
||||
end
|
||||
|
||||
context "ab_guid" do
|
||||
before do
|
||||
course_with_student_logged_in(course_name: "Course 1", active_all: 1)
|
||||
@course.start_at = 14.days.ago
|
||||
@course.save!
|
||||
@assignment = @course.assignments.create!(title: "Assignment 1",
|
||||
points_possible: 10,
|
||||
submission_types: "online_text_entry",
|
||||
ab_guid: ["1234"])
|
||||
account = Account.default
|
||||
outcome = account.created_learning_outcomes.create!(
|
||||
title: "My Outcome",
|
||||
description: "Description of my outcome",
|
||||
vendor_guid: "vendorguid9000"
|
||||
)
|
||||
rating = [{ id: "rat1",
|
||||
description: "Full Marks",
|
||||
long_description: "Student did a great job.",
|
||||
points: 5.0 }]
|
||||
criteria = [{ id: 1, points: 9000, learning_outcome_id: outcome.id, description: "description", long_description: "long description", ratings: rating }]
|
||||
|
||||
@assignment2 = @course.assignments.create!(title: "Assignment 2")
|
||||
@rubric = @course.rubrics.create!(title: "My Rubric", context: @course, data: criteria)
|
||||
@assignment2.rubric = @rubric
|
||||
@assignment2.save!
|
||||
@assignment2.rubric_association.context = @course
|
||||
@assignment2.rubric_association.save!
|
||||
|
||||
@assignment3 = @course.assignments.create!(title: "Assignment 3")
|
||||
end
|
||||
|
||||
def get_assignment_with_guid(assignment_id)
|
||||
api_call(:get,
|
||||
"/api/v1/courses/#{@course.id}/assignments/#{assignment_id}?include[]=ab_guid",
|
||||
{ controller: "assignments_api",
|
||||
action: "show",
|
||||
format: "json",
|
||||
course_id: @course.id.to_s,
|
||||
id: assignment_id,
|
||||
include: ["ab_guid"] })
|
||||
end
|
||||
|
||||
it "returns ab_guid when it is included in include param" do
|
||||
json = get_assignment_with_guid(@assignment.id)
|
||||
expect(json).to have_key("ab_guid")
|
||||
expect(json["ab_guid"]).to eq(["1234"])
|
||||
end
|
||||
|
||||
it "returns vendor_id through rubrics if no ab_guid is present" do
|
||||
json = get_assignment_with_guid(@assignment2.id)
|
||||
expect(json).to have_key("ab_guid")
|
||||
expect(json["ab_guid"]).to eq(["vendorguid9000"])
|
||||
end
|
||||
|
||||
it "returns an empty array if ab_guid is requested and none exists on assignment or through rubric" do
|
||||
json = get_assignment_with_guid(@assignment3.id)
|
||||
expect(json).to have_key("ab_guid")
|
||||
expect(json["ab_guid"]).to eq([])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "update_from_params" do
|
||||
|
|
Loading…
Reference in New Issue