-
Notifications
You must be signed in to change notification settings - Fork 194
E2614 Role Based Reviewing #347
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
89ba18e
7f79b10
f40a96d
09e9f3f
44c27b1
6c520a9
afc70bd
df699ac
a5cceb0
389a220
8e605f0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -32,3 +32,8 @@ | |
| coverage/ | ||
| rsa_keys.yml | ||
| pg_data/ | ||
|
|
||
| # Other | ||
| setup.sh | ||
| Gemfile.lock | ||
| Gemfile | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,31 +1,78 @@ | ||
| class AssignmentsDutiesController < ApplicationController | ||
| include Authorization | ||
| #before_action :authenticate_user! | ||
| before_action :action_allowed!, only: [:create, :destroy] | ||
| before_action :action_allowed!, only: [:create, :destroy, :update_limit] | ||
| before_action :set_assignment | ||
| before_action :set_assignment_duty, only: [:update_limit] | ||
|
|
||
| # GET /assignments/:assignment_id/duties | ||
| def index | ||
| assignment = Assignment.find(params[:assignment_id]) | ||
| render json: assignment.duties | ||
| render json: serialized_assignment_duties | ||
| end | ||
|
|
||
| # POST /assignments/:assignment_id/duties | ||
| def create | ||
| assignment = Assignment.find(params[:assignment_id]) | ||
| duty = Duty.find(params[:duty_id]) | ||
| assignment.duties << duty unless assignment.duties.include?(duty) | ||
| render json: assignment.duties | ||
| assignment_duty = @assignment.assignments_duties.find_or_initialize_by(duty_id: duty.id) | ||
| assignment_duty.max_members_for_duty ||= 1 | ||
|
|
||
| if assignment_duty.save | ||
| render json: serialized_assignment_duties, status: :ok | ||
| else | ||
| render json: assignment_duty.errors, status: :unprocessable_entity | ||
| end | ||
| end | ||
|
|
||
| # DELETE /assignments/:assignment_id/duties/:id | ||
| def destroy | ||
| assignment = Assignment.find(params[:assignment_id]) | ||
| duty = Duty.find(params[:id]) | ||
| assignment.duties.delete(duty) | ||
| @assignment.duties.delete(duty) | ||
| head :no_content | ||
| end | ||
|
|
||
| # PATCH /assignments/:assignment_id/duties/:id/limit | ||
| # Set or update the maximum number of team members allowed to have this duty (role) for a given assignment. | ||
| # Request body: { max_members_for_duty: integer } | ||
| # Response: The updated duty mapping with the new limit, or validation errors if invalid. | ||
| def update_limit | ||
| if @assignment_duty.update(limit_params) | ||
| render json: serialize_assignment_duty(@assignment_duty), status: :ok | ||
| else | ||
| render json: @assignment_duty.errors, status: :unprocessable_entity | ||
| end | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def set_assignment | ||
| @assignment = Assignment.find(params[:assignment_id]) | ||
| end | ||
|
|
||
| def set_assignment_duty | ||
| @assignment_duty = @assignment.assignments_duties.find_by(duty_id: params[:id]) | ||
| return if @assignment_duty | ||
|
|
||
| render json: { error: 'Duty is not assigned to this assignment' }, status: :not_found | ||
| end | ||
|
|
||
| def limit_params | ||
| params.permit(:max_members_for_duty) | ||
| end | ||
|
|
||
| def serialized_assignment_duties | ||
| @assignment.assignments_duties.includes(:duty).map do |assignment_duty| | ||
| serialize_assignment_duty(assignment_duty) | ||
| end | ||
| end | ||
|
|
||
| def serialize_assignment_duty(assignment_duty) | ||
| { | ||
| duty_id: assignment_duty.duty_id, | ||
| duty_name: assignment_duty.duty&.name, | ||
| max_members_for_duty: assignment_duty.max_members_for_duty | ||
| } | ||
| end | ||
|
|
||
| def action_allowed! | ||
| unless current_user_has_instructor_privileges? | ||
| render json: { error: 'Not authorized' }, status: :forbidden | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. action_allowed! renders 403 but does not return, so the action may continue after unauthorized access. |
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -46,6 +46,8 @@ def show | |
| end | ||
| end | ||
|
|
||
|
|
||
|
|
||
| # Assign the specified authorization to the participant and add them to an assignment | ||
| # POST /participants/:authorization | ||
| def add | ||
|
|
@@ -98,6 +100,20 @@ def update_authorization | |
| end | ||
| end | ||
|
|
||
| # Update the specified participant duty | ||
| # PATCH /participants/:id/duty | ||
| def update_duty | ||
| participant = find_participant | ||
| return unless participant | ||
|
|
||
| participant.duty_id = params[:duty_id].presence | ||
| if participant.save | ||
| render json: participant, status: :created | ||
| else | ||
| render json: participant.errors, status: :unprocessable_entity | ||
| end | ||
| end | ||
|
Comment on lines
+103
to
+115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use The Proposed fix if participant.save
- render json: participant, status: :created
+ render json: participant, status: :ok
else🤖 Prompt for AI Agents |
||
|
|
||
| # Delete a participant | ||
| # params - id | ||
| # DELETE /participants/:id | ||
|
|
@@ -189,4 +205,4 @@ def validate_authorization | |
|
|
||
| authorization | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,8 +24,30 @@ def update_duty | |
| end | ||
|
|
||
| duty_id = params.dig(:teams_participant, :duty_id) || params.dig("teams_participant", "duty_id") | ||
| team_participant.update(duty_id: duty_id) | ||
| render json: { message: "Duty updated successfully" }, status: :ok | ||
| participant = team_participant.participant | ||
|
|
||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Needs a clearer comment. |
||
| # For assignment teams, derive the assignment from the team, not from participant.parent_id. | ||
| # This controller also serves CourseTeam, so participant.parent_id is not always an assignment id. | ||
| # Gate this action to assignment teams and resolve the assignment from team_participant.team. | ||
| assignment = nil | ||
| if team_participant.team.type == "AssignmentTeam" | ||
| assignment = team_participant.team.assignment | ||
| else | ||
| render json: { error: 'Duty assignment is only supported for assignment teams' }, status: :unprocessable_entity and return | ||
| end | ||
| if duty_id.present? | ||
| assignment_duty = AssignmentsDuty.find_by(assignment_id: assignment.id, duty_id: duty_id) | ||
| unless assignment_duty | ||
| render json: { error: 'Duty is not assigned to this assignment' }, status: :unprocessable_entity and return | ||
| end | ||
| end | ||
|
|
||
| participant.duty_id = duty_id | ||
| if participant.save | ||
| render json: { message: 'Duty updated successfully' }, status: :ok | ||
| else | ||
| render json: { error: participant.errors.full_messages.to_sentence }, status: :unprocessable_entity | ||
| end | ||
| end | ||
|
|
||
| # Displays a list of all participants in a specific team. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,6 @@ | ||
| class AssignmentsDuty < ApplicationRecord | ||
| belongs_to :assignment | ||
| belongs_to :duty | ||
|
|
||
| validates :max_members_for_duty, numericality: { only_integer: true, greater_than_or_equal_to: 1 } | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,17 @@ | ||
| class AssignmentSerializer < ActiveModel::Serializer | ||
| attributes :id, :name, :max_team_size, :course_id | ||
| attributes :id, :name, :max_team_size, :course_id, :has_role_based_review, :assignment_duties | ||
|
|
||
| def has_role_based_review | ||
| object.assignments_duties.exists? | ||
| end | ||
|
|
||
| def assignment_duties | ||
| object.assignments_duties.includes(:duty).map do |assignment_duty| | ||
| { | ||
| duty_id: assignment_duty.duty_id, | ||
| duty_name: assignment_duty.duty&.name, | ||
| max_members_for_duty: assignment_duty.max_members_for_duty | ||
| } | ||
| end | ||
|
Comment on lines
+4
to
+15
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Inspect where AssignmentSerializer is used and whether those paths preload duties.
# Expected result: collection render paths should preload assignments_duties: :duty.
echo "== Assignment render sites =="
rg -nP --type ruby 'AssignmentSerializer|each_serializer:\s*AssignmentSerializer|serializer:\s*AssignmentSerializer|render\s+json:\s+@assignments|render\s+json:\s+@assignment' app/controllers app/serializers
echo
echo "== Duty preloading in controllers =="
rg -nP --type ruby 'includes\([^)]*assignments_duties[^)]*duty|preload\([^)]*assignments_duties[^)]*duty|eager_load\([^)]*assignments_duties[^)]*duty' app/controllersRepository: expertiza/reimplementation-back-end Length of output: 756 🏁 Script executed: #!/bin/bash
# Check for other potential uses of AssignmentSerializer or assignment rendering
echo "== All AssignmentSerializer references =="
rg -n 'AssignmentSerializer' --type ruby
echo
echo "== Render json with assignment variable (broader search) =="
rg -nB2 -A2 'render.*json.*assignment' --type ruby app/controllers
echo
echo "== Check for respond_with patterns =="
rg -n 'respond_with.*assignment' --type ruby app/controllers
echo
echo "== Check what student.assignment returns =="
rg -n 'def assignment' --type ruby app/models/user.rb app/models/student.rbRepository: expertiza/reimplementation-back-end Length of output: 13995 🏁 Script executed: #!/bin/bash
# Check if Student model exists or if it's part of User
echo "== Student model or relationship =="
fd -e rb . app/models | xargs grep -l "class.*Student" | head -5
echo
echo "== Check AssignmentSerializer attributes =="
cat -n app/serializers/assignment_serializer.rb | head -30
echo
echo "== Check if assignment_duties is accessed in controllers =="
rg -n 'assignment_duties|assignments_duties' app/controllers/student_teams_controller.rbRepository: expertiza/reimplementation-back-end Length of output: 962 🏁 Script executed: #!/bin/bash
# Verify the Assignment model associations
echo "== Assignment model class definition and associations =="
cat -n app/models/assignment.rb | head -50
echo
echo "== Check for has_many :assignments_duties in Assignment =="
rg -A5 'has_many.*assignments_duties|has_many.*:assignments_duties' app/models/assignment.rbRepository: expertiza/reimplementation-back-end Length of output: 3536 These serializer methods trigger unnecessary database queries per assignment render. Both 🧰 Tools🪛 RuboCop (1.86.1)[convention] 4-4: Use 2 (not 5) spaces for indentation. (Layout/IndentationWidth) [convention] 5-5: Use 2 (not 5) spaces for indentation. (Layout/IndentationWidth) [convention] 8-8: Use 2 (not 5) spaces for indentation. (Layout/IndentationWidth) [convention] 9-9: Use 2 (not 5) spaces for indentation. (Layout/IndentationWidth) [convention] 10-10: Use 2 (not 5) spaces for indentation. (Layout/IndentationWidth) [convention] 11-11: Use 2 spaces for indentation in a hash, relative to the start of the line where the left curly brace is. (Layout/FirstHashElementIndentation) 🤖 Prompt for AI Agents |
||
| end | ||
| end | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a comment saying that the limit is the max # of team members who can have this duty.