CSC/ECE 517 Spring 2024 - E2443 Reimplement grades controller
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:
- Refactoring Grades Controller: Enhancing code clarity, optimizing loops, and adding comments for unclear lines of code to improve maintainability and readability.
- Removing Redundant Methods: Identifying and eliminating unused methods in the controller to reduce complexity and streamline functionality.
- CRUD Operations: Implementing CRUD operations (Create, Read, Update, Delete) in the controller for efficient data management.
- Adherence to DRY Principle: Ensuring that the reimplementation follows the Don't Repeat Yourself (DRY) principle to avoid duplication and improve code efficiency.
- 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.
Class UML Diagram
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.
Reimplementation of GradesController
Implementation of APIs
In the context of the project "E2443. Reimplement grades_controller" for the Expertiza platform, the modifications made to the routes.rb file for the GradesController are intended to streamline and enhance the existing functionality related to grading. These routing configurations play a critical role in structuring how grade-related requests are processed and responded to within the application.
The specific routes added under the grades resource leverage a nested structure to facilitate clear and organized handling of various grade-related actions, ensuring that each action pertains directly to individual grade entities identified by :id, being the participant ID.
Breakdown of Routes
- View Team (:id/view_team) - This endpoint retrieves detailed information about the team associated with a specific assignment participant. It is crucial for scenarios where assessments or grades are team-based. The API fetches the participant's team and provides comprehensive details, including the team members and their contributions to assignments. This functionality supports collaborative assessments and enhances transparency in team-based grading.
def view_team
participant = AssignmentParticipant.find(params[:id])
assignment = participant.assignment
team = participant.team
questionnaires = assignment.questionnaires
questions = retrieve_questions(questionnaires, assignment.id)
pscore = participant_scores(participant, questions)
render json: { participant:, assignment:, team:, questions:, pscore:}
end
- View Grade Details (:id/view) - This endpoint allows for viewing all relevant details of an assignment for a particular participant. It aggregates data such as related questionnaires, questions, and scores, providing a holistic view of the grading criteria and results. This function is particularly valuable for providing a comprehensive breakdown of scores across different review rounds, assisting in a deeper understanding of how grades were allocated.
def view
assignment = Assignment.find(params[:id])
questionnaires = assignment.questionnaires
if assignment.num_review_rounds > 1
questions = retrieve_questions questionnaires, assignment.id
else
questions = {}
questionnaires.each do |questionnaire|
questions[questionnaire.symbol] = questionnaire.questions
end
end
scores = review_grades(assignment, questions)
num_reviewers_assigned_scores = scores[:teams].length
averages = vector(scores)
avg_of_avg = mean(averages)
render json: { scores:, averages:, avg_of_avg:, num_reviewers_assigned_scores: }
end
- View Scores (:id/view_scores) - This API endpoint is designed to show scores for a specific assignment participant. It provides detailed insights into how participants are performing in the ongoing assignments and the various stages of assignment completion. This is essential for tracking academic progress and identifying areas where students may need additional support or recognition.
def view_scores
participant = AssignmentParticipant.find(params[:id])
assignment = participant.assignment
# questionnaires = assignment.questionnaires
# questions = retrieve_questions questionnaires, assignment.id
# pscore = participant_scores(participant, questions)
topic_id = SignedUpTeam.topic_id(participant.assignment.id, participant.user_id)
stage = assignment.current_stage(topic_id)
render json: { participant: }
end
- Update Grade (:id/update) - This endpoint enables updating the grade for a participant. It accepts a new grade value and updates the participant’s record if the new grade differs from the existing one. This API is crucial for situations where grades need to be revised or corrected, ensuring that grading remains dynamic and reflective of actual student performance and any subsequent reviews or reassessments.
def update
participant = AssignmentParticipant.find(params[:id])
total_score = params[:total_score]
unless format('%.2f', total_score) == params[:participant][:grade]
participant.update_attribute(:grade, params[:participant][:grade])
message = if participant.grade.nil?
"The computed score will be used for #{participant.user.name}."
else
"A score of #{params[:participant][:grade]}% has been saved for #{participant.user.name}."
end
end
render json: { message: message}
end
- Show Grade (:id) - This endpoint provides a detailed view of a participant's assignment by fetching and displaying all relevant information such as the assignment details, participant info, related questions, and scores. This function is a vital read operation that allows instructors and students to access complete information on a participant's performance in a specific assignment.
def show
participant = AssignmentParticipant.find(params[:id])
assignment = participant.assignment
questions = list_questions(assignment)
scores = participant_scores(participant, questions)
render json: { participant:, assignment:, questions:, scores: }
end
- Action Allowed (:id/action_allowed) - This endpoint checks if a particular action is allowed for the current user, based on the user’s role and the specific action they intend to perform (like viewing scores). It ensures that operations are conducted within established permissions, enhancing security and role-based access control within the application. This is critical for maintaining the integrity of the grading process and ensuring that only authorized actions are executed.
def action_allowed
case params[:action]
when 'view_scores'
if current_user_has_student_privileges? &&
are_needed_authorizations_present?(params[:id], 'reader', 'reviewer') &&
self_review_finished?
render json: { allowed: true }
else
render json: { allowed: false, error: 'Unauthorized' }, status: :forbidden
end
when 'view_team'
if current_user_is_a? 'Student'
participant = AssignmentParticipant.find_by(id: params[:id])
if participant && current_user_is_assignment_participant?(participant.assignment.id)
render json: { allowed: true }
else
render json: { allowed: false, error: 'Unauthorized' }, status: :forbidden
end
else
render json: { allowed: true }
end
else
if current_user_has_ta_privileges?
render json: { allowed: true }
else
render json: { allowed: false, error: 'Unauthorized' }, status: :forbidden
end
end
end
- Save Grade and Comment (:id/save_grade_and_comment_for_submission) - This API allows for the saving of grades and comments for a specific submission. It is designed to capture both quantitative and qualitative feedback, storing this information securely and making it accessible for review. This endpoint is particularly important for providing feedback that is integral to educational environments, fostering a constructive and informative feedback loop.
def save_grade_and_comment_for_submission
participant = AssignmentParticipant.find(params[:id])
@team = participant.team
@team.grade_for_submission = params[:grade]
@team.comment_for_submission = params[:comment]
begin
@team.save!
render json: { success: "Grade \'#{params[:grade]}\' and comment \'#{params[:comment]}\' for submission successfully saved." }
rescue StandardError => e
render json: { error: e.message }, status: :unprocessable_entity
end
end
These routes collectively support the essential CRUD operations needed for managing grades within the Expertiza system. By carefully defining these routes, the project ensures that grade management is handled in a structured and efficient manner, thereby reducing redundancy and enhancing the overall system architecture. This reimplementation fosters better scalability and easier maintenance, adhering to improved software design principles and practices.
Implementation of Spec file
The specification file for the Grades API provides comprehensive testing scenarios for various endpoints within the Api::V1::GradesController. This document is crafted using RSpec, a popular testing framework for Ruby applications, and employs the Swagger tool to describe and document the API endpoints. The focus is on ensuring that the controller behaves as expected across different use cases, adhering to both functional and non-functional requirements. Here is a detailed explanation of the test cases outlined in the specification:
Testing Framework and Tools
- RSpec is utilized for writing and executing the test cases, providing a DSL (Domain-Specific Language) that is readable and expressive.
- Swagger helps in documenting the API, which not only serves for testing but also acts as a live documentation for developers and users of the API.
Endpoints and Test Scenarios
- GET /api/v1/grades/{id}/view - This endpoint tests the functionality to view detailed grade information for a specific ID.The test ensures that upon receiving a valid ID, the system returns a 200 status code indicating a successful operation.
- GET /api/v1/grades/{id}/view_team - Tests the ability to retrieve team details associated with a specific grade.A 200 status code response is expected when valid data is provided, confirming the correct retrieval of team information.
- GET /api/v1/grades/{id}/view_scores - Focuses on viewing scores for a specific grade.The test confirms that the endpoint returns a 200 status when executed with a correct ID, displaying the scores accurately.
- PUT /api/v1/grades/{id}/update - Tests the grade update functionality. This endpoint expects a JSON body with grade details.Two scenarios are tested: successful grade update (200 status) and failure due to missing required attributes (422 status), ensuring robust error handling and data validation.
- GET /api/v1/grades/{id} - Simple retrieval of grade details. The test verifies that the endpoint fetches and returns the correct grade information, indicated by a 200 success status.
- GET /api/v1/grades/{id}/action_allowed - Checks if a certain action is permissible for a given grade ID. It tests for both allowed (200 status) and not allowed scenarios (403 status), important for validating access control mechanisms.
- PUT /api/v1/grades/{id}/save_grade_and_comment_for_submission - This test ensures that grades and comments can be successfully saved for a submission. The parameters are passed via query strings, and the test checks for a successful save operation (200 status).
Importance of These Tests
The specified tests are essential for maintaining the integrity and reliability of the Grades API. They ensure that:
- The API responds correctly under various conditions.
- Data integrity is maintained with correct inputs and handling of incorrect or partial inputs.
- Security and access control measures are effective, particularly in verifying user permissions.
This meticulous testing setup reflects a commitment to quality and robustness in the software development lifecycle, crucial for applications like Expertiza that are used in educational settings. These tests contribute directly to the project's aim of enhancing maintainability, readability, and adherence to the DRY principle within the grades_controller.
Swagger File Implementation
The Swagger file uses YAML format to describe the HTTP routes, parameters, and responses for the API. Each path is clearly defined with required parameters and expected responses, facilitating automatic documentation generation and client code generation.
GET /grades/{id}/view_team
Summary: Retrieve team details associated with a specific grade.
Response 200: Indicates successful retrieval of the team details. Parameter: id (integer, required) - the unique identifier for a grade.
GET /grades/{id}/view
Summary: Fetch comprehensive details about a specific grade.
Response 200: Successful operation, returning complete grade details.
GET /grades/{id}/view_scores
Summary: Obtain scoring details for a specific grade.
Response 200: Successful retrieval of score data.
PUT /grades/{id}/update
Summary: Update details of a specific grade.
Response 200: Confirmation of successful update.
GET /grades/{id}
Summary: Access detailed information about a specific grade.
Response 200: Successful operation, showing detailed grade data.
GET /grades/{id}/action_allowed
Summary: Check permissions for performing certain actions on a grade.
Response 200: Successful verification of allowed actions.
POST /grades/{id}/save_grade_and_comment_for_submission
Summary: Save a grade along with a comment for a specific submission.
Response 200: Successfully saved the grade and comment.
Importance and Usage
- Standardization and Documentation: Swagger files like this one help in standardizing API practices across development teams, providing a consistent and predictable interface. It ensures that all team members, including backend and frontend developers, understand the API’s functionality and constraints without diving into the codebase.
- Client SDK Generation: Tools such as Swagger Codegen can utilize this file to automatically generate client libraries in various programming languages, which can accelerate development and integration efforts.
- Interactive API Documentation: Swagger UI can render this file into interactive API documentation that allows developers to perform live API calls for testing purposes, greatly enhancing the developer experience and easing troubleshooting and testing.
The careful organization and detailed descriptions in the Swagger file are critical for maintaining the robustness and reliability of the API, aligning with best practices in API design and documentation. This approach supports the project's goal to improve maintainability and readability of the grades_controller, ensuring that the API is both accessible and functional.
Implementation of Helper Files
The recently implemented helper modules—AssignmentHelper, AuthorizationHelper, GradesHelper, PenaltyHelper, and StudentTaskHelper—serve as foundational components that augment the functionality of the Expertiza system. Each module encapsulates specific, reusable logic that interfaces with various aspects of the system, promoting a modular, maintainable, and DRY codebase. Here’s a comprehensive breakdown of each helper module:
AssignmentHelper
This module provides utility functions tailored to manage and manipulate course and assignment data:
- course_options: Generates a list of courses available to the user based on their role, such as teaching assistants seeing only their courses, while administrators can view all courses.
- questionnaire_options: Filters questionnaires based on visibility and association with the instructor, allowing users to select questionnaires relevant to their role.
- review_strategy_options and due_date: These functions assist in setting up review strategies for assignments and managing due dates respectively, essential for assignment creation and editing.
AuthorizationHelper
Focused on security, this module determines the access level of users within the system:
- current_user_has_..._privileges? methods check the user’s role against required privileges, ensuring actions are permitted before proceeding.
- current_user_is_assignment_participant? and similar methods validate user participation in specific contexts, crucial for enforcing role-based actions within assignments.
GradesHelper
This module aids in the handling and display of grades and related functionalities:
- accordion_title and score_vector: These functions manage the display of structured data, such as grouping related responses under a common header in the UI.
- vector and mean: Utilized for statistical calculations to derive insights from grade data, supporting detailed analytical features like average scores.
PenaltyHelper
Manages the calculation of penalties based on assignment deadlines and submission timelines:
- calculate_penalty and its associated methods (calculate_submission_penalty, calculate_review_penalty, etc.) assess penalties for late submissions and incomplete reviews, ensuring students are graded fairly based on timeliness and compliance with deadlines.
StudentTaskHelper
Enhances the student interaction with tasks and reviews:
- get_review_grade_info and get_awarded_badges: Provide visual feedback and recognition for students’ efforts, which are crucial for motivation and engagement.
- check_reviewable_topics and unsubmitted_self_review?: These functions check for reviewable topics and monitor the submission status of self-reviews, critical for maintaining the integrity and progression of peer reviews.
Integration and Impact
The integration of these helper modules significantly enhances the system's robustness by segregating responsibilities into distinct, manageable components. This approach not only simplifies the main application logic but also enhances code readability and maintenance—core aims of the project. By refining the backend architecture through these helpers, the system can more easily adapt to new requirements and facilitate smoother updates and enhancements.
Incorporating these modules into the Expertiza platform effectively addresses key project objectives, including adherence to DRY principles, improved maintainability, and ensuring a scalable architecture that can handle the evolving needs of educational environments. These improvements directly contribute to a more stable, efficient, and user-friendly grading system.
Implementation of Migration files
These migration files represent changes to the database schema in a Ruby on Rails application using ActiveRecord. Let's go through each migration file and explain its purpose:
- CreateDeadlineTypes: This migration creates a table called deadline_types with a name column. It also inserts some initial data into this table.
- CreateLatePolicies: This migration creates a table called late_policies with columns for penalty period, penalty per unit, whether the penalty is expressed as a percentage, and maximum penalty. It also inserts initial data into this table and adds an index on the penalty_period_in_minutes column.
- CreateDueDates: This migration creates a table called due_dates with columns for due date, deadline type ID, assignment ID, late policy ID, and flags for submission, review, resubmission, rereview, and review of review permissions. It adds foreign key constraints to reference other tables and indexes on various columns.
- ChangeAssignmentIdInDueDatesTableToParentId: This migration renames the assignment_id column in the due_dates table to parent_id and removes the foreign key constraint on it. This seems to be part of a restructuring where assignment_id is renamed to parent_id in several tables.
- CreateTeams: This migration creates a table called teams with a name column and an assignment_id column, which references the assignments table. It adds a foreign key constraint on the assignment_id column.
- ChangeAssignmentIdInParticipantsTableToParentId: This migration renames the assignment_id column in the participants table to parent_id and removes the foreign key constraint on it.
- ChangeAssignmentIdInTeamsTableToParentId: This migration renames the assignment_id column in the teams table to parent_id and removes the foreign key constraint on it.
- AddUsedInRoundToAssignmentQuestionnaires: This migration adds a column called used_in_round to the assignment_questionnaires table.
- UpdateParticipants: This migration adds columns for grade, comments_to_student, and private_instructor_comments to the participants table.
- CreateTeamsUsers: This migration creates a join table called teams_users to establish a many-to-many relationship between teams and users. It adds foreign key constraints for both team_id and user_id.
Use of Design and Dry Principles
In the provided GradesController code, several design principles and DRY (Don't Repeat Yourself) principles have been applied to ensure a clean and maintainable codebase. Let's break down the implementation and discuss how these principles are reflected:
Modularization and Inclusion of Concerns
The controller includes several modules like Scoring, PenaltyHelper, StudentTaskHelper, AssignmentHelper, GradesHelper, and AuthorizationHelper. This modularization helps in organizing related functionalities into separate files, promoting better code organization and reusability. Each module encapsulates related methods and concerns, promoting separation of concerns and making the codebase more modular.
Single Responsibility Principle (SRP)
Methods within the controller have clear and specific responsibilities. For example, view, view_scores, view_team, edit, show, update, and save_grade_and_comment_for_submission methods handle specific actions related to grading, viewing, and updating grades. This adherence to SRP makes the code easier to understand, test, and maintain as each method focuses on a single task.
Conditional Handling with Case Statements
The action_allowed method utilizes a case statement to handle different actions based on the parameters received. This approach makes the code more readable and maintainable compared to using multiple nested if-else statements. Each case in the statement handles a specific action, improving code organization and readability.
Code Reuse and DRY Principle
Common functionalities are abstracted into helper modules (GradesHelper, PenaltyHelper, etc.) and included where needed. For example, methods like participant_scores, calculate_penalty, retrieve_questions, etc., are reused across different controller actions. By extracting shared functionalities into helper modules, the code adheres to the DRY principle, reducing redundancy and promoting code reuse.
Comments and Documentation
The code includes comments explaining the purpose of methods, API endpoints, and complex logic. Comments are used judiciously to provide clarity where necessary. While comments are helpful, they should ideally focus on explaining why certain decisions were made or complex logic was implemented rather than simply restating what the code does.
Private Methods for Encapsulation
Private methods like set_assignment, list_questions, and self_review_finished? encapsulate internal implementation details and are not accessible outside the controller. This encapsulation hides complexity and promotes cleaner interfaces.
Error Handling
Error handling is implemented using rescue_from for ActiveRecord::RecordNotFound and StandardError. This ensures graceful handling of exceptions and provides meaningful error responses to clients. Overall, the GradesController demonstrates good adherence to design principles like SRP, modularity, DRY, encapsulation, and error handling. The code is well-structured, readable, and follows best practices for building maintainable Rails applications.
Resources
Pull Request : https://github.com/expertiza/reimplementation-back-end/pull/97
Demonstration : https://drive.google.com/drive/folders/1bfh348t-zbcLZ-VlpTCAGRtoSgVimTnU?usp=sharing
Team
Mentor
- Kashika Malick
Members
- Sravya Yepuri (syepuri@ncsu.edu)
- Hasini Chenchala (hchench@ncsu.edu)
- Chirag Hegde (chegde@ncsu.edu)