<commit_before>#
# Copyright (C) 2011 - 2012 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/>.
#

# @API Quiz Submissions
# @beta
#
# API for accessing quiz submissions
#
# @model QuizSubmission
#     {
#       "id": "QuizSubmission",
#       "required": ["id", "quiz_id"],
#       "properties": {
#         "id": {
#           "description": "The ID of the quiz submission.",
#           "example": 1,
#           "type": "integer",
#           "format": "int64"
#         },
#         "quiz_id": {
#           "description": "The ID of the Quiz the quiz submission belongs to.",
#           "example": 2,
#           "type": "integer",
#           "format": "int64"
#         },
#         "user_id": {
#           "description": "The ID of the Student that made the quiz submission.",
#           "example": 3,
#           "type": "integer",
#           "format": "int64"
#         },
#         "submission_id": {
#           "description": "The ID of the Submission the quiz submission represents.",
#           "example": 1,
#           "type": "integer",
#           "format": "int64"
#         },
#         "started_at": {
#           "description": "The time at which the student started the quiz submission.",
#           "example": "2013-11-07T13:16:18Z",
#           "type": "string",
#           "format": "date-time"
#         },
#         "finished_at": {
#           "description": "The time at which the student submitted the quiz submission.",
#           "example": "2013-11-07T13:16:18Z",
#           "type": "string",
#           "format": "date-time"
#         },
#         "end_at": {
#           "description": "The time at which the quiz submission will be overdue, and be flagged as a late submission.",
#           "example": "2013-11-07T13:16:18Z",
#           "type": "string",
#           "format": "date-time"
#         },
#         "attempt": {
#           "description": "For quizzes that allow multiple attempts, this field specifies the quiz submission attempt number.",
#           "example": 3,
#           "type": "integer",
#           "format": "int64"
#         },
#         "extra_attempts": {
#           "description": "Number of times the student was allowed to re-take the quiz over the multiple-attempt limit.",
#           "example": 1,
#           "type": "integer",
#           "format": "int64"
#         },
#         "extra_time": {
#           "description": "Amount of extra time allowed for the quiz submission, in minutes.",
#           "example": 60,
#           "type": "integer",
#           "format": "int64"
#         },
#         "manually_unlocked": {
#           "description": "The student can take the quiz even if it's locked for everyone else",
#           "example": true,
#           "type": "boolean"
#         },
#         "time_spent": {
#           "description": "Amount of time spent, in seconds.",
#           "example": 300,
#           "type": "integer",
#           "format": "int64"
#         },
#         "score": {
#           "description": "The score of the quiz submission, if graded.",
#           "example": 3,
#           "type": "integer",
#           "format": "int64"
#         },
#         "score_before_regrade": {
#           "description": "The original score of the quiz submission prior to any re-grading.",
#           "example": 2,
#           "type": "integer",
#           "format": "int64"
#         },
#         "kept_score": {
#           "description": "For quizzes that allow multiple attempts, this is the score that will be used, which might be the score of the latest, or the highest, quiz submission.",
#           "example": 5,
#           "type": "integer",
#           "format": "int64"
#         },
#         "fudge_points": {
#           "description": "Number of points the quiz submission's score was fudged by.",
#           "example": 1,
#           "type": "integer",
#           "format": "int64"
#         },
#         "has_seen_results": {
#           "description": "Whether the student has viewed their results to the quiz.",
#           "example": true,
#           "type": "boolean"
#         },
#         "workflow_state": {
#           "description": "The current state of the quiz submission. Possible values: ['untaken'|'pending_review'|'complete'|'settings_only'|'preview'].",
#           "example": "untaken",
#           "type": "string"
#         },
#         "overdue_and_needs_submission": {
#           "description": "Indicates whether the quiz submission is overdue and needs submission",
#           "example": "false",
#           "type": "boolean"
#         }
#       }
#     }
#
class Quizzes::QuizSubmissionsApiController < ApplicationController
  include Api::V1::QuizSubmission
  include Filters::Quizzes
  include Filters::QuizSubmissions

  before_filter :require_user, :require_context, :require_quiz
  before_filter :require_overridden_quiz, :except => [ :index ]
  before_filter :require_quiz_submission, :except => [ :index, :create ]
  before_filter :prepare_service, :only => [ :create, :update, :complete ]
  before_filter :validate_ldb_status!, :only => [ :create, :complete ]

  # @API Get all quiz submissions.
  # @beta
  #
  # Get a list of all submissions for this quiz.
  #
  # @argument include[] [String, "submission"|"quiz"|"user"]
  #   Associations to include with the quiz submission.
  #
  # <b>200 OK</b> response code is returned if the request was successful.
  #
  # @example_response
  #  {
  #    "quiz_submissions": [QuizSubmission]
  #  }
  def index
    quiz_submissions = if @context.grants_any_right?(@current_user, session, :manage_grades, :view_all_grades)
      # teachers have access to all student submissions
      Api.paginate @quiz.quiz_submissions.where(:user_id => visible_user_ids),
        self,
        api_v1_course_quiz_submissions_url(@context, @quiz)
    elsif @quiz.grants_right?(@current_user, session, :submit)
      # students have access only to their own
      @quiz.quiz_submissions.where(:user_id => @current_user).flat_map(&:submitted_attempts)
    end

    if !quiz_submissions
      render_unauthorized_action
    else
      # trigger delayed grading job for all submission id's which needs grading
      quiz_submissions_ids = quiz_submissions.map(&:id).uniq
      Quizzes::OutstandingQuizSubmissionManager.new(@quiz).send_later_if_production(:grade_by_ids, quiz_submissions_ids)
      serialize_and_render quiz_submissions
    end
  end

  # @API Get a single quiz submission.
  # @beta
  #
  # Get a single quiz submission.
  #
  # @argument include[] [String, "submission"|"quiz"|"user"]<commit_msg>Add temporary additional logging for QSApi#index performance<commit_after>#
