CSC/ECE 517 Spring 2025 - E2525 Reimplement review mapping controller.rb

From Expertiza_Wiki
Jump to navigation Jump to search

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

References