diff --git a/app/controllers/assignments_api_controller.rb b/app/controllers/assignments_api_controller.rb index 7fa60da5edf..b5c2731cca0 100644 --- a/app/controllers/assignments_api_controller.rb +++ b/app/controllers/assignments_api_controller.rb @@ -61,6 +61,29 @@ # // Turnitin plugin available # turnitin_enabled: true, # +# // Settings to pass along to turnitin to control what kinds of matches +# // should be considered. +# // originality_report_visibility can be 'immediate', 'after_grading', or 'after_due_date' +# // exclude_small_matches_type can be null, 'percent', 'words' +# // exclude_small_matches_value: +# // - if type is null, this will be null also +# // - if type is 'percent', this will be a number between 0 and 100 +# // representing match size to exclude as a percentage of the document size. +# // - if type is 'words', this will be number > 0 representing how many +# // words a match must contain for it to be considered +# // NOTE: This flag will not appear unless your account has the +# // Turnitin plugin available +# turnitin_settings: { +# originality_report_visibility => 'after_grading', +# s_paper_check => false, +# internet_check => false, +# journal_check => false, +# exclude_biblio => false, +# exclude_quoted => false, +# exclude_small_matches_type => 'percent', +# exclude_small_matches_value => 50, +# }, +# # // If this is a group assignment, boolean flag indicating whether or # // not students will be graded individually. # grade_group_students_individually: false, @@ -71,9 +94,9 @@ # // Use the "External Tools" API if you need more information about # // an external tool. # external_tool_tag_attributes: { -# // URL to the external tool \ +# // URL to the external tool # url: "http://instructure.com", -# // Whether or not there is a new tab for the external tool\ +# // Whether or not there is a new tab for the external tool # new_tab: false # }, # @@ -269,6 +292,10 @@ class AssignmentsApiController < ApplicationController # Toggles Turnitin submissions for the assignment. # Will be ignored if Turnitin is not available for the course. # + # @argument assignment[turnitin_settings] [Optional] + # Settings to send along to turnitin. See Assignment object definition for + # format. + # # @argument assignment[peer_reviews] [Optional,Boolean] # If submission_types does not include external_tool,discussion_topic, # online_quiz, or on_paper, determines whether or not peer reviews diff --git a/app/models/account.rb b/app/models/account.rb index 1279f48a4a8..86b65538d89 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -871,7 +871,7 @@ class Account < ActiveRecord::Base end def turnitin_settings - if self.turnitin_account_id && self.turnitin_shared_secret && !self.turnitin_account_id.empty? && !self.turnitin_shared_secret.empty? + if self.turnitin_account_id.present? && self.turnitin_shared_secret.present? [self.turnitin_account_id, self.turnitin_shared_secret] else self.parent_account.turnitin_settings rescue nil diff --git a/app/models/assignment.rb b/app/models/assignment.rb index 6daf8ac427c..50a146d16b2 100644 --- a/app/models/assignment.rb +++ b/app/models/assignment.rb @@ -312,10 +312,6 @@ class Assignment < ActiveRecord::Base end end - def turnitin_enabled? - self.turnitin_enabled - end - attr_accessor :updated_submissions def update_submissions_later if @old_assignment_group_id != self.assignment_group_id diff --git a/lib/api/v1/assignment.rb b/lib/api/v1/assignment.rb index 06d2fd70e6d..8d58fd36b92 100644 --- a/lib/api/v1/assignment.rb +++ b/lib/api/v1/assignment.rb @@ -71,6 +71,7 @@ module Api::V1::Assignment if assignment.context && assignment.context.turnitin_enabled? hash['turnitin_enabled'] = assignment.turnitin_enabled + hash['turnitin_settings'] = turnitin_settings_json(assignment) end if assignment.rubric_association @@ -108,6 +109,25 @@ module Api::V1::Assignment hash end + def turnitin_settings_json(assignment) + settings = assignment.turnitin_settings.with_indifferent_access + [:s_paper_check, :internet_check, :journal_check, :exclude_biblio, :exclude_quoted].each do |key| + settings[key] = value_to_boolean(settings[key]) + end + + ex_type = settings.delete(:exclude_type) + settings[:exclude_small_matches_type] = case ex_type + when '0'; nil + when '1'; 'words' + when '2'; 'percent' + end + + ex_value = settings.delete(:exclude_value) + settings[:exclude_small_matches_value] = ex_value.present? ? ex_value.to_i : nil + + settings.slice(*API_ALLOWED_TURNITIN_SETTINGS) + end + API_ALLOWED_ASSIGNMENT_INPUT_FIELDS = %w( name description @@ -127,6 +147,18 @@ module Api::V1::Assignment grade_group_students_individually set_custom_field_values turnitin_enabled + turnitin_settings + ) + + API_ALLOWED_TURNITIN_SETTINGS = %w( + originality_report_visibility + s_paper_check + internet_check + journal_check + exclude_biblio + exclude_quoted + exclude_small_matches_type + exclude_small_matches_value ) def update_api_assignment(assignment, assignment_params, save = true) @@ -137,24 +169,44 @@ module Api::V1::Assignment update_params["submission_types"] = update_params["submission_types"].join(',') end - if assignment_params.has_key?("assignment_group_id") - ag_id = assignment_params["assignment_group_id"] - if ag_id.nil? || ag_id.is_a?( Numeric ) - assignment.assignment_group = assignment.context.assignment_groups.find_by_id(ag_id) - end + # validate and add to update_params + if update_params.has_key?("assignment_group_id") + ag_id = update_params.delete("assignment_group_id").presence + ag = assignment.context.assignment_groups.find_by_id(ag_id) + update_params["assignment_group_id"] = ag.try(:id) end - if assignment_params.has_key?("group_category_id") - gc_id = assignment_params["group_category_id"] - if gc_id.nil? || gc_id.is_a?( Numeric ) - assignment.group_category = assignment.context.group_categories.find_by_id(gc_id) - end + # validate and add to update_params + if update_params.has_key?("group_category_id") + gc_id = update_params["group_category_id"].presence + gc = assignment.context.group_categories.find_by_id(gc_id) + update_params["group_category_id"] = gc.try(:id) end + # do some fiddling with due_at for fancy midnight and add to update_params if update_params.has_key?("due_at") update_params["time_zone_edited"] = Time.zone.name - # do some fiddling with due_at for fancy midnight assignment.due_at = update_params["due_at"] + update_params["due_at"] = assignment.due_at + end + + if !assignment.context.try(:turnitin_enabled?) + update_params.delete("turnitin_enabled") + update_params.delete("turnitin_settings") + end + + # use Assignment#turnitin_settings= to normalize, but then assign back to + # hash so that it is written with update_params + if update_params.has_key?("turnitin_settings") + turnitin_settings = update_params["turnitin_settings"].slice(*API_ALLOWED_TURNITIN_SETTINGS) + turnitin_settings['exclude_type'] = case turnitin_settings['exclude_small_matches_type'] + when nil; '0' + when 'words'; '1' + when 'percent'; '2' + end + turnitin_settings['exclude_value'] = turnitin_settings['exclude_small_matches_value'] + assignment.turnitin_settings = turnitin_settings + update_params["turnitin_settings"] = assignment.turnitin_settings end # TODO: allow rubric creation diff --git a/lib/turnitin.rb b/lib/turnitin.rb index f3414f34e08..816cb44a891 100644 --- a/lib/turnitin.rb +++ b/lib/turnitin.rb @@ -120,7 +120,8 @@ module Turnitin settings[:originality_report_visibility] = 'immediate' unless ['immediate', 'after_grading', 'after_due_date'].include?(settings[:originality_report_visibility]) [:s_paper_check, :internet_check, :journal_check, :exclude_biblio, :exclude_quoted].each do |key| - settings[key] = '0' unless settings[key] == '1' + bool = Canvas::Plugin.value_to_boolean(settings[key]) + settings[key] = bool ? '1' : '0' end exclude_value = settings[:exclude_value].to_i diff --git a/spec/apis/v1/assignments_api_spec.rb b/spec/apis/v1/assignments_api_spec.rb index 14b23bc2e4c..dec5943d3f1 100644 --- a/spec/apis/v1/assignments_api_spec.rb +++ b/spec/apis/v1/assignments_api_spec.rb @@ -195,10 +195,19 @@ describe AssignmentsApiController, :type => :integration do @json['position'].should == 1 @json['group_category_id'].should == @group_category.id @json['turnitin_enabled'].should == true + @json['turnitin_settings'].should == { + 'originality_report_visibility' => 'immediate', + 's_paper_check' => true, + 'internet_check' => true, + 'journal_check' => true, + 'exclude_biblio' => true, + 'exclude_quoted' => true, + 'exclude_small_matches_type' => nil, + 'exclude_small_matches_value' => nil + } @json['allowed_extensions'].should =~ [ 'docx','ppt' ] - @json['turnitin_enabled'].should == true @json['points_possible'].should == 12 @json['grading_type'].should == 'points' @json['due_at'].should == @assignment.due_at.iso8601 @@ -341,10 +350,10 @@ describe AssignmentsApiController, :type => :integration do 'points_possible' => '12', 'assignment_group_id' => @group.id, 'set_custom_field_values' => { - 'test_custom' => { - 'value' => '1' - } - }, + 'test_custom' => { + 'value' => '1' + } + }, 'group_category_id' => nil, 'description' => 'assignment description', 'grading_type' => 'points', @@ -428,7 +437,6 @@ describe AssignmentsApiController, :type => :integration do it "updates custom fields" do @assignment.get_custom_field_value('test_custom').true?.should == true end - end context "when updating assignment overrides on the assignment" do @@ -480,6 +488,78 @@ describe AssignmentsApiController, :type => :integration do end end + context "when turnitin is enabled on the context" do + before do + course_with_teacher(:active_all => true) + @assignment = @course.assignments.create! + acct = @course.account + acct.turnitin_account_id = 0 + acct.turnitin_shared_secret = "blah" + acct.save! + end + + it "should allow setting turnitin_enabled" do + @assignment.should_not be_turnitin_enabled + api_update_assignment_call(@course,@assignment,{ + 'turnitin_enabled' => '1', + }) + @assignment.reload.should be_turnitin_enabled + api_update_assignment_call(@course,@assignment,{ + 'turnitin_enabled' => '0', + }) + @assignment.reload.should_not be_turnitin_enabled + end + + it "should allow setting valid turnitin_settings" do + update_settings = { + :originality_report_visibility => 'after_grading', + :s_paper_check => '0', + :internet_check => false, + :journal_check => '1', + :exclude_biblio => true, + :exclude_quoted => '0', + :exclude_small_matches_type => 'percent', + :exclude_small_matches_value => 50 + } + + json = api_update_assignment_call(@course, @assignment, { + :turnitin_settings => update_settings + }) + json["turnitin_settings"].should == { + 'originality_report_visibility' => 'after_grading', + 's_paper_check' => false, + 'internet_check' => false, + 'journal_check' => true, + 'exclude_biblio' => true, + 'exclude_quoted' => false, + 'exclude_small_matches_type' => 'percent', + 'exclude_small_matches_value' => 50 + } + + @assignment.reload.turnitin_settings.should == { + 'originality_report_visibility' => 'after_grading', + 's_paper_check' => '0', + 'internet_check' => '0', + 'journal_check' => '1', + 'exclude_biblio' => '1', + 'exclude_quoted' => '0', + 'exclude_type' => '2', + 'exclude_value' => '50' + } + end + + it "should not allow setting invalid turnitin_settings" do + update_settings = { + :blah => '1' + }.with_indifferent_access + + api_update_assignment_call(@course, @assignment, { + :turnitin_settings => update_settings + }) + @assignment.reload.turnitin_settings["blah"].should be_nil + end + end + context "when a non-admin tries to update a completely frozen assignment" do it "doesn't allow the non-admin to update the frozen assignment" do course_with_teacher(:active_all => true) diff --git a/spec/models/assignment_spec.rb b/spec/models/assignment_spec.rb index 8324fd69833..b3e87471640 100644 --- a/spec/models/assignment_spec.rb +++ b/spec/models/assignment_spec.rb @@ -1960,20 +1960,20 @@ describe Assignment do assignment.turnitin_settings = { :originality_report_visibility => 'invalid', :s_paper_check => '2', - :internet_check => '2', - :journal_check => '2', - :exclude_biblio => '2', - :exclude_quoted => '2', + :internet_check => 1, + :journal_check => 0, + :exclude_biblio => true, + :exclude_quoted => false, :exclude_type => '3', :exclude_value => 'asdf', :bogus => 'haha' } assignment.turnitin_settings.should eql({ :originality_report_visibility => 'immediate', - :s_paper_check => '0', - :internet_check => '0', + :s_paper_check => '1', + :internet_check => '1', :journal_check => '0', - :exclude_biblio => '0', + :exclude_biblio => '1', :exclude_quoted => '0', :exclude_type => '0', :exclude_value => ''