CSC/ECE 517 Spring 2025 - E2525 Reimplement review mapping controller.rb
Introduction
This project aims to re-implement the ReviewMappingController as part of a backend modernization effort. The goal is to build a clean, efficient, and maintainable version of the controller from scratch in the reimplementation-back-end repository, following Rails best practices and RESTful design principles. The new controller will be responsible only for handling HTTP requests such as creating, assigning, and deleting review mappings, while delegating all business logic to models, or helper modules. This approach will ensure better code organization, improved testability, and easier long-term maintenance. It ensures that each layer of the application handles a specific task: the controller manages request handling, the models encapsulate data-related logic, and service objects take care of complex workflows or operations. Overall, this reimplementation aims to establish a solid foundation for the controller that aligns with modern Rails development standards and supports the long-term sustainability of the Expertiza platform.
Requirements
- Reimplement the ReviewMappingController in the reimplementation-back-end repository without reusing legacy code.
- Ensure the controller only handles routing, parameter validation, and delegation of actions.
- Migrate business logic to models, service objects, or helper modules.
- Use polymorphic or strategy design patterns to manage different review mapping types.
- Provide thorough unit and integration tests along with inline documentation.
Existing Issues
- ReviewMappingController contains highly coupled and outdated code that mixes business logic with request handling.
- Hardcoded parameters reduce flexibility and adaptability across different environments.
- Inconsistent validation and minimal error handling lead to unclear user feedback.
- Several methods are overly long and difficult to maintain.
Design / Proposed Solution
The ReviewMappingController is responsible for managing the creation, assignment, and deletion of review mappings among participants in an assignment. Following reimplementation-back-end best practices, the controller strictly handles HTTP request routing, parameter validation, and delegates business logic to model methods. It interacts with models such as ReviewResponseMap, MetareviewResponseMap, and SelfReviewResponseMap, which maintain reviewer-reviewee relationships. Operations like dynamic reviewer assignment, metareviewer assignment, and self-review initiation are implemented as model methods to ensure a thin controller and a fat model design. Helper modules like ReviewMappingHelper provide small reusable utility methods, promoting modularity, extensibility, and maintainability while adhering to Rails conventions and RESTful principles.
Controller Diagram
Methods / API Calls
Method | HTTP Verb | Route | Purpose |
---|---|---|---|
list_mappings | GET | /review_mapping | List all mappings for an assignment |
create_mapping | POST | /review_mapping | Create a new reviewer → reviewee mapping |
assign_reviewer_dynamically | POST | /review_mapping/assign_reviewer_dynamically | Dynamically assign a reviewer to a team |
assign_metareviewer_dynamically | POST | /review_mapping/assign_metareviewer_dynamically | Dynamically assign a metareviewer to a review |
start_self_review | POST | /review_mapping/start_self_review | Initiate a self-review for a participant |
automatic_review_mapping | POST | /review_mapping/automatic_review_mapping | Automatically distribute reviewers across teams |
delete_mapping | DELETE | /review_mapping/:id | Delete a review mapping |
unsubmit_review | POST | /review_mapping/unsubmit_review | Unsubmit a submitted review |
add_calibration | POST | /review_mappings/add_calibration | Creates calibration review mapping between team and assignment |
add_reviewer | POST | /review_mappings/add_reviewer | Assigns reviewers to teams |
assign_reviewer_dynamically | POST | /review_mappings/assign_reviewer_dynamically | Dynamically assigns reviewer to team/topic |
review_allowed | GET | /review_mappings/review_allowed | Checks if reviewer can perform more reviews |
check_outstanding_reviews | GET | /review_mappings/check_outstanding_reviews | Checks for exceeded maximum outstanding reviews |
assign_quiz_dynamically | POST | /review_mappings/assign_quiz_dynamically | Assigns quiz to participant |
select_reviewer | GET | /review_mappings/select_reviewer | Selects and stores a contributor for review mapping |
select_metareviewer | POST | /review_mappings/:id/select_metareviewer | Assigns a specific metareviewer to a review mapping. |
add_metareviewer | POST | /review_mappings/:id/add_metareviewer | Randomly assigns a metareviewer using helper logic |
assign_metareviewer_dynamically | POST | /review_mappings/:id/assign_metareviewer_dynamically | Assigns a metareviewer dynamically from eligible participants. |
delete_all_metareviewers | DELETE | /review_mappings/delete_all_metareviewers/:assignment_id | Removes all metareview mappings for a given assignment. |
delete_outstanding_reviewers | DELETE | /review_mappings/delete_outstanding_reviewers/{assignment_id} | Deletes all unsubmitted review mappings for a given assignment. |
delete_reviewer | DELETE | /review_mappings/:id/delete_reviewer | Deletes a specific reviewer mapping by ID. |
delete_metareviewer | DELETE | /review_mappings/delete_metareviewer/:id | Deletes a metareviewer assignment from a review mapping. |
delete_metareview | DELETE | /review_mappings/:id/delete_metareview | Removes a specific metareview associated with a review mapping. |
unsubmit_review | DELETE | /review_mappings/:id/unsubmit_review | Marks a submitted review as unsubmitted or retracts the review submission. |
Pattern(s)
Strategy
The Strategy Pattern is a behavioral design pattern that enables selecting an algorithm’s behavior at runtime by defining a family of algorithms, encapsulating each one, and making them interchangeable, thus promoting flexibility and adherence to the Open/Closed Principle. In the context of ReviewMappingControlle, which handles multiple types of review mappings such as reviewer assignments, meta-reviews, and self-reviews—each with distinct business logic—the Strategy Pattern allows each mapping type to be implemented as a separate strategy class following a common interface. The controller simply delegates the task to the appropriate strategy based on the mapping type in the request, thereby abstracting complex decision-making logic away from the controller itself. This promotes separation of concerns, enhances extensibility by enabling the addition of new mapping types without altering existing code, eliminates repetitive conditional statements, and encourages a modular design that is easier to test, maintain, and understand.
SOLID Principle(s)
Open and Closed Principle
The Open/Closed Principle (OCP) is one of the SOLID principles of object-oriented design and states that software entities such as classes, modules, and functions should be open for extension but closed for modification. This means that the behavior of a system can be extended without altering its existing source code, promoting stability and reducing the risk of introducing bugs when requirements evolve. In the context of ReviewMappingController, applying OCP ensures that new review mapping types (such as peer-reviews, meta-reviews, or future additions) can be supported by introducing new strategy or service classes, rather than modifying the controller or existing logic. This principle encourages the use of abstraction, interfaces, and polymorphism to allow new behaviors to be added seamlessly. By adhering to OCP, the controller remains resilient to change, promoting a modular and maintainable architecture where existing tested code remains untouched, and new functionality is integrated through clearly defined extension points.
Single Responsibility Principle
The Single Responsibility Principle (SRP) is a fundamental concept in the SOLID principles of object-oriented design and states that a class or module should have only one reason to change, meaning it should only be responsible for one specific part of the system’s functionality. In the context of the ReviewMappingController, SRP implies that the controller should focus solely on handling HTTP requests—such as receiving input, validating parameters, and returning appropriate responses—while delegating all business logic (like reviewer assignment algorithms or validation checks) to separate service classes, models, or helper modules. This separation improves modularity, readability, and testability, as each component has a clearly defined role. Adhering to SRP reduces the complexity of individual components, makes the code easier to maintain, and ensures that changes in one area (e.g., business logic) do not inadvertently affect another (e.g., request routing), resulting in a more robust and scalable architecture.
Implementation
The review mapping-related endpoints were implemented using a consistent architectural approach that embraces Rails best practices. Each feature follows a thin-controller, rich-helper paradigm to ensure separation of concerns, where controller actions handle routing and validation while helper methods encapsulate the business logic. All endpoints are protected by JWT-based authentication, and routes are designed to be RESTful and easy to integrate. The implementation includes careful handling of STI (Single Table Inheritance) for models like ResponseMap and its subclasses such as TeamReviewResponseMap. All input parameters are validated to return appropriate error messages with standardized status codes like 404 for not found, 400/422 for invalid inputs, and 200 for success. In parallel, full Swagger documentation has been maintained to support testability and discoverability. The seed file was also enhanced to ensure that all entities like assignments, teams, participants, and questionnaire items are in place for robust testing of each endpoint.
Function-wise Implementation Details
1. list_mappings
- Controller fetches assignment and extracts query filters.
- Delegates filter logic to fetch_review_mappings helper.
- Uses ActiveRecord queries and includes associations.
- Returns reviewer/reviewee names and mapping types.
2. automatic_review_mapping
- Validates assignment presence and input parameters.
- Helper automatic_review_mapping_logic assigns reviewers randomly.
- Ensures no self-reviews and saves mappings.
3. automatic_review_mapping_strategy
- Extends automatic_review_mapping with strategy support.
- Supports default and round_robin strategies.
- Delegates logic to generate_review_mappings_with_strategy.
- Strategy logic selects reviewees based on pattern.
4. automatic_review_mapping_staggered
- Supports staggered circular mappings.
- Helper generate_staggered_review_mappings auto-detects assignment type.
- Avoids self-reviews and duplicates.
- Supports team and individual assignments.
5. assign_reviewers_for_team
- Assigns a specific number of reviewers to a given team.
- Avoids self-review by excluding team members.
- Uses helper assign_reviewers_to_team.
- Checks for duplicate mappings before insertion.
6. peer_review_strategy
- Assigns reviewers in a staggered fashion using round-robin.
- Supports both team and individual reviewers.
- Uses generate_peer_review_strategy to handle mapping logic.
7. save_grade_and_comment_for_reviewer
- Accepts reviewer input: grades, comments, rubric answers.
- Helper save_review_data validates participants and questionnaire items.
- Saves records to Response, Answer, and links to ResponseMap.
8. add_calibration
- Creates calibration review mappings between instructors and teams.
- Validates permissions and prevents duplicate calibrations.
- Uses ReviewMapping.create_calibration_review in the model layer.
9. select_reviewer
- Selects a contributor/team for review.
- Validates input using a helper method and stores selection in session.
10. add_reviewer
- Adds a reviewer to a team for a specific assignment.
- Validates user identity, participant existence, and prevents duplicates.
- Creates ReviewMapping entries on success.
11. assign_reviewer_dynamically
- Dynamically assigns reviewers to teams based on topic or preference.
- Validates review limits and outstanding reviews.
- Supports both topic-based and open assignments.
12. review_allowed
- Checks if a reviewer is allowed to perform more reviews based on current limits.
- Uses ReviewResponseMap.review_allowed? logic for validations.
13. check_outstanding_reviews
- Verifies if a reviewer has pending or uncompleted reviews.
- Implements review counting logic with error handling.
14. start_self_review
- Creates a self-review mapping for the participant.
- Validates assignment, prevents duplicates, and uses team information.
15. assign_quiz_dynamically
- Dynamically assigns a quiz to a reviewer.
- Validates reviewer, assignment, and questionnaire IDs.
- Creates QuizResponseMap entries.
16. select_metareviewer
- Controller fetches review mapping and metareviewer using provided IDs.
- Checks if the metareviewer is already assigned to the mapping.
- If not assigned, creates a MetareviewResponseMap linking the metareviewer to the reviewer.
- Returns appropriate JSON messages for success or error conditions.
17.add_metareviewer
- Controller locates the review mapping by ID.
- Delegates the assignment logic to add_metareviewer helper method.
- Helper selects a random eligible metareviewer from the assignment's participants.
- Creates a MetareviewResponseMap entry.
- Returns a success or failure message based on assignment feasibility.
18. assign_metareviewer_dynamically
- Controller identifies the ResponseMap and corresponding Assignment.
- Delegates participant selection logic to helper: find_available_metareviewer.
- Creates a new MetareviewResponseMap entry linking selected participant.
- Returns a JSON object indicating the outcome and assigned metareviewer ID.
19. delete_outstanding_reviewers
- Controller receives an assignment_id.
- Queries all ReviewResponseMap entries with no associated Response (i.e., pending reviews).
- Deletes all such mappings using delete_all.
- Returns a message indicating how many records were removed.
20. delete_all_metareviewers
- Controller identifies the assignment and retrieves all related participant IDs.
- Deletes all MetareviewResponseMap entries whose reviewee_id matches any participant in the assignment.
- Responds with a JSON count of the deleted metareviewer mappings.
21. delete_reviewer
- Controller locates a ResponseMap by its ID.
- If found, deletes the record.
- Returns a success message or a 404 if the mapping is not found.
22.delete_metareviewer
- Controller locates a MetareviewResponseMap by its ID.
- If found, deletes the metareviewer mapping.
- Returns a success message or a 404 if the mapping is not found.
23.delete_metareview
- Controller finds a MetareviewResponseMap by ID.
- Deletes the record if it exists.
24.unsubmit_review
- Controller fetches the ResponseMap by ID.
- Checks for an associated Response object.
- Sets submitted_at to nil if present to unsubmit.
Implementation Examples
Here are a few representative code snippets of POST,GET,DELETE function types implemented:
assign_metareviewer_dynamically
This function dynamically assigns an available metareviewer to a given review mapping. It finds the appropriate assignment and review mapping, selects an eligible metareviewer, and creates a new metareview mapping, returning an error if any step fails.
# POST /api/v1/review_mappings/:id/assign_metareviewer_dynamically def assign_metareviewer_dynamically review_mapping = ResponseMap.find_by(id: params[:id]) return render json: { error: 'Review mapping not found' }, status: :not_found unless review_mapping assignment = Assignment.find_by(id: review_mapping.reviewed_object_id) return render json: { error: 'Assignment not found' }, status: :not_found unless assignment metareviewer = ReviewMappingsHelper.find_available_metareviewer(assignment, review_mapping) return render json: { error: 'No available metareviewer found' }, status: :unprocessable_entity unless metareviewer MetareviewResponseMap.create!( reviewed_object_id: review_mapping.id, reviewer_id: metareviewer.id, reviewee_id: review_mapping.reviewer_id ) render json: { message: 'Metareviewer dynamically assigned successfully', metareviewer_id: metareviewer.id }, status: :ok rescue ActiveRecord::RecordInvalid => e render json: { error: e.message }, status: :unprocessable_entity end
delete_reviewer
This function deletes a specific review mapping (reviewer assignment) based on the provided ID, removing the reviewer from the review process and returning a success or error message.
# DELETE /api/v1/review_mappings/:id/delete_reviewer def delete_reviewer review_mapping = ResponseMap.find_by(id: params[:id]) return render json: { error: 'Review mapping not found' }, status: :not_found unless review_mapping review_mapping.destroy render json: { message: 'Reviewer mapping deleted successfully' }, status: :ok end
select_reviewer
This function selects a contributor (team) for review mapping, stores the selected contributor in the session for tracking during the review assignment process, and returns the contributor's information or an error if not found.
# GET /api/v1/review_mappings/select_reviewer # Selects a contributor for review mapping and stores it in the session # This is used in the review assignment process to track the selected contributor def select_reviewer @contributor = Team.find(params[:contributor_id]) session[:contributor] = @contributor render json: @contributor, status: :ok rescue ActiveRecord::RecordNotFound render json: { error: "Contributor not found" }, status: :not_found end
Testing Plan
Initial Setup
1. Seed the database
Run `rails db:seed` to populate realistic test data.
2. Start the server - Launch the app with `rails server`.
3. JWT Authentication - Use `/api/v1/login` to get a JWT token and authorize Swagger (`Bearer <token>`).
Test Execution Workflow
- Navigate to the Swagger UI (/api-docs) and authorize via JWT.
- For each endpoint, supply valid and edge-case parameters drawn from seed data.
- Execute the request and examine the HTTP response code and payload.
Endpoint-specific Test Coverage
1.list_mappings
- 200 OK: Valid assignment ID, filtered mappings returned
- 404: Assignment does not exist
2.automatic_review_mapping
- 200 OK: Valid input, randomized mappings generated
- 400/404: Missing assignment or malformed payload
3.automatic_review_mapping_strategy
- 200 OK: Valid strategies (default, round_robin)
- 422: Unsupported strategy
4.automatic_review_mapping_staggered
- 200 OK: Correct staggered mapping for individuals/teams
- 404: Invalid assignment
5.assign_reviewers_for_team
- 200 OK: Reviewers correctly mapped to all team members
- 404/422: Team not found or reviewer pool insufficient
6.peer_review_strategy
- 200 OK: Valid round-robin distribution
- 400/404: Missing data or invalid assignment ID
7.save_grade_and_comment_for_reviewer
- 200 OK: Response and answers created
- 422: Invalid rubric input or participant mismatch
8.add_calibration
- 200 OK: Calibration mapping established
- 403/422: Unauthorized or duplicate entry
9.select_reviewer
- 200 OK: Reviewer session updated
- 400/404: Invalid contributor ID
10.add_reviewer
- 200 OK: Reviewer successfully assigned
- 422/404: Participant mismatch or user not found
11.assign_reviewer_dynamically
- 200 OK: Reviewer assigned via topic selection
- 422: Over-assignment detected
12.review_allowed
- 200 OK: Returns true/false based on review count
- 422/404: Incomplete parameters or non-existent reviewer
13.check_outstanding_reviews
- 200 OK : Boolean response indicating pending reviews
14.start_self_review
- 200 OK : Self-review mapping saved
- 422/404: Review already exists or assignment invalid
15.assign_quiz_dynamically
- 200 OK: Quiz mapped to participant
- 422/404: Invalid input or questionnaire ID
16. select_metareviewer
- 200 OK: Metareviewer already assigned
- 201 Created: Metareviewer successfully assigned
- 404 Not Found: Review mapping or metareviewer not found
17. add_metareviewer
- 200 OK: Random eligible metareviewer assigned
- 422 Unprocessable Entity: No available metareviewers found
18. assign_metareviewer_dynamically
- 200 OK: Valid input, metareviewer assigned successfully
- 404 Not Found: Review mapping or assignment not found
- 422 Unprocessable Entity: No valid metareviewers available
19. delete_outstanding_reviewers
- 200 OK: Pending reviewer mappings deleted
- 200 OK: No outstanding mappings to delete (0 removed)
20. delete_all_metareviewers
- 200 OK: All metareviewer mappings deleted successfully
- 200 OK: No metareviewer mappings to delete
- 404 Not Found: Assignment does not exist
21. delete_reviewer
- 200 OK: Reviewer mapping deleted successfully
- 404 Not Found: Review mapping does not exist
22. delete_metareviewer
- 200 OK: Metareviewer mapping deleted successfully
- 404 Not Found: Metareview mapping not found
23. delete_metareview
- 200 OK: Metareview mapping deleted successfully
- 404 Not Found: Metareview mapping not found
24. unsubmit_review
- 200 OK: Review unsubmitted successfully
- 404 Not Found: Review mapping or response not found
- 422 Unprocessable Entity: Failed to update the response
Sample Testing Block (Swagger UI)
All endpoints were tested using Swagger UI to ensure correctness. Below is an example test for the save_grade_and_comment endpoint:
Test Case: Save Grade and Comment
Request URL:
POST /api/v1/review_mappings/save_grade_and_comment
Request Headers:
Authorization: Bearer <your_token_here> Content-Type: application/json
Request Body:
{ "participant_id": 3, "assignment_id": 1, "reviewee_id": 1, "grade": 9.5, "comment": "Very thoughtful and constructive feedback!", "answers": [ { "item_id": 1, "score": 9, "comment": "Strong analysis and good structure." }, { "item_id": 2, "comment": "Keep up the good work!" } ], "is_submitted": true }
Response: You should get a 200 OK with:
{ "message": "Grade and comment saved successfully." }
If anything goes wrong, you'll get a helpful 422 error like:
"Reviewee not part of assignment" "Invalid item ID" "Score must be between X and Y"
Team Information
Mentor
- Anish Toorpu
Team Members
- Niharika Maruvanahalli Suresh
- Cristian Salitre
- Aditya Anand Kulkarni