CSC/ECE 517 Spring 2024 - E2443 Reimplement grades controller

From Expertiza_Wiki
Jump to navigation Jump to search

Expertiza

Expertiza is an open-source web application built on Ruby on Rails that enables instructors to create customized assignments with topic lists for students to choose from. It facilitates team formation, allowing students to collaborate on assignments and projects. Expertiza supports peer review processes where students can provide feedback on each other's submissions across various document formats, including URLs and wiki pages. This freely available platform provides a flexible and comprehensive solution for managing assignments, teamwork, and peer evaluations.

Introduction

We're refactoring the grades_controller.rb in Expertiza, enhancing its codebase to adhere to DRY and design principles, improving readability, and reducing redundancy. Our focus includes thorough rswag testing of the grades_controller method and demonstrating Swagger UI integration.

Problem Statement

The reimplementation project entails:

  1. Refactoring Grades Controller: Enhancing code clarity, optimizing loops, and adding comments for unclear lines of code to improve maintainability and readability.
  2. Removing Redundant Methods: Identifying and eliminating unused methods in the controller to reduce complexity and streamline functionality.
  3. CRUD Operations: Implementing CRUD operations (Create, Read, Update, Delete) in the controller for efficient data management.
  4. Adherence to DRY Principle: Ensuring that the reimplementation follows the Don't Repeat Yourself (DRY) principle to avoid duplication and improve code efficiency.
  5. Testing with RSwag: Writing comprehensive tests using RSwag for each controller method to ensure functionality and integration with Swagger UI, followed by a video demonstration showcasing the Swagger UI integration and functionality of the reimplemented controller.

Plan for Reimplementation of GradesController

Code Clarity:

Existing methods such as view, view_my_scores, and view_team will undergo refinement to improve clarity and readability. This includes adding inline comments within complex methods to provide better understanding. Additionally, loops within make_chart will be streamlined for improved performance, while conditional statements within action_allowed? will be simplified for easier comprehension.

Authorization Logic:

Authorization logic within controller actions will be refined to simplify the process and improve error handling. This involves extracting authorization concerns into separate methods like student_privileges_allowed?, ta_privileges_allowed?, etc. Furthermore, error messages within action_allowed? will be enhanced to provide clearer feedback to users in case of access denial.

View Method Optimization:

In view methods like view, view_my_scores, and view_team, efforts will be made to streamline code and reduce dependencies. This includes optimizing database queries to improve performance and removing redundant code to enhance maintainability.

Edit and Update Actions:

Edit and update actions such as edit and update will be enhanced to adhere to RESTful conventions. Specifically, parameter handling will be simplified to ensure consistency and robust error handling will be implemented to gracefully handle invalid input.

Chart Generation Optimization:

Chart generation methods will be optimized to improve efficiency and scalability. This includes implementing caching mechanisms for precomputed data within make_chart to enhance responsiveness. Additionally, exploration of client-side rendering using JavaScript libraries like Chart.js will be considered to further improve performance.

Self-Review Logic Enhancement:

Self-review logic within self_review_finished? will undergo validation to ensure accuracy and effectiveness. This includes refining edge cases where self-review completion may not be accurately detected and enhancing user guidance for a smoother experience.

Redirection Logic Improvement:

Redirection logic within redirect_when_disallowed will be improved to simplify and consolidate redirection processes. This involves enhancing error handling to provide clearer feedback to users and optimizing redirection logic to minimize unnecessary redirects.

Comprehensive Testing with RSwag:

Comprehensive testing with RSwag will be conducted to validate API endpoints and ensure thorough test coverage. This includes writing tests for each controller method, covering both positive and negative test cases, and integrating automated testing into the CI pipeline for consistency and reliability.

Design Principles

Single Responsibility Principle (SRP):

  • Each action in the GradesController will be responsible for a specific task related to managing grades.
  • Actions will be refactored to separate concerns such as data retrieval, computation, and view rendering.
  • For example, the `view` action will focus solely on retrieving grading data and rendering the grading report.

Don't Repeat Yourself (DRY) Principle:

  • Code duplication in the GradesController will be eliminated by extracting common functionality into helper methods or modules.
  • Repetitive logic, such as retrieving questions or calculating penalties, will be refactored to promote code reusability and maintainability.

