Skip to content

Commit 6fe8e77

Browse files
committed
initial refactor for teams hierarchy
1 parent 93b9a76 commit 6fe8e77

File tree

13 files changed

+367
-169
lines changed

13 files changed

+367
-169
lines changed

app/controllers/teams_controller.rb

Lines changed: 16 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -42,45 +42,31 @@ def members
4242
# POST /teams/:id/members
4343
# Adds a new member to the team.
4444
def add_member
45-
# Find the user specified in the request.
4645
user = User.find(team_participant_params[:user_id])
47-
48-
# Determine the correct type of participant (Assignment or Course) based on the team type.
49-
participant_class = @team.is_a?(AssignmentTeam) ? AssignmentParticipant : CourseParticipant
50-
51-
# Find the specific participant record for this user in the team's context.
52-
participant = participant_class.find_by(user_id: user.id, parent_id: @team.parent_id)
53-
54-
# If no participant record exists, the user isn't part of the assignment/course.
55-
unless participant
56-
return render json: { errors: ["#{user.name} is not a participant in this context."] }, status: :unprocessable_entity
57-
end
58-
59-
# Delegate the add operation to the Team model with the found participant.
60-
result = @team.add_member(participant)
46+
result = @team.add_member(user)
6147

6248
if result[:success]
6349
render json: user, serializer: UserSerializer, status: :created
6450
else
6551
render json: { errors: [result[:error]] }, status: :unprocessable_entity
6652
end
53+
rescue ActiveRecord::RecordNotFound
54+
render json: { errors: ['User not found'] }, status: :not_found
6755
end
6856

6957
# DELETE /teams/:id/members/:user_id
7058
# Removes a member from the team based on user ID
7159
def remove_member
7260
user = User.find(params[:user_id])
73-
participant = Participant.find_by(user: user, parent_id: @team.parent_id)
74-
tp = @team.teams_participants.find_by(participant: participant)
61+
result = @team.remove_member(user)
7562

76-
if tp
77-
tp.destroy
63+
if result[:success]
7864
head :no_content
7965
else
80-
render json: { error: 'Member not found' }, status: :not_found
66+
render json: { errors: [result[:error]] }, status: :not_found
8167
end
8268
rescue ActiveRecord::RecordNotFound
83-
render json: { error: 'User not found' }, status: :not_found
69+
render json: { errors: ['User not found'] }, status: :not_found
8470
end
8571

8672
# Placeholder method to get current user (can be replaced by actual auth logic)
@@ -99,7 +85,7 @@ def set_team
9985

10086
# Whitelists the parameters allowed for team creation/updation
10187
def team_params
102-
params.require(:team).permit(:name, :max_team_size, :type, :assignment_id)
88+
params.require(:team).permit(:name, :max_team_size, :type, :assignment_id, :course_id, :parent_id)
10389
end
10490

10591
# Whitelists parameters required to add a team member
@@ -109,10 +95,14 @@ def team_participant_params
10995

11096
# Validates the team type before team creation to ensure it's among allowed types
11197
def validate_team_type
112-
return unless params[:team] && params[:team][:type]
113-
valid_types = ['CourseTeam', 'AssignmentTeam', 'MentoredTeam']
114-
unless valid_types.include?(params[:team][:type])
115-
render json: { error: 'Invalid team type' }, status: :unprocessable_entity
98+
# Use .dig to safely access nested params.
99+
# Check that the type is present AND included in the list.
100+
team_type = params.dig(:team, :type)
101+
102+
unless team_type.in?(['CourseTeam', 'AssignmentTeam', 'MentoredTeam'])
103+
# If type is nil or not in the list, render an error and stop execution.
104+
render json: { errors: ["Invalid or missing team type. Must be one of: CourseTeam, AssignmentTeam, MentoredTeam."] },
105+
status: :unprocessable_entity
116106
end
117107
end
118108
end

app/helpers/team_operations_helper.rb

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ def self.validate_mentor_assignment(team, user)
1313

1414
# Copies all members from the source team to the target team
1515
def self.copy_team_members(source_team, target_team)
16-
source_team.users.each do |user|
17-
target_team.add_member(user)
18-
end
16+
source_team.copy_members_to(target_team)
1917
end
2018

2119
# Returns a hash of basic statistics about the given team

app/models/assignment_team.rb

Lines changed: 5 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,40 +2,13 @@
22

33
class AssignmentTeam < Team
44
# Each AssignmentTeam must belong to a specific assignment
5-
belongs_to :assignment, class_name: 'Assignment', foreign_key: 'parent_id'
5+
belongs_to :assignment, class_name: 'Assignment', foreign_key: 'parent_id', inverse_of: :teams
66

