<commit_before>#
# Copyright (C) 2011 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 AssessmentQuestion < ActiveRecord::Base
  include Workflow
  attr_accessible :name, :question_data, :form_question_data

  has_many :quiz_questions, :class_name => 'Quizzes::QuizQuestion'
  has_many :attachments, :as => :context
  delegate :context, :context_id, :context_type, :to => :assessment_question_bank
  attr_accessor :initial_context
  belongs_to :assessment_question_bank, :touch => true
  simply_versioned :automatic => false
  acts_as_list :scope => :assessment_question_bank
  before_validation :infer_defaults
  after_save :translate_links_if_changed
  validates_length_of :name, :maximum => maximum_string_length, :allow_nil => true
  validates_presence_of :workflow_state, :assessment_question_bank_id

  ALL_QUESTION_TYPES = ["multiple_answers_question", "fill_in_multiple_blanks_question",
                        "matching_question", "missing_word_question",
                        "multiple_choice_question", "numerical_question",
                        "text_only_question", "short_answer_question",
                        "multiple_dropdowns_question", "calculated_question",
                        "essay_question", "true_false_question", "file_upload_question"]

  serialize_utf8_safe :question_data

  set_policy do
    given{|user, session| self.context.grants_right?(user, session, :manage_assignments) }
    can :read and can :create and can :update and can :delete
  end

  def infer_defaults
    self.question_data ||= HashWithIndifferentAccess.new
    if self.question_data.is_a?(Hash)
      if self.question_data[:question_name].try(:strip).blank?
        self.question_data[:question_name] = t :default_question_name, "Question"
      end
      self.question_data[:name] = self.question_data[:question_name]
    end
    self.name = self.question_data[:question_name] || self.name
    self.assessment_question_bank ||= AssessmentQuestionBank.unfiled_for_context(self.initial_context)
  end

  def translate_links_if_changed
    # this has to be in an after_save, because translate_links may create attachments
    # with this question as the context, and if this question does not exist yet,
    # creating that attachment will fail.
    translate_links if self.question_data_changed? && !@skip_translate_links
  end

  def self.translate_links(ids)
    ids.each do |aqid|
      if aq = AssessmentQuestion.find(aqid)
        aq.translate_links
      end
    end
  end

  def translate_link_regex
    @regex ||= Regexp.new(%{/#{context_type.downcase.pluralize}/#{context_id}/(?:files/(\\d+)/(?:download|preview)|file_contents/(course%20files/[^'"?]*))(?:\\?([^'"]*))?})
  end

  def file_substitutions
    @file_substitutions ||= {}
  end

  def translate_file_link(link, match_data=nil)
    match_data ||= link.match(translate_link_regex)
    return link unless match_data

    id = match_data[1]
    path = match_data[2]
    id_or_path = id || path

    if !file_substitutions[id_or_path]
      if id
        file = Attachment.where(context_type: context_type, context_id: context_id, id: id_or_path).first
      elsif path
        path = URI.unescape(id_or_path)
        file = Folder.find_attachment_in_context_with_path(assessment_question_bank.context, path)
      end
      begin
        new_file = file.clone_for(self)
      rescue => e
        new_file = nil
        er_id = Canvas::Errors.capture_exception(:file_clone_during_translate_links, e)[:error_report]
        logger.error("Error while cloning attachment during"\
                           " AssessmentQuestion#translate_links: "\
                           "id: #{self.id} error_report: #{er_id}")
      end
      new_file.save if new_file
      file_substitutions[id_or_path] = new_file
    end
    if sub = file_substitutions[id_or_path]
      query_rest = match_data[3] ? "&#{match_data[3]}" : ''
      "/assessment_questions/#{self.id}/files/#{sub.id}/download?verifier=#{sub.uuid}#{query_rest}"
    else
      link
    end
  end

  def translate_links
    # we can't translate links unless this question has a context (through a bank)<commit_msg>don't raise/record an error when a file link can't be found<commit_after>#
# Copyright (C) 2011 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 AssessmentQuestion < ActiveRecord::Base
  include Workflow
  attr_accessible :name, :question_data, :form_question_data

  has_many :quiz_questions, :class_name => 'Quizzes::QuizQuestion'
  has_many :attachments, :as => :context
  delegate :context, :context_id, :context_type, :to => :assessment_question_bank
  attr_accessor :initial_context
  belongs_to :assessment_question_bank, :touch => true
  simply_versioned :automatic => false
  acts_as_list :scope => :assessment_question_bank
  before_validation :infer_defaults
  after_save :translate_links_if_changed
  validates_length_of :name, :maximum => maximum_string_length, :allow_nil => true
  validates_presence_of :workflow_state, :assessment_question_bank_id

  ALL_QUESTION_TYPES = ["multiple_answers_question", "fill_in_multiple_blanks_question",
                        "matching_question", "missing_word_question",
                        "multiple_choice_question", "numerical_question",
                        "text_only_question", "short_answer_question",
                        "multiple_dropdowns_question", "calculated_question",
                        "essay_question", "true_false_question", "file_upload_question"]

  serialize_utf8_safe :question_data

  set_policy do
    given{|user, session| self.context.grants_right?(user, session, :manage_assignments) }
    can :read and can :create and can :update and can :delete
  end

  def infer_defaults
    self.question_data ||= HashWithIndifferentAccess.new
    if self.question_data.is_a?(Hash)
      if self.question_data[:question_name].try(:strip).blank?
        self.question_data[:question_name] = t :default_question_name, "Question"
      end
      self.question_data[:name] = self.question_data[:question_name]
    end
    self.name = self.question_data[:question_name] || self.name
    self.assessment_question_bank ||= AssessmentQuestionBank.unfiled_for_context(self.initial_context)
  end

  def translate_links_if_changed
    # this has to be in an after_save, because translate_links may create attachments
    # with this question as the context, and if this question does not exist yet,
    # creating that attachment will fail.
    translate_links if self.question_data_changed? && !@skip_translate_links
  end

  def self.translate_links(ids)
    ids.each do |aqid|
      if aq = AssessmentQuestion.find(aqid)
        aq.translate_links
      end
    end
  end

  def translate_link_regex
    @regex ||= Regexp.new(%{/#{context_type.downcase.pluralize}/#{context_id}/(?:files/(\\d+)/(?:download|preview)|file_contents/(course%20files/[^'"?]*))(?:\\?([^'"]*))?})
  end

  def file_substitutions
    @file_substitutions ||= {}
  end

  def translate_file_link(link, match_data=nil)
    match_data ||= link.match(translate_link_regex)
    return link unless match_data

    id = match_data[1]
    path = match_data[2]
    id_or_path = id || path

    if !file_substitutions[id_or_path]
      if id
        file = Attachment.where(context_type: context_type, context_id: context_id, id: id_or_path).first
      elsif path
        path = URI.unescape(id_or_path)
        file = Folder.find_attachment_in_context_with_path(assessment_question_bank.context, path)
      end
      begin
        new_file = file.try(:clone_for, self)
      rescue => e
        new_file = nil
        er_id = Canvas::Errors.capture_exception(:file_clone_during_translate_links, e)[:error_report]
        logger.error("Error while cloning attachment during"\
                           " AssessmentQuestion#translate_links: "\
                           "id: #{self.id} error_report: #{er_id}")
      end
      new_file.save if new_file
      file_substitutions[id_or_path] = new_file
    end
    if sub = file_substitutions[id_or_path]
      query_rest = match_data[3] ? "&#{match_data[3]}" : ''
      "/assessment_questions/#{self.id}/files/#{sub.id}/download?verifier=#{sub.uuid}#{query_rest}"
    else
      link
    end
  end

  def translate_links
    # we can't translate links unless this question has a context (through a bank)
{'fraction_non_alphanumeric': 0.09409671341846347, 'fraction_numerical': 0.0025120368432070338, 'mean_word_length': 3.6999508116084603, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4648030', 'n_tokens_mistral': 2951, 'n_tokens_neox': 2855, 'n_words': 722}