Encapsulation:

  • Data and behavior within the GradesController will be encapsulated within appropriate methods and classes to minimize dependencies.
  • Access to instance variables and controller actions will be limited, promoting encapsulation and separation of concerns.

Dependency Inversion Principle (DIP):

  • The GradesController will depend on abstractions, interfaces, or higher-level modules instead of concrete implementations.
  • Dependency injection or inversion of control will be used to decouple the controller from specific database or service implementations.

Testing with RSwag:

Integration Testing:

  • Integration tests using RSwag will be written to verify the behavior of each controller action, including `view`, `view_my_scores`, and `view_team`.
  • CRUD operations (Create, Read, Update, Delete) will be tested to ensure proper data management and interaction with the database.

Swagger UI Integration:

  • RSwag will be integrated with Swagger UI to provide a user-friendly interface for interacting with API endpoints.
  • Swagger UI will accurately reflect the API documentation and allow users to test endpoints interactively.

Parameter Validation:

  • Endpoint parameters, such as assignment IDs and participant IDs, will be tested with various input values to ensure proper handling and error reporting.
  • Input parameters such as IDs, query parameters, and request bodies will be validated to ensure data integrity.

Error Handling:

  • Error handling scenarios, including validation errors, resource not found, unauthorized access, and server errors, will be tested.
  • Error responses will contain appropriate status codes, error messages, and error details as per the API contract.

Security Testing:

  • API endpoints will be assessed for vulnerabilities such as SQL injection, cross-site scripting (XSS), and sensitive data exposure.
  • Authentication and authorization mechanisms will be evaluated for effectiveness to ensure secure access to grading functionalities.

Performance Testing:

  • Performance of API endpoints under various load conditions will be evaluated using tools like JMeter or Gatling.
  • Response times, throughput, and resource utilization will be measured to identify performance bottlenecks and optimize critical paths.

Documentation Verification:

  • API documentation generated by RSwag will be validated to accurately reflect implemented endpoints, parameters, and responses.
  • Documentation will be kept up-to-date and aligned with the behavior of API endpoints.

By following these design principles and testing strategies, the reimplementation of the GradesController will result in a well-designed, maintainable, and thoroughly tested component, meeting project requirements effectively.

Initial Attempt in re-implementation

The initial implementation of the grades_controller in the API namespace enhances the code structure, readability, and maintainability through a series of thoughtful improvements. These changes align with the principles of modularity, reusability, and clarity, ensuring the controller's effectiveness in handling grading-related tasks within the application.

One significant improvement is the organization of controller actions, each serving a specific purpose and adhering to RESTful conventions. Actions such as `view`, `view_my_scores`, `view_team`, `edit`, `update`, and `save_grade_and_comment_for_submission` are clearly defined and encapsulate distinct functionalities. This structured approach enhances code readability and makes it easier to understand the controller's responsibilities at a glance.

     def view
       @assignment = Assignment.find(params[:id])
       @scores = review_grades(@assignment, retrieve_questions(@assignment.questionnaires, @assignment.id))
       render json: grading_report_data(@assignment, @scores)
     end
 
     def view_my_scores
       @questions = retrieve_questions(@participant.assignment.questionnaires, @participant.assignment.id)
       @pscore = participant_scores(@participant, @questions)
       make_chart
       @summary = prepare_feedback_summary
       render json: my_scores_data
     end
 
     def view_team
       @assignment = @participant.assignment
       @team = @participant.team
       @team_id = @team.id
       @questions = retrieve_questions(@assignment.questionnaires, @assignment.id)
       @pscore = participant_scores(@participant, @questions)
       @penalties = calculate_penalty(@participant.id)
       @vmlist = populate_view_models
       @current_role_name = current_role_name
       render json: team_view_data
     end
 
     def edit
       @questions = list_questions(@participant.assignment)
       @scores = participant_scores(@participant, @questions)
       render json: edit_data
     end
 
     def update
       if @participant.update(participant_params)
         render json: @participant
       else
         render json: @participant.errors, status: :unprocessable_entity
       end
     end
 
     def save_grade_and_comment_for_submission
       if @participant.team.update(grade_for_submission: params[:grade_for_submission], comment_for_submission: params[:comment_for_submission])
         render json: { success: true }
       else
         render json: { success: false, errors: @participant.team.errors.full_messages }, status: :unprocessable_entity
       end
     end

