<commit_before>require 'fileutils' # FileUtils used here

# Handle for getting student submissions.  Actual instance depend
# on whether an assignment is a group or individual assignment.
class Submission < ActiveRecord::Base
  after_create :create_result
  before_validation :bump_old_submissions, on: :create

  validates_numericality_of :submission_version, only_integer: true
  validate :max_number_of_results
  belongs_to :grouping

  has_many   :results, -> { order :created_at },
             dependent: :destroy

  has_many   :non_pr_results, -> { where(peer_review_id: nil).order(:created_at) },
             class_name: 'Result'

  has_one    :current_result, -> { where(peer_review_id: nil).order(created_at: :desc) },
             class_name: 'Result'

  has_one    :submitted_remark, -> { where.not remark_request_submitted_at: nil },
             class_name: 'Result'

  has_many   :submission_files, dependent: :destroy
  has_many   :annotations, through: :submission_files
  has_many   :test_script_results,
             -> { order 'created_at DESC' },
             dependent: :destroy
  has_many   :test_script_results_all_data,
             -> { includes(:test_script, :test_results, :requested_by).order('created_at DESC') },
             class_name: 'TestScriptResult'
  has_many   :feedback_files, dependent: :destroy


  def self.create_by_timestamp(grouping, timestamp)
     unless timestamp.kind_of? Time
       raise 'Expected a timestamp of type Time'
     end
     repo = grouping.group.repo
     path = grouping.assignment.repository_folder
     revision = repo.get_revision_by_timestamp(timestamp, path)
     submission = self.generate_new_submission(grouping, revision)
     repo.close
     submission
  end

  def self.create_by_revision_identifier(grouping, revision_identifier)
    repo = grouping.group.repo
    revision = repo.get_revision(revision_identifier)
    submission = self.generate_new_submission(grouping, revision)
    repo.close
    submission
  end

  def self.generate_new_submission(grouping, revision)
    new_submission = Submission.new
    new_submission.grouping = grouping
    new_submission.submission_version = 1
    new_submission.submission_version_used = true
    new_submission.revision_timestamp = revision.timestamp
    new_submission.revision_identifier = revision.revision_identifier
    new_submission.transaction do
      begin
        new_submission.populate_with_submission_files(revision)
      rescue Repository::FileDoesNotExist
        # populate the submission with no files instead of raising an exception
      end
      new_submission.save
    end
    new_submission
  end

  # Returns the original result.
  def get_original_result
    non_pr_results.first
  end

  def remark_result
    if remark_request_timestamp.nil? || non_pr_results.length < 2
      nil
    else
      non_pr_results.last
    end
  end

  def remark_result_id
    remark_result.try(:id)
  end

  # Returns the latest result.
  def get_latest_result
    if !submitted_remark.nil?
      remark_result
    else
      get_original_result
    end
  end

  # Returns the latest completed result.
  def get_latest_completed_result
    if remark_submitted? &&
       remark_result.marking_state == Result::MARKING_STATES[:complete]
      remark_result
    elsif get_original_result.marking_state == Result::MARKING_STATES[:complete]
      get_original_result
    else
      nil
    end
  end

  # Sets marks when automated tests are run
  def set_marks_for_tests
    return if test_script_results.empty?

    result = get_latest_result
    complete_marks = true
    if result.marks.empty? # can happen if a criterion is created after collection
      result.create_marks
    end
    result.marks.each do |mark|
      all_marks_earned = 0.0
      all_marks_total = 0.0
      mark.markable.test_scripts.each do |test_script|
        res = test_script_results.where(test_script_id: test_script.id).first
        all_marks_earned += res.marks_earned
        unless all_marks_total.nil?<commit_msg>autotest: Fix n+1 queries in Submission#set_marks_for_tests<commit_after>require 'fileutils' # FileUtils used here

# Handle for getting student submissions.  Actual instance depend
# on whether an assignment is a group or individual assignment.
class Submission < ActiveRecord::Base
  after_create :create_result
  before_validation :bump_old_submissions, on: :create

  validates_numericality_of :submission_version, only_integer: true
  validate :max_number_of_results
  belongs_to :grouping

  has_many   :results, -> { order :created_at },
             dependent: :destroy

  has_many   :non_pr_results, -> { where(peer_review_id: nil).order(:created_at) },
             class_name: 'Result'

  has_one    :current_result, -> { where(peer_review_id: nil).order(created_at: :desc) },
             class_name: 'Result'

  has_one    :submitted_remark, -> { where.not remark_request_submitted_at: nil },
             class_name: 'Result'

  has_many   :submission_files, dependent: :destroy
  has_many   :annotations, through: :submission_files
  has_many   :test_script_results,
             -> { order 'created_at DESC' },
             dependent: :destroy
  has_many   :test_script_results_all_data,
             -> { includes(:test_script, :test_results, :requested_by).order('created_at DESC') },
             class_name: 'TestScriptResult'
  has_many   :feedback_files, dependent: :destroy


  def self.create_by_timestamp(grouping, timestamp)
     unless timestamp.kind_of? Time
       raise 'Expected a timestamp of type Time'
     end
     repo = grouping.group.repo
     path = grouping.assignment.repository_folder
     revision = repo.get_revision_by_timestamp(timestamp, path)
     submission = self.generate_new_submission(grouping, revision)
     repo.close
     submission
  end

  def self.create_by_revision_identifier(grouping, revision_identifier)
    repo = grouping.group.repo
    revision = repo.get_revision(revision_identifier)
    submission = self.generate_new_submission(grouping, revision)
    repo.close
    submission
  end

  def self.generate_new_submission(grouping, revision)
    new_submission = Submission.new
    new_submission.grouping = grouping
    new_submission.submission_version = 1
    new_submission.submission_version_used = true
    new_submission.revision_timestamp = revision.timestamp
    new_submission.revision_identifier = revision.revision_identifier
    new_submission.transaction do
      begin
        new_submission.populate_with_submission_files(revision)
      rescue Repository::FileDoesNotExist
        # populate the submission with no files instead of raising an exception
      end
      new_submission.save
    end
    new_submission
  end

  # Returns the original result.
  def get_original_result
    non_pr_results.first
  end

  def remark_result
    if remark_request_timestamp.nil? || non_pr_results.length < 2
      nil
    else
      non_pr_results.last
    end
  end

  def remark_result_id
    remark_result.try(:id)
  end

  # Returns the latest result.
  def get_latest_result
    if !submitted_remark.nil?
      remark_result
    else
      get_original_result
    end
  end

  # Returns the latest completed result.
  def get_latest_completed_result
    if remark_submitted? &&
       remark_result.marking_state == Result::MARKING_STATES[:complete]
      remark_result
    elsif get_original_result.marking_state == Result::MARKING_STATES[:complete]
      get_original_result
    else
      nil
    end
  end

  # Sets marks when automated tests are run
  def set_marks_for_tests
    return if test_script_results.empty?

    result = get_latest_result
    complete_marks = true
    if result.marks.empty? # can happen if a criterion is created after collection
      result.create_marks
    end
    result.marks.includes(markable: :test_scripts).each do |mark|
      all_marks_earned = 0.0
      all_marks_total = 0.0
      mark.markable.test_scripts.each do |test_script|
        res = test_script_results.where(test_script_id: test_script.id).first
        all_marks_earned += res.marks_earned
        unless all_marks_total.nil?
{'fraction_non_alphanumeric': 0.06671572234486142, 'fraction_numerical': 0.001594309541329409, 'mean_word_length': 3.4417211328976034, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 15, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '627684', 'n_tokens_mistral': 2470, 'n_tokens_neox': 2421, 'n_words': 549}