# Copyright (C) 2011 - 2012 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/>.
#

# @API Quiz Submissions
# @beta
#
# API for accessing quiz submissions
#
# @model QuizSubmission
#     {
#       "id": "QuizSubmission",
#       "required": ["id", "quiz_id"],
#       "properties": {
#         "id": {
#           "description": "The ID of the quiz submission.",
#           "example": 1,
#           "type": "integer",
#           "format": "int64"
#         },
#         "quiz_id": {
#           "description": "The ID of the Quiz the quiz submission belongs to.",
#           "example": 2,
#           "type": "integer",
#           "format": "int64"
#         },
#         "user_id": {
#           "description": "The ID of the Student that made the quiz submission.",
#           "example": 3,
#           "type": "integer",
#           "format": "int64"
#         },
#         "submission_id": {
#           "description": "The ID of the Submission the quiz submission represents.",
#           "example": 1,
#           "type": "integer",
#           "format": "int64"
#         },
#         "started_at": {
#           "description": "The time at which the student started the quiz submission.",
#           "example": "2013-11-07T13:16:18Z",
#           "type": "string",
#           "format": "date-time"
#         },
#         "finished_at": {
#           "description": "The time at which the student submitted the quiz submission.",
#           "example": "2013-11-07T13:16:18Z",
#           "type": "string",
#           "format": "date-time"
#         },
#         "end_at": {
#           "description": "The time at which the quiz submission will be overdue, and be flagged as a late submission.",
#           "example": "2013-11-07T13:16:18Z",
#           "type": "string",
#           "format": "date-time"
#         },
#         "attempt": {
#           "description": "For quizzes that allow multiple attempts, this field specifies the quiz submission attempt number.",
#           "example": 3,
#           "type": "integer",
#           "format": "int64"
#         },
#         "extra_attempts": {
#           "description": "Number of times the student was allowed to re-take the quiz over the multiple-attempt limit.",
#           "example": 1,
#           "type": "integer",
#           "format": "int64"
#         },
#         "extra_time": {
#           "description": "Amount of extra time allowed for the quiz submission, in minutes.",
#           "example": 60,
#           "type": "integer",
#           "format": "int64"
#         },
#         "manually_unlocked": {
#           "description": "The student can take the quiz even if it's locked for everyone else",
#           "example": true,
#           "type": "boolean"
#         },
#         "time_spent": {
#           "description": "Amount of time spent, in seconds.",
#           "example": 300,
#           "type": "integer",
#           "format": "int64"
#         },
#         "score": {
#           "description": "The score of the quiz submission, if graded.",
#           "example": 3,
#           "type": "integer",
#           "format": "int64"
#         },
#         "score_before_regrade": {
#           "description": "The original score of the quiz submission prior to any re-grading.",
#           "example": 2,
#           "type": "integer",
#           "format": "int64"
#         },
#         "kept_score": {
#           "description": "For quizzes that allow multiple attempts, this is the score that will be used, which might be the score of the latest, or the highest, quiz submission.",
#           "example": 5,
#           "type": "integer",
#           "format": "int64"
#         },
#         "fudge_points": {
#           "description": "Number of points the quiz submission's score was fudged by.",
#           "example": 1,
#           "type": "integer",
#           "format": "int64"
#         },
#         "has_seen_results": {
#           "description": "Whether the student has viewed their results to the quiz.",
#           "example": true,
#           "type": "boolean"
#         },
#         "workflow_state": {
#           "description": "The current state of the quiz submission. Possible values: ['untaken'|'pending_review'|'complete'|'settings_only'|'preview'].",
#           "example": "untaken",
#           "type": "string"
#         },
#         "overdue_and_needs_submission": {
#           "description": "Indicates whether the quiz submission is overdue and needs submission",
#           "example": "false",
#           "type": "boolean"
#         }
#       }
#     }
#
class Quizzes::QuizSubmissionsApiController < ApplicationController
  include Api::V1::QuizSubmission
  include Filters::Quizzes
  include Filters::QuizSubmissions

  before_filter :require_user, :require_context, :require_quiz
  before_filter :require_overridden_quiz, :except => [ :index ]
  before_filter :require_quiz_submission, :except => [ :index, :create ]
  before_filter :prepare_service, :only => [ :create, :update, :complete ]
  before_filter :validate_ldb_status!, :only => [ :create, :complete ]

  # @API Get all quiz submissions.
  # @beta
  #
  # Get a list of all submissions for this quiz.
  #
  # @argument include[] [String, "submission"|"quiz"|"user"]
  #   Associations to include with the quiz submission.
  #
  # <b>200 OK</b> response code is returned if the request was successful.
  #
  # @example_response
  #  {
  #    "quiz_submissions": [QuizSubmission]
  #  }
  def index
    t = Time.now
    quiz_submissions = if @context.grants_any_right?(@current_user, session, :manage_grades, :view_all_grades)
      # teachers have access to all student submissions
      Api.paginate @quiz.quiz_submissions.where(:user_id => visible_user_ids),
        self,
        api_v1_course_quiz_submissions_url(@context, @quiz)
    elsif @quiz.grants_right?(@current_user, session, :submit)
      # students have access only to their own
      @quiz.quiz_submissions.where(:user_id => @current_user).flat_map(&:submitted_attempts)
    end

    if !quiz_submissions
      render_unauthorized_action
    else
      # trigger delayed grading job for all submission id's which needs grading
      quiz_submissions_ids = quiz_submissions.map(&:id).uniq
      Quizzes::OutstandingQuizSubmissionManager.new(@quiz).send_later_if_production(:grade_by_ids, quiz_submissions_ids)
      serialize_and_render quiz_submissions
    end
    Rails.logger.info "QS_API -- url: #{@_request.original_url}"
    Rails.logger.info "QS_API -- filtered_params: #{@_request.filtered_parameters}"
    Rails.logger.info "QS_API -- timer: #{Time.now-t} seconds"
  end

  # @API Get a single quiz submission.
  # @beta
  #
  # Get a single quiz submission.
  #
  # @argument include[] [String, "submission"|"quiz"|"user"]
{'fraction_non_alphanumeric': 0.133356024506467, 'fraction_numerical': 0.012933968686181076, 'mean_word_length': 2.38736453769887, 'pattern_counts': {'":': 190, '<': 11, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '1789083', 'n_tokens_mistral': 4544, 'n_tokens_neox': 4271, 'n_words': 1280}