Furthermore, the controller leverages `before_action` callbacks to set the participant before executing certain actions, reducing redundancy and promoting DRY (Don't Repeat Yourself) principles.

class GradesController < ApplicationController

   before_action :set_participant, only: [:view_my_scores, :view_team, :edit, :update, :save_grade_and_comment_for_submission]

By defining the `set_participant` method as a callback for actions that require participant data, the controller avoids repetitive code and ensures consistency in handling participant-related tasks across different actions.

   def set_participant
       @participant = AssignmentParticipant.find(params[:id])
   end

The use of private helper methods encapsulates logic that is specific to each action, promoting code reuse and modularity. These methods, such as `grading_report_data`, `my_scores_data`, `team_view_data`, `edit_data`, `prepare_feedback_summary`, and `populate_view_models`, encapsulate data preparation and processing tasks, keeping the controller actions focused and concise.

     def grading_report_data(assignment, scores)
       {
         assignment: assignment,
         scores: scores,
         num_reviewers_assigned_scores: scores[:teams].length,
         average_chart: bar_chart(vector(scores)),
         avg_of_avg: mean(vector(scores)),
         penalties: penalties(assignment.id),
         show_reputation: false
       }
     end
 
     def my_scores_data
       {
         participant: @participant,
         questions: @questions,
         pscore: @pscore,
         summary: @summary,
         avg_scores_by_round: @summary.avg_scores_by_round,
         avg_scores_by_criterion: @summary.avg_scores_by_criterion
       }
     end
 
     def team_view_data
       {
         participant: @participant,
         assignment: @assignment,
         team: @team,
         team_id: @team_id,
         questions: @questions,
         pscore: @pscore,
         penalties: @penalties,
         vmlist: @vmlist,
         current_role_name: @current_role_name
       }
     end
 
     def edit_data
       {
         participant: @participant,
         questions: @questions,
         scores: @scores
       }
     end
 
     def prepare_feedback_summary
       summary_ws_url = WEBSERVICE_CONFIG['summary_webservice_url']
       sum = SummaryHelper::Summary.new.summarize_reviews_by_reviewee(@questions, @participant.assignment, @team_id, summary_ws_url, session)
       sum.summary
     end
 
     def populate_view_models
       vmlist = []
       counter_for_same_rubric = 0
       if @assignment.vary_by_topic?
         topic_id = SignedUpTeam.topic_id_by_team_id(@team_id)
         topic_specific_questionnaire = AssignmentQuestionnaire.where(assignment_id: @assignment.id, topic_id: topic_id).first.questionnaire
         vmlist << populate_view_model(topic_specific_questionnaire)
       end
       @assignment.questionnaires.each do |questionnaire|
         @round = nil
         next if @assignment.vary_by_topic? && questionnaire.type == 'ReviewQuestionnaire'
 
         if @assignment.varying_rubrics_by_round? && questionnaire.type == 'ReviewQuestionnaire'
           questionnaires = AssignmentQuestionnaire.where(assignment_id: @assignment.id, questionnaire_id: questionnaire.id)
           if questionnaires.count > 1
             @round = questionnaires[counter_for_same_rubric].used_in_round
             counter_for_same_rubric += 1
           else
             @round = questionnaires[0].used_in_round
             counter_for_same_rubric = 0
           end
         end
         vmlist << populate_view_model(questionnaire)
       end
       vmlist
     end

Additionally, the controller follows strong parameter conventions by using the `participant_params` method to permit specific attributes for participant updates. This helps mitigate security risks associated with mass assignment vulnerabilities and ensures that only permitted attributes can be modified through API requests.

     def participant_params
       params.require(:participant).permit(:grade, :other_params)
     end

Team

Mentor
  • Kashika Malick
Members
  • Sravya Yepuri (syepuri@ncsu.edu)
  • Hasini Chenchala (hchench@ncsu.edu)
  • Chirag Hegde (chegde@ncsu.edu)