Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 91 additions & 0 deletions app/controllers/revision_requests_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# frozen_string_literal: true

class RevisionRequestsController < ApplicationController
prepend_before_action :set_assignment, only: :index
prepend_before_action :set_revision_request, only: %i[show update]

def action_allowed?
case params[:action]
when 'index'
current_user_has_instructor_privileges? && current_user_instructs_assignment?(@assignment)
when 'show'
return false unless @revision_request

owns_revision_request? || current_user_instructs_assignment?(@revision_request.assignment)
when 'update'
return false unless @revision_request

current_user_has_instructor_privileges? && current_user_instructs_assignment?(@revision_request.assignment)
else
false
end
end

def index
return if invalid_status_filter?

revision_requests = RevisionRequest.where(assignment_id: @assignment.id)
revision_requests = revision_requests.where(status: params[:status]) if params[:status].present?

render json: revision_requests.order(created_at: :desc).map(&:as_json), status: :ok
end

def show
return unless @revision_request

render json: @revision_request.as_json, status: :ok
end

def update
return render json: { error: 'This revision request has already been processed' }, status: :unprocessable_entity unless @revision_request.status == RevisionRequest::PENDING
return if invalid_update_status?

if @revision_request.update(update_params)
render json: @revision_request.as_json, status: :ok
else
render json: { error: @revision_request.errors.full_messages.to_sentence }, status: :unprocessable_entity
end
end

private

def set_assignment
@assignment = Assignment.find_by(id: params[:assignment_id])
return if @assignment

render json: { error: 'Assignment not found' }, status: :not_found
end

def set_revision_request
@revision_request = RevisionRequest.find_by(id: params[:id])
return if @revision_request

render json: { error: 'Revision request not found' }, status: :not_found
end

def owns_revision_request?
@revision_request.participant.user_id == current_user.id
end

def invalid_status_filter?
return false if params[:status].blank? || RevisionRequest::STATUSES.include?(params[:status])

render json: { error: 'Status must be PENDING, APPROVED, or DECLINED' }, status: :unprocessable_entity
true
end

def invalid_update_status?
return false if valid_resolved_status?

render json: { error: 'Status must be APPROVED or DECLINED' }, status: :unprocessable_entity
true
end

def valid_resolved_status?
[RevisionRequest::APPROVED, RevisionRequest::DECLINED].include?(update_params[:status])
end

def update_params
params.require(:revision_request).permit(:status, :response_comment)
end
end
48 changes: 36 additions & 12 deletions app/controllers/student_tasks_controller.rb
Original file line number Diff line number Diff line change
@@ -1,28 +1,52 @@
class StudentTasksController < ApplicationController
before_action :set_student_task, only: %i[show view request_revision]

# List retrieves all student tasks associated with the current logged-in user.
def action_allowed?
current_user_has_student_privileges?
end

def index
list
end

def list
# Retrieves all tasks that belong to the current user.
@student_tasks = StudentTask.from_user(current_user)
# Render the list of student tasks as JSON.
render json: @student_tasks, status: :ok
render json: StudentTask.from_user(current_user), status: :ok
end

def show
render json: @student_task, status: :ok
end

# The view function retrieves a student task based on a participant's ID.
# It is meant to provide an endpoint where tasks can be queried based on participant ID.
def view
# Retrieves the student task where the participant's ID matches the provided parameter.
# This function will be used for clicking on a specific student task to "view" its details.
@student_task = StudentTask.from_participant_id(params[:id])
# Render the found student task as JSON.
render json: @student_task, status: :ok
show
end

def request_revision
return render json: { error: 'Revision requests require a team submission' }, status: :unprocessable_entity unless @student_task.team
return render json: { error: 'Revision requests are not available for this task' }, status: :unprocessable_entity unless @student_task.can_request_revision

revision_request = RevisionRequest.new(
participant: @participant,
team: @student_task.team,
assignment: @participant.assignment,
comments: params[:comments]
)

if revision_request.save
@student_task = StudentTask.from_participant(@participant)
render json: { message: 'Revision request submitted successfully', revision_request: revision_request.as_json, student_task: @student_task.as_json }, status: :created
else
render json: { error: revision_request.errors.full_messages.to_sentence }, status: :unprocessable_entity
end
end

private

def set_student_task
@participant = AssignmentParticipant.find_by(id: params[:id])
return render json: { error: 'Student task not found' }, status: :not_found unless @participant
return render json: { error: 'You are not authorized to access this student task' }, status: :forbidden unless @participant.user_id == current_user.id

@student_task = StudentTask.from_participant(@participant)
end
end
1 change: 1 addition & 0 deletions app/models/assignment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Assignment < ApplicationRecord
has_many :due_dates,as: :parent, class_name: 'DueDate', dependent: :destroy
has_many :assignments_duties, dependent: :destroy
has_many :duties, through: :assignments_duties
has_many :revision_requests, dependent: :destroy
belongs_to :course, optional: true
belongs_to :instructor, class_name: 'User', inverse_of: :assignments

Expand Down
1 change: 1 addition & 0 deletions app/models/assignment_participant.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ class AssignmentParticipant < Participant
has_many :review_mappings, class_name: 'ReviewResponseMap', foreign_key: 'reviewee_id'
has_many :response_maps, foreign_key: 'reviewee_id'
has_many :sent_invitations, class_name: 'Invitation', foreign_key: 'from_id'
has_many :revision_requests, foreign_key: 'participant_id', dependent: :destroy
belongs_to :duty, optional: true
belongs_to :user
validates :handle, presence: true
Expand Down
3 changes: 2 additions & 1 deletion app/models/assignment_team.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ class AssignmentTeam < Team
has_many :review_mappings, class_name: 'ReviewResponseMap', foreign_key: 'reviewee_id'
has_many :review_response_maps, foreign_key: 'reviewee_id'
has_many :responses, through: :review_response_maps, foreign_key: 'map_id'
has_many :revision_requests, foreign_key: 'team_id', dependent: :destroy

# Delegation to avoid Law of Demeter violations
delegate :path, to: :assignment, prefix: true
Expand Down Expand Up @@ -224,4 +225,4 @@ def validate_assignment_team_type
end
end

class TeamFullError < StandardError; end
class TeamFullError < StandardError; end
40 changes: 40 additions & 0 deletions app/models/revision_request.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

class RevisionRequest < ApplicationRecord
PENDING = 'PENDING'
APPROVED = 'APPROVED'
DECLINED = 'DECLINED'
STATUSES = [PENDING, APPROVED, DECLINED].freeze

belongs_to :participant, class_name: 'AssignmentParticipant'
belongs_to :team, class_name: 'AssignmentTeam'
belongs_to :assignment

validates :comments, presence: true
validates :status, inclusion: { in: STATUSES }
validate :one_pending_request_per_participant_team, on: :create

scope :pending, -> { where(status: PENDING) }

def as_json(_options = {})
{
id: id,
participant_id: participant_id,
team_id: team_id,
assignment_id: assignment_id,
status: status,
comments: comments,
response_comment: response_comment,
created_at: created_at&.iso8601,
updated_at: updated_at&.iso8601
}
end

private

def one_pending_request_per_participant_team
return unless self.class.pending.exists?(participant_id: participant_id, team_id: team_id)

errors.add(:base, 'A pending revision request already exists for this task')
end
end
Loading
Loading