CSC/ECE 517 Spring 2025 - E2526 Reimplement Teams and Participant hierarchies
Introduction
This project reimplements the Team and Participant hierarchies within the new Expertiza system. It introduces cleaner, modular structures for Participant, AssignmentParticipant, and CourseParticipant. The redesign also addresses legacy code smells and shifts logic to follow SOLID principles, improving maintainability, testability, and extensibility.
Requirements
- Reimplement Participant-related Classes: Fully reimplement the Participant, AssignmentParticipant, and CourseParticipant classes using clean object-oriented design, ensuring each class has a well-defined responsibility and follows SOLID principles.
- Refactor the Team Hierarchy: Integrate the Team hierarchy refactoring from OSS Project 3, improving upon it by addressing feedback provided in
Pull Request #2941
. - Address Code Smells and Design Flaws: Eliminate all SRP violations and code smells from the original Participant, AssignmentParticipant, and CourseParticipant implementations.
- Write Comprehensive Tests: Implement thorough RSpec tests for all model behaviors, including edge cases.
Existing Issues
1. Participant Hierarchy Violations
The current implementations of participant.rb
, assignment_participant.rb
, and course_participant.rb
contain multiple issues that must be addressed:
- Refactor code to fully comply with the Single Responsibility Principle (SRP).
- Eliminate code smells such as misplaced methods.
- Write sufficient RSpec tests to verify correct behavior under various conditions.
- Enhance inline documentation and comments to clearly explain:
- The purpose of each method.
- Justification for retaining or removing existing logic.
- Any key design decisions made during the implementation.
2. Team Hierarchy Refactor
The existing team.rb
, assignment_team.rb
, course_team.rb
, and mentored_team.rb
classes require rework to align with the updated architecture:
- Reimplement these classes using the clean design introduced in OSS Project 3.
- Address key feedback from
Pull Request #2941
, such as:- Rename methods like user? to more expressive terms like has_as_member? to improve clarity.
- Remove or clearly justify any commented-out legacy code.
- Refactor create_new_team() to better reflect intent (e.g., assign_team_to_topic) and clean up legacy logic based on outdated user-team assumptions.
Design / Proposed Solution
team.rb Reimplementation
In the new team.rb implementation, we renamed is_member? to has_as_member?. The original method name implied that the team was a member of the user, rather than the other way around. Additionally, we moved the add_participant() method from AssignmentTeam to Team to apply the Factory Method Pattern. This change allows both CourseTeam and AssignmentTeam to reuse the same logic for participant creation while deferring type-specific behavior (like selecting the correct Participant subclass) to the factory method. All of this was done based on the feedback from the instructor provided in Pull Request #2941
.
Below is a breakdown of each method that was changed/added in the revised Team class and its role:
has_as_member?
Method
Description
This method checks whether a given user is a member of the team.
Changes Made
Renamed from is_member? to has_as_member? for clarity.
add_participant
Method
Description
Adds a Participant to the team, given a user object. It first checks if a corresponding participant already exists, and if not, creates a new one using the appropriate subclass, either AssignmentParticipant or CourseParticipant.
Changes Made
Moved from AssignmentTeam to the parent Team class to promote reuse and apply the Factory Method Pattern.
assignment_team.rb Reimplementation
In the new assignment_team.rb implementation, we added the participant_class method to apply the Factory Method pattern, as used in the Team class's add_participant method. Additionally, we renamed link_user_and_topic to assign_team_to_topic to reflect the modern team-based signup model. These changes were made in response to the instructor’s feedback, as noted in Pull Request #2941
.
Below is a breakdown of each method that was changed/added in the revised AssignmentTeam class and its role:
participant_class
Method
Description
This method implements the Factory Method Pattern by returning the class responsible for handling participants associated with an AssignmentTeam, which is AssignmentParticipant.
Changes Made
Introduced as part of the Factory Method refactor. It enables the shared Team methods (like add_participant) to dynamically resolve and instantiate the appropriate subclass of Participant (AssignmentParticipant in this case) based on the context of the team type (AssignmentTeam).
assign_team_to_topic
Method
Description
This method assigns a team to a signup topic and links all its members to the corresponding team node.
Changes Made
Was changed from link_user_and_topic to assign_team_to_topic to reflect the modern team-based signup model. Previously, it accepted a user ID, which was a remnant from when users not teams were assigned to topics.
course_team.rb Reimplementation
In the new course_team.rb implementation, we decided to add a participant_class method to apply the Factory Method Pattern that is used in the Team class add_participant Method based on the feedback from the instructor provided in Pull Request #2941
.
Below is a breakdown of each method that was changed/added in the revised CourseTeam class and its role:
participant_class
Method
Description
This method implements the Factory Method Pattern by returning the class responsible for handling participants associated with a CourseTeam, which is CourseParticipant.
Changes Made
Introduced as part of the Factory Method refactor. It enables the shared Team methods (like add_participant) to dynamically resolve and instantiate the appropriate subclass of Participant (CourseParticipant in this case) based on the context of the team type (CourseTeam).
mentored_team.rb Reimplementation
In the new mentored_team.rb implementation, we decided to remove the MentorManagement class and integrate its functionality directly into MentoredTeam and change add_member() to take participant instead of user and assignment id. This decision was made not only to improve cohesion and eliminate unnecessary indirection, but also to address structural feedback from the instructor provided in Pull Request #2941
.
Below is a breakdown of each method that was changed/added in the revised MentoredTeam class and its role:
add_member
Method
Description
Adds a participant to the team using the superclass implementation and then automatically assigns a mentor to the team if the conditions are met (such as minimum team size and absence of an existing mentor).
Changes Made
Changed from add_member(user, assignment_id = nil) to add_member(participant), since the participant contains all this information.
import_team_members
Method
Description
Takes a hash of team member names and iterates through them. For each valid name, it looks up the corresponding user, ensures they are not already on the team, finds their AssignmentParticipant, and adds them to the team via add_member. It also triggers mentor assignment as part of this process.
Changes Made
Changed the call inside it for add_member(user, assignment_id = nil) to add_member(participant).
assign_mentor
Method
Description
A private method that encapsulates the full logic of mentor assignment. It checks several conditions (e.g., whether mentoring is enabled, team has a topic, mentor already exists, etc.). If all conditions are satisfied, it selects an available mentor using the select_mentor method and adds them to the team, followed by notification emails.
Changes Made
No functional changes were made; the method was simply moved from MentorManagement to MentoredTeam.
select_mentor
Method
Description
A private method that implements the algorithm for choosing the most suitable mentor. It selects among participants marked as mentors for the given assignment and returns the one who is currently mentoring the fewest teams. Ties are automatically broken by Hash#sort_by. This ensures a balanced distribution of mentor assignments.
Changes Made
No functional changes were made; the method was simply moved from MentorManagement to MentoredTeam.
zip_mentors_with_team_count
Method
Description
A private method that generates a mapping of mentor user_ids to the number of teams each is currently mentoring, used by the select_mentor method. It performs a grouped count on the TeamsUser table and returns a sorted list of mentors by workload.
Changes Made
No functional changes were made; the method was simply moved from MentorManagement to MentoredTeam.
mentors_for_assignment
Method
Description
A private method that returns all Participant records for a given assignment where the participant has can_mentor: true. This filters potential mentor candidates from the larger participant pool and serves as the base query for mentor selection logic.
Changes Made
No functional changes were made; the method was simply moved from MentorManagement to MentoredTeam.
notify_team_of_mentor_assignment
Method
Description
A private method that sends an email notification to all members of a team informing them that a mentor has been assigned. The message includes the mentor’s name and email, the assignment name, and a list of team members.
Changes Made
No functional changes were made; the method was simply moved from MentorManagement to MentoredTeam.
notify_mentor_of_assignment
Method
Description
A private method that sends an email to the newly assigned mentor notifying them of their assignment. The email includes the assignment name and a full list of current team members to help the mentor familiarize themselves with the team.
Changes Made
No functional changes were made; the method was simply moved from MentorManagement to MentoredTeam.
participant.rb Reimplementation
In the new implementation of the Participant model, we refactored several methods to improve clarity, maintainability, and alignment with the Single Responsibility Principle (SRP). The topic_name method was removed, as topics are associated with teams rather than participants, making its presence in Participant a design inconsistency. We also relocated the mail_assigned_reviewers method to a dedicated mailer class, where notification logic more appropriately belongs. The authorization method was renamed for clarity, and sort_by_name was eliminated due to its overly narrow use case and lack of general applicability. These changes help decouple responsibilities and make the class easier to test and extend.
Below is a breakdown of each method that was changed/added in the revised Participant class and its role:
name
Method
Description
Returns the participant’s full name by delegating the call to the associated user’s fullname method.
Changes Made
Renamed method from fullname to name.
responses
Method
Description
Returns a list of responses by mapping each response map associated with the participant to its corresponding response.
Changes Made
No functional changes were made; the method was reimplemented as is.
username
Method
Description
Returns the participant’s name by delegating the call to the associated user’s `name` method.
Changes Made
Renamed method from name to username.
delete
Method
Description
Deletes the participant if no associated response maps or team exist, or if forced; otherwise, raise an exception.
Changes Made
No functional changes were made; the method was reimplemented as is.
force_delete
Method
Description
Destroys all associated response maps and remove the team if this is the last participant; then delete the participant itself.
Changes Made
No functional changes were made; the method was reimplemented as is.
task_role
Method
Description
Determines the participant’s role (e.g., mentor, reviewer, submitter) based on permission flags.
Changes Made
Was renamed to task_role() instead of authorization()
self.export
Method
Description
Exports participant data to a CSV file based on the selected options for personal details, role, parent, email settings, and handle.
Changes Made
Update to align with new framework https://wiki.expertiza.ncsu.edu/index.php?title=CSC517_Spring_2019/E1923_New_Framework_For_Import/Export
self.export_fields
Method
Description
Returns a list of CSV header fields to include in the export, based on the provided options.
Changes Made
The method was updated to align with new framework https://wiki.expertiza.ncsu.edu/index.php?title=CSC517_Spring_2019/E1923_New_Framework_For_Import/Export
parent_absent?
Method
Description
Ensures that a Participant belongs to exactly one parent entity, either an Assignment or a Course, but not both.
Changes Made
It was added as a private method for validation purposes.
assignment_participant.rb Reimplementation
In the revised implementation of the AssignmentParticipant model, we focused on improving code clarity, reducing duplication, and strengthening adherence to robust object-oriented principles. Several methods were delegated to the Participant superclass to promote reuse and maintain DRYness, particularly team, path, and set_handle, whose logic applies universally across all participant types. For instance, the get_ prefix was removed from reviewer to align with Ruby idioms and enhance readability. Additionally, methods tied to the deprecated TeamsUser model, such as team_user and duty_id, were removed in favor of the newly introduced TeamsParticipant abstraction. These changes support a cleaner class hierarchy, minimize SRP violations, and establish a more modular foundation for future extensions to participant behavior.
Below is a breakdown of each method that was changed or added in the revised AssignmentParticipant class and its role:
dir_path
Method
Description
Returns the directory path of the assignment associated with this participant.
Changes Made
Renamed for directory_path for clarity and used safe navigation (&.).
reviewers
Method
Description
Fetches all participants who reviewed the team this participant is part of.
Changes Made
Replaced loop and .push with map for conciseness.
set_current_user
Method
Description
Stub method added for interface compatibility with other components like AssignmentTeam.
Changes Made
No functional changes were made; preserved as is for future maintainers.
copy_to_course
Method
Description
Creates a CourseParticipant
entry for the same user in a specified course if it doesn't already exist.
Changes Made
Used ::CourseParticipant explicitly to avoid class ambiguity and renamed parent_id to course_id.
feedback
Method
Description
Retrieves feedback maps (peer review feedback) related to this participant.
Changes Made
No functional changes were made; the method was reimplemented as is.
reviews
Method
Description
Returns all peer review responses for the participant’s team.
Changes Made
No functional changes were made; the method was reimplemented as is.
get_reviewer
Method
Description
Returns the reviewer context: either the team or the participant itself, depending on whether team reviewing is enabled for the assignment.
Changes Made
Renamed to reviewer because get_ prefix is non-idiomatic in Ruby.
get_logged_in_reviewer_id
Method
Description
Returns this participant's ID. Intended as a polymorphic method to match the interface in AssignmentTeam
.
Changes Made
Renamed to logged_in_reviewer_id for clarity thereby adhering to Ruby's idiomatic naming conventions.
current_user_is_reviewer?
Method
Description
Checks if the current user is the same as the one associated with this participant.
Changes Made
No functional changes were made; the method was reimplemented as is.
quizzes_taken
Method
Description
Returns quiz response maps submitted by this participant.
Changes Made
No functional changes were made; the method was reimplemented as is.
metareviews
Method
Description
Returns all metareview response maps (i.e., reviews of reviews) for this participant.
Changes Made
No functional changes were made; the method was reimplemented as is.
teammate_reviews
Method
Description
Returns teammate review maps for this participant. Reflects peer evaluations among team members.
Changes Made
No functional changes were made; the method was reimplemented as is.
bookmark_reviews
Method
Description
Returns any bookmark rating response maps for this participant.
Changes Made
No functional changes were made; the method was reimplemented as is.
files(directory)
Method
Description
Recursively collects all files within the specified directory.
Changes Made
Replaced recursive loop with Dir.glob for simplicity and efficiency.
team
Method
Description
Fetches the AssignmentTeam
instance this participant belongs to by delegating to a class method.
Changes Made
Removed duplicate team method from assignment_participant.rb to promote DRYness.
self.import
Method
Description
Imports a participant from a given row. If the user does not exist, it creates one and then adds them to the assignment.
Changes Made
Refactored to use keyword arguments (session:, assignment_id:); improved error messaging; renamed parent_id to assignment_id.
assign_copyright
Method
Description
Assigns publishing rights by verifying a digital signature with a private key.
Changes Made
No functional changes were made; the method was reimplemented as is because logic is participant-specific.
verify_digital_signature
Method
Description
Checks if the user’s public key matches the digital signature generated from the provided private key.
Changes Made
Renamed to verify_signature; streamlined OpenSSL usage.
set_handle
Method
Description
Sets a unique handle for the participant based on their user’s handle or name. Ensures no duplication within the assignment.
Changes Made
Improved condition readability using strip.empty?; renamed parent_id to assignment_id; removed unnecessary ! from save!
path
Method
Description
Returns the complete file path for this participant's team, based on the assignment path and team directory number.
Changes Made
Used File.join for path construction; added early return if assignment or team is nil.
review_file_path
Method
Description
Constructs a file path for uploading review artifacts. Handles both team and non-team scenarios.
Changes Made
Refactored logic for clarity; used guard clauses and File.join; removed legacy parameterization logic.
current_stage
Method
Description
Returns the current stage of the assignment for this participant, accounting for topic signup.
Changes Made
Commented out due to schema incompatibility; marked for future support.
stage_deadline
Method
Description
Returns the due date for the current stage, handling both staggered and non-staggered assignments.
Changes Made
Commented out due to schema incompatibility; marked for future support.
duty_id
Method
Description
Returns the duty ID from the TeamsUser record for this participant’s team.
Changes Made
Removed as TeamsUser has been fully replaced by TeamsParticipant.
team_user
Method
Description
Finds the TeamsUser linking this participant’s user to their team.
Changes Made
Removed as TeamsUser has been fully replaced by TeamsParticipant.
course_participant.rb Reimplementation
In the revised implementation of the CourseParticipant model, we focused on improving cohesion, reducing redundant logic, and aligning more closely with object-oriented principles such as SRP and the Liskov Substitution Principle. The copy method was refactored to streamline control flow without violating class responsibilities. The import method, originally overloaded with multiple responsibilities, was modularized using helper functions to promote clarity and reduce code smells. Additionally, the path method was moved to the Participant superclass to enable reuse across participant types, thereby reducing duplication and enhancing maintainability. These changes make the class easier to understand, extend, and test.
Below is a breakdown of each method that was changed or added in the revised CourseParticipant class and its role:
copy_to_assignment
Method
Description
Copies the course participant into a specific assignment by creating an AssignmentParticipant
record if it doesn't already exist. It also assigns a handle to the new participant.
Changes Made
Refactored to reduce inline creation logic and improve readability; specifically, removed manual nil check and kept logic self-contained in CourseParticipant, respecting the Liskov Substitution Principle. Also, changed the name from copy() to copy_to_assignment().
self.import
Method
Description
Imports a course participant from a hash. If the user doesn't exist, it is created and then added to the course.
Changes Made
The import method for CourseParticipant was refactored to improve clarity, robustness, and data integrity.
set_handle
Method
Description
This method assigns a unique and valid handle to the CourseParticipant based on the associated user's information.
Changes Made
Added as a helper method to copy_to_assignment()
SOLID Principle(s)
Single Responsibility Principle: "A class should only have one responsibility and should never have more than one reason to change."
There are many violations of the Single Responsibility Principle (SRP) across all Participant classes. For example, AssignmentParticipant contains methods like reviews() and get_reviewer(), which should be the responsibility of the Team class. Similarly, Participant includes mail_assigned_reviewers() and email(), which should belong to the Mailer class.
Test Plan
We will write tests to ensure that all model validations function correctly for each class, including Participant, AssignmentParticipant, CourseParticipant, and all team-related models. We will verify that belongs_to, has_many, and other associations are correctly configured and that no orphaned or dangling records remain after creation or deletion. Special attention will be given to business logic, particularly methods reused or inherited across subclasses. Additionally, we will test edge conditions like minimum team size thresholds, permission flags (e.g., can_mentor), and mentor-selection criteria to ensure the intended behavior is preserved.
Team Information
Mentor:
- Ed Gehringer
Team Members:
- Ahmed Hassan (aohassan)
- Rameez Malik (remalik)
- Moaad Benkaraache (mbenkar)
Links
- Link to Project 3 Team hierarchy Refactoring: https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Spring_2025_-_E2503._Refactor_the_Team_hierarchy
- Link to reimplementation-back-end Expertiza Repository: https://github.com/expertiza/reimplementation-back-end
- Link to Forked Repository: https://github.com/AhmedOHassan/reimplementation-back-end
- Link to Pull Request: https://github.com/expertiza/reimplementation-back-end/pull/198
- Link to Video Walkthrough: https://youtu.be/yanx2hjBhLw