7-
# Copies the current assignment team to a course team
8-
# - Creates a new CourseTeam with a modified name
9-
# - Copies team members from the assignment team to the course team
10-
def copy_to_course_team(course)
11-
course_team = CourseTeam.new(
12-
name: "#{name} (Course)", # Appends "(Course)" to the team name
13-
course: course # Associates new team with the given course
14-
)
15-
if course_team.save
16-
team_members.each do |member|
17-
course_team.add_member(member.user) # Copies each member to the new course team
18-
end
19-
end
20-
course_team # Returns the newly created course team object
7+
def copy_to_course(course, name: "#{self.name} (Course)")
8+
copy_to(course, as: CourseTeam, name:)
219
end
2210

23-
protected
24-
25-
# Validates if a user is eligible to join the team
26-
# - Checks whether the user is a participant of the associated assignment
27-
def validate_membership(user)
28-
# Ensure user is enrolled in the assignment by checking AssignmentParticipant
29-
assignment.participants.exists?(user: user)
30-
end
31-
32-
private
33-
34-
35-
# Validates that the team is an AssignmentTeam or a subclass (e.g., MentoredTeam)
36-
def validate_assignment_team_type
37-
unless self.kind_of?(AssignmentTeam)
38-
errors.add(:type, 'must be an AssignmentTeam or its subclass')
39-
end
11+
def copy_to_assignment(assignment, as: AssignmentTeam, name: self.name)
12+
copy_to(assignment, as:, name:)
4013
end
4114
end

app/models/course_team.rb

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2,44 +2,13 @@
22

33
class CourseTeam < Team
44
#Each course team must belong to a course
5-
belongs_to :course, class_name: 'Course', foreign_key: 'parent_id'
5+
belongs_to :course, class_name: 'Course', foreign_key: 'parent_id', inverse_of: :teams
66

7-
#adds members to the course team post validation
8-
def add_member(user)
9-
# return false unless validate_membership(user)
10-
super(user)
7+
def copy_to_assignment(assignment, as: AssignmentTeam, name: "#{self.name} (Assignment)")
8+
copy_to(assignment, as:, name:)
119
end
1210

13-
# Copies the current course team to an assignment team
14-
# - Creates a new AssignmentTeam with a modified name
15-
# - Copies team members from the course team to the assignment team
16-
def copy_to_assignment_team(assignment)
17-
assignment_team = AssignmentTeam.new(
18-
name: "#{name} (Assignment)", # Appends "(Assignment)" to the team name
19-
assignment: assignment # Associates the course team with an assignment
20-
)
21-
if assignment_team.save
22-
team_members.each do |member|
23-
assignment_team.add_member(member.user) # Copies each member to the new assignment team
24-
end
25-
end
26-
assignment_team # Returns the newly created assignment team object
27-
end
28-
29-
protected
30-
31-
def validate_membership(user)
32-
# Check if user is enrolled in any assignment in the course
33-
course.assignments.any? { |assignment| assignment.participants.exists?(user: user) }
34-
end
35-
36-
private
37-
38-
# Custom validation method for team type
39-
# - Ensures the type is 'CourseTeam'
40-
def type_must_be_course_team
41-
unless self.kind_of?(CourseTeam)
42-
errors.add(:type, 'must be CourseTeam')
43-
end
11+
def copy_to_course(course, name: self.name)
12+
copy_to(course, as: CourseTeam, name:)
4413
end
4514
end

app/models/mentored_team.rb

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,23 @@ def mentor
1414
alias_method :mentor_user, :mentor
1515

1616
# Adds members to the team who are not mentors
17-
def add_member(participant)
17+
def add_member(member)
18+
participant =
19+
case member
20+
when Participant
21+
member
22+
when User
23+
assignment&.participants&.find_by(user_id: member.id, parent_id: assignment&.id)
24+
else
25+
nil
26+
end
27+
return super(member) unless participant
28+
1829
# Fail fast if the participant's duty is 'mentor'.
19-
if participant.duty&.name&.downcase&.include?('mentor')
30+
if mentor_designation?(participant)
2031
return { success: false, error: 'Mentors cannot be added as regular members.' }
2132
end
22-
33+
2334
# If not a mentor, proceed with the standard add_member logic.
2435
super(participant)
2536
end
@@ -84,4 +95,8 @@ def mentor_must_be_present
8495
errors.add(:base, 'a mentor must be present')
8596
end
8697
end
98+
99+
def mentor_designation?(participant)
100+
participant.duty&.name&.downcase&.include?('mentor')
101+
end
87102
end

0 commit comments

Comments
 (0)