<commit_before>class Form < ActiveRecord::Base
  include MissionBased, FormVersionable, Replication::Standardizable, Replication::Replicable

  API_ACCESS_LEVELS = %w(private public)

  has_many(:responses, inverse_of: :form)
  has_many(:versions, class_name: "FormVersion", inverse_of: :form, dependent: :destroy)
  has_many(:whitelistings, as: :whitelistable, class_name: "Whitelisting", dependent: :destroy)
  has_many(:standard_form_reports, class_name: "Report::StandardFormReport", dependent: :destroy)
  has_many(:form_forwardings)
  has_many(:forwardee_users, through: :form_forwardings, as: :forwardees, source: :forwardee, source_type: "User")
  has_many(:forwardee_user_groups, through: :form_forwardings, as: :forwardees, source: :forwardee, source_type: "UserGroup")

  # while a form has many versions, this is a reference to the most up-to-date one
  belongs_to(:current_version, class_name: "FormVersion")

  # For some reason dependent: :destroy doesn't work with this assoc.
  belongs_to :root_group, autosave: true, class_name: "QingGroup", foreign_key: :root_id

  before_validation(:normalize_fields)
  before_save(:update_pub_changed_at)

  # For some reason this works but dependent: :destroy doesn't.
  before_destroy { root_group.destroy }

  validates(:name, presence: true, length: {maximum: 32})
  validate(:name_unique_per_mission)

  before_create(:init_downloads)

  scope(:published, -> { where(published: true) })

  # this scope adds a count of the questionings on this form and
  # the number of copies of this form, and of those that are published
  # if the form is not a standard, these will just be zero
  scope(:with_questioning_and_copy_counts, -> { select(%{
      forms.*,
      COUNT(DISTINCT form_items.id) AS questionings_count_col,
      COUNT(DISTINCT copies.id) AS copy_count_col,
      SUM(copies.published) AS published_copy_count_col,
      SUM(copies.responses_count) AS copy_responses_count_col
    })
    .joins(%{
      LEFT OUTER JOIN form_items ON forms.id = form_items.form_id AND form_items.type = 'Questioning'
      LEFT OUTER JOIN forms copies ON forms.id = copies.original_id AND copies.standard_copy = 1
    })
    .group("forms.id") })

  scope(:by_name, -> { order("forms.name") })
  scope(:default_order, -> { by_name })

  delegate :arrange_descendants,
    :children,
    :c,
    :descendants,
    :child_groups,
    to: :root_group

  delegate :code, to: :current_version

  replicable child_assocs: :root_group, uniqueness: {field: :name, style: :sep_words},
    dont_copy: [:published, :pub_changed_at, :downloads, :responses_count, :upgrade_needed,
      :smsable, :current_version_id, :allow_incomplete, :access_level, :root_id]


  # remove heirarchy of objects
  def self.terminate_sub_relationships(form_ids)
    FormVersion.where(form_id: form_ids).delete_all
    Questioning.where(form_id: form_ids).delete_all
  end

  # Gets a cache key based on the mission and the max (latest) pub_changed_at value.
  def self.odk_index_cache_key(options)
    # Note that since we're using maximum method, dates don't seem to be TZ adjusted on load, which is fine as long as it's consistent.
    max_pub_changed_at = if for_mission(options[:mission]).published.any?
      for_mission(options[:mission]).maximum(:pub_changed_at).utc.to_s(:cache_datetime)
    else
      'no-pubd-forms'
    end
    "odk-form-list/mission-#{options[:mission].id}/#{max_pub_changed_at}"
  end

  def add_questions_to_top_level(questions)
    Array.wrap(questions).each_with_index do |q, i|
      Questioning.create!(mission: mission, form: self, question: q, parent: root_group)
    end
  end

  def root_questionings(reload = false)
    # Not memoizing this because it causes all sorts of problems.
    root_group ? root_group.children.order(:rank).reject{ |q| q.is_a?(QingGroup) } : []
  end

  def forwardees
    [forwardee_users, forwardee_user_groups].flatten
  end

  def sms_forwardees
    forwardees.map { |fwd| BroadcastRecipient.new(object: fwd) }
  end

  def sms_forwardee_ids
    sms_forwardees.map(&:id)
  end

  def sms_forwardee_ids=(ids)
    user_list = []
    group_list = []
    ids.each do |id_string|
      type, _, id = id_string.rpartition("_")
      case type
      when "user"
        user_list << id
      when "user_group"
        group_list << id
      end
    end
    self.forwardee_users = User.where(id: user_list)
    self.forwardee_user_groups = UserGroup.where(id: group_list)
    self.save! unless self.new_record?
  end

  def forwardees_as_users
    user_list = []
    self.forwardees.each do |fwd|
      case fwd.class.to_s
      when "User"
        user_list << fwd
      when "UserGroup"
        fwd.users.each do |u|
          user_list << u
        end
      end
    end
    user_list
  end

  def forwardees=(forwardees)
    forwardees.each do |fwd|
      case fwd.class.to_s
      when "User"
        forwardee_users << fwd
      when "UserGroup"
        forwardee_user_groups << fwd
      end
    end
  end

  def odk_download_cache_key
    "odk-form/#{id}-#{pub_changed_at}"
  end

  def api_user_id_can_see?(api_user_id)
    access_level == "public" || access_level == "protected" &&
      whitelistings.pluck(:user_id).include?(api_user_id)
  end

  def api_visible_questions
    questions.select{ |q| q.access_level == "inherit" }
  end

  def temp_response_id
    "#{name}_#{ActiveSupport::SecureRandom.random_number(899999999) + 100000000}"
  end

  def version
    current_version.try(:sequence) || ""
  end

  def version_with_code
    current_version.try(:sequence_and_code) || ""
  end

  def has_questions?
    root_questionings.any?
  end

  def full_name
    # this used to include the form type, but for now it's just name
    name
  end

  # current override code for incomplete responses
  def override_code
    mission.override_code
  end<commit_msg>4929: fixes missing links<commit_after>class Form < ActiveRecord::Base
  include MissionBased, FormVersionable, Replication::Standardizable, Replication::Replicable

  API_ACCESS_LEVELS = %w(private public)

  has_many(:responses, inverse_of: :form)
  has_many(:versions, class_name: "FormVersion", inverse_of: :form, dependent: :destroy)
  has_many(:whitelistings, as: :whitelistable, class_name: "Whitelisting", dependent: :destroy)
  has_many(:standard_form_reports, class_name: "Report::StandardFormReport", dependent: :destroy)
  has_many(:form_forwardings)
  has_many(:forwardee_users, through: :form_forwardings, as: :forwardees, source: :forwardee, source_type: "User")
  has_many(:forwardee_user_groups, through: :form_forwardings, as: :forwardees, source: :forwardee, source_type: "UserGroup")

  # while a form has many versions, this is a reference to the most up-to-date one
  belongs_to(:current_version, class_name: "FormVersion")

  # For some reason dependent: :destroy doesn't work with this assoc.
  belongs_to :root_group, autosave: true, class_name: "QingGroup", foreign_key: :root_id

  before_validation(:normalize_fields)
  before_save(:update_pub_changed_at)

  # For some reason this works but dependent: :destroy doesn't.
  before_destroy { root_group.destroy }

  validates(:name, presence: true, length: {maximum: 32})
  validate(:name_unique_per_mission)

  before_create(:init_downloads)

  scope(:published, -> { where(published: true) })

  # this scope adds a count of the questionings on this form and
  # the number of copies of this form, and of those that are published
  # if the form is not a standard, these will just be zero
  scope(:with_questioning_and_copy_counts, -> { select(%{
      forms.*,
      COUNT(DISTINCT form_items.id) AS questionings_count_col,
      COUNT(DISTINCT copies.id) AS copy_count_col,
      SUM(copies.published) AS published_copy_count_col,
      SUM(copies.responses_count) AS copy_responses_count_col
    })
    .joins(%{
      LEFT OUTER JOIN form_items ON forms.id = form_items.form_id AND form_items.type = 'Questioning'
      LEFT OUTER JOIN forms copies ON forms.id = copies.original_id AND copies.standard_copy = 1
    })
    .group("forms.id") })

  scope(:by_name, -> { order("forms.name") })
  scope(:default_order, -> { by_name })

  delegate :arrange_descendants,
    :children,
    :c,
    :descendants,
    :child_groups,
    to: :root_group

  delegate :code, to: :current_version

  replicable child_assocs: :root_group, uniqueness: {field: :name, style: :sep_words},
    dont_copy: [:published, :pub_changed_at, :downloads, :responses_count, :upgrade_needed,
      :smsable, :current_version_id, :allow_incomplete, :access_level, :root_id]


  # remove heirarchy of objects
  def self.terminate_sub_relationships(form_ids)
    FormVersion.where(form_id: form_ids).delete_all
    Questioning.where(form_id: form_ids).delete_all
  end

  # Gets a cache key based on the mission and the max (latest) pub_changed_at value.
  def self.odk_index_cache_key(options)
    # Note that since we're using maximum method, dates don't seem to be TZ adjusted on load, which is fine as long as it's consistent.
    max_pub_changed_at = if for_mission(options[:mission]).published.any?
      for_mission(options[:mission]).maximum(:pub_changed_at).utc.to_s(:cache_datetime)
    else
      'no-pubd-forms'
    end
    "odk-form-list/mission-#{options[:mission].id}/#{max_pub_changed_at}"
  end

  def add_questions_to_top_level(questions)
    Array.wrap(questions).each_with_index do |q, i|
      Questioning.create!(mission: mission, form: self, question: q, parent: root_group)
    end
  end

  def root_questionings(reload = false)
    # Not memoizing this because it causes all sorts of problems.
    root_group ? root_group.children.order(:rank).reject{ |q| q.is_a?(QingGroup) } : []
  end

  def forwardees
    [forwardee_users, forwardee_user_groups].flatten
  end

  def sms_forwardees
    forwardees.map { |fwd| BroadcastRecipient.new(object: fwd) }
  end

  def sms_forwardee_ids
    sms_forwardees.map(&:id)
  end

  def sms_forwardee_ids=(ids)
    user_list = []
    group_list = []
    ids.each do |id_string|
      type, _, id = id_string.rpartition("_")
      case type
      when "user"
        user_list << id
      when "user_group"
        group_list << id
      end
    end
    self.forwardee_users = User.where(id: user_list)
    self.forwardee_user_groups = UserGroup.where(id: group_list)
    self.save! unless self.new_record?
  end

  def forwardees_as_users
    user_list = []
    self.forwardees.each do |fwd|
      case fwd.class.to_s
      when "User"
        user_list << fwd
      when "UserGroup"
        fwd.users.each do |u|
          user_list << u
        end
      end
    end
    user_list
  end

  def forwardees=(forwardees)
    forwardees.each do |fwd|
      case fwd.class.to_s
      when "User"
        forwardee_users << fwd
      when "UserGroup"
        forwardee_user_groups << fwd
      end
    end
  end

  def odk_download_cache_key
    "odk-form/#{id}-#{pub_changed_at}"
  end

  def api_user_id_can_see?(api_user_id)
    access_level == "public" || access_level == "protected" &&
      whitelistings.pluck(:user_id).include?(api_user_id)
  end

  def api_visible_questions
    questions.select{ |q| q.access_level == "inherit" }
  end

  def temp_response_id
    "#{name}_#{ActiveSupport::SecureRandom.random_number(899999999) + 100000000}"
  end

  def version
    current_version.try(:sequence) || ""
  end

  def version_with_code
    current_version.try(:sequence_and_code) || ""
  end

  def has_questions?
    questionings.any?
  end

  def full_name
    # this used to include the form type, but for now it's just name
    name
  end

  # current override code for incomplete responses
  def override_code
    mission.override_code
  end
{'fraction_non_alphanumeric': 0.10072494669509595, 'fraction_numerical': 0.003923240938166312, 'mean_word_length': 3.8574979287489644, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 4, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3902906', 'n_tokens_mistral': 4089, 'n_tokens_neox': 3841, 'n_words': 944}