CSC/ECE 517 Spring 2024 - E2417. Reimplement submitted content controller.rb

From Expertiza_Wiki
Jump to navigation Jump to search

E2417. Reimplement Submitted Content Controller

This page provides a description of the Expertiza based OSS Project E2417. Reimplement the SubmittedContentController

About Expertiza

Expertiza is an open-source web application facilitating peer feedback and assessment in educational courses. It enables students to submit work, review peers' submissions, and receive feedback. The platform supports anonymous peer reviews, grading rubrics, and discussion forums. Its goal is to enhance collaborative learning and improve the quality of student work through constructive criticism.

Objective

A submitted content controller should have the functionalities to manage the submitted content, for eg, the CRUD operations for submissions, different types of submissions, etc. The goal here is to implement this controller via API's and use principles of Object-Oriented Programming such as SOLID.

Problem Description

The reimplementation of SubmittedContentController in Expertiza needs to be done to enhance its functionality and maintainability. It should handle various tasks like file submissions and uploads, hyperlink submissions, and downloads, but the controller suffers from code redundancy and mixed responsibilities within methods. The reimplementation aims to ensure participants receive appropriate messages and HTTP status codes for actions like uploading files and deleting them, while also using SOLID principles, refactoring DRY code and minimizing excessive use of instance variables. The goal is to optimize the controller's design, adhering to best practices in readability and modularity.

Improvements in New Code

Index, show and create operations have been performed for the submission content controller.

  • Index method for Submitted Content
  • Show method for Submitted Content
  • Create method to Submit Content
  • SignedUpTopic Model
  • SignedUpTeam Model
  • SubmissionRecord Model
  • Duty Model
  • Late Policy Model
  • Submitted Content Helper

Index method for Submitted Content

 def index
   @submission_record = SubmissionRecord.all
   render json: @submission_record, status: :ok
 end

The index function is responsible for retrieving the data for the users. This function serves to fetch all submission records from the database using the `SubmissionRecord` model and assigns them to an instance variable `@submission_record`. The status: :ok parameter ensures that the HTTP response status is set to 200, indicating a successful request. This function operates on the HTTP GET method, allowing clients to request information from the server. The rendered JSON response includes the submission records, providing comprehensive data to clients.

Show method for Submitted Content

 def show
   @submission_record = SubmissionRecord.find(params[:id])
   render json: @submission_record, status: :ok
 rescue ActiveRecord::RecordNotFound => e
   render json: { error: e.message }, status: :not_found
 end

The show function enables clients to retrieve detailed information about specific submission records stored in the backend database and operates on HTTP GET method. The function retrieves the submission record associated with the provided ID using the SubmissionRecord.find(params[:id]) query. If the record is found, it is serialized into JSON format using the render method. If the record is not found, it renders a JSON response containing an error message conveying the exception's details, alongside an appropriate HTTP status code of :not_found (404).

Create method to Submit Content

 def create
   @submission_record = SubmissionRecord.create(submitted_content_params)
   if @submission_record.save
     render json: @submission_record, status: :created
   else
     render json: @submission_record.errors, status: :unprocessable_entity
   end
 end

The create function facilitates the creation of new submission records using HTTP POST method. The function initializes a new SubmissionRecord instance by passing the submitted content parameters obtained from the client to the SubmissionRecord.create method. If the record is successfully saved, indicating that the submission was processed without errors, the function renders a JSON response containing the newly created submission record along with an HTTP status code of :created (201), signifying successful record creation. If not, it renders a JSON response containing the errors encountered during the submission attempt, along with an appropriate HTTP status code of :unprocessable_entity (422), indicating that it cannot process it due to validation errors.

SignedUpTeam Model

The SignedUpTeam model represents the association between a topic and a team in the Expertiza project. Each instance of SignedUpTeam signifies that a particular team has signed up for a specific topic. This model facilitates the management and validation of these associations. This model is used by the Submitted Content Control so a skeleton for the same was implemented in this phase of the project.

   class SignedUpTeam < ApplicationRecord
     validates :topic_id, :team_id, presence: true
     scope :by_team_id, ->(team_id) { where('team_id = ?', team_id) }
   end


SignedUpTopic Model

The SignUpTopic model in the Expertiza project manages topics that users can sign up for within specific assignments. It provides functionalities for topic creation, modification, and retrieval. Additionally, it handles the association between topics and various other entities such as teams, due dates, bids, and assignment questionnaires. This model is used by the Submitted Content Control so a skeleton for the same was implemented in this phase of the project.

   class SignUpTopic < ApplicationRecord
     validates :topic_name, :assignment_id, :max_choosers, presence: true
     validates :topic_identifier, length: { maximum: 10 }
   end

SubmissionRecord Model

The SubmissionRecord model in the Expertiza project manages records of submissions made by users for specific assignments. It ensures that essential attributes related to the submission, such as content, operation, team ID, user, and assignment ID, are present through validations. This model is used by the Submitted Content Control so a skeleton for the same was implemented in this phase of the project.

   class SubmissionRecord < ApplicationRecord
       validates :content, presence: true
       validates :operation, presence: true
       validates :team_id, presence: true
       validates :user, presence: true
       validates :assignment_id, presence: true
   end  

Participant Model

Participant model is implemented in association with user submitting an assignment. This model is used by the Submitted Content Control so a skeleton for the same was implemented in this phase of the project.

   class Participant < ApplicationRecord
     validates :grade, numericality: { allow_nil: true }
   end


Assignment Model

Assignment model handles the assignment submission. This model is used by the Submitted Content Control so a skeleton for the same was implemented in this phase of the project.

   class Assignment < ApplicationRecord
   end

Assignment Participant Model

The AssignmentParticipant model extends the functionality of the Participant model, providing additional features and associations specific to assignments. This model is used by the Submitted Content Control so a skeleton for the same was implemented in this phase of the project.

   class AssignmentParticipant < Participant
     validates :handle, presence: true 
   end

SignUpSheet Model

The SignUpSheet model handles the signing up of users for topics within assignments. It provides methods for managing teams, confirming topics, adding signup topics, and importing signup data. This model is used by the Submitted Content Control so a skeleton for the same was implemented in this phase of the project.

   class SignUpSheet < ApplicationRecord
   end

Team Model

The Team model facilitates the management of teams. This model is used by the Submitted Content Control so a skeleton for the same was implemented in this phase of the project.

   class Team < ApplicationRecord
   end
   

Team User Model

The TeamUserNode model represents nodes in a tree structure associated with team users. This model is used by the Submitted Content Control so a skeleton for the same was implemented in this phase of the project.

   class TeamsUser < ApplicationRecord
   end

Duty Model

The Duty model in the Expertiza project manages duties assigned to individuals within teams. It validates the attributes of duties, including their names and maximum member counts, and provides a method for determining whether a duty can be assigned to a particular team. The Submitted Content Controller has an indirect dependency on this model. This model will be implemented in the next phase of the project.

Late Policy Model

The LatePolicy model in the Expertiza project manages late submission policies. It associates late policies with assignments. The model provides validations for policy attributes and methods for checking policy names and updating penalty objects when policies are modified. The Submitted Content Controller has an indirect dependency on this model. This model will be implemented in the next phase of the project.

Submitted Content Helper

The SubmittedContentHelper module facilitates the unzipping of files, aiding in the management of uploaded content within web applications. Its unzip_file method efficiently extracts the contents of a zip file to a specified directory, ensuring reliability. By leveraging the RubyZip gem, this module helps in the process of handling zip files, enhancing the backend's functionality.

Original Code

 module SubmittedContentHelper
   # Installing RubyZip
   # run the command,  gem install rubyzip
   # restart the server
   def self.unzip_file(file_name, unzip_dir, should_delete)
     # Checking if file exists.
     raise "File #{file_name} does not exist" unless File.exist?(file_name)
     # begin
     Zip::File.open(file_name) do |zf|
       zf.each do |e|
         safename = FileHelper.sanitize_filename(e.name)
         fpath = File.join(unzip_dir, safename)
         FileUtils.mkdir_p(File.dirname(fpath))
         zf.extract(e, fpath)
       end
     end
     if should_delete
       # The zip file is no longer needed, so delete it
       File.delete(file_name)
     end
   end
 end

Refactored Code

 module SubmittedContentHelper
   # Installing RubyZip
   # run the command,  gem install rubyzip
   # restart the server
   def self.unzip_file(file_name, unzip_dir, should_delete)
     # Checking if file exists.
     raise "File #{file_name} does not exist" unless File.exist?(file_name)
     # begin
     Zip::File.open(file_name) do |zf|
       zf.each do |e|
         extract_entry(e, unzip_dir)
       end
     end
     if should_delete
       # The zip file is no longer needed, so delete it
       File.delete(file_name)
     end
   end
   private
   def extract_entry(e, unzip_dir)
     safename = FileHelper.sanitize_filename(e.name)
     fpath = File.join(unzip_dir, safename)
     FileUtils.mkdir_p(File.dirname(fpath))
     zf.extract(e, fpath)
   end
 end

In the previous code, to extract files, it was tightly coupled with the unzip_files method, making it less reusable and harder to maintain. After refactoring the code, a separate private method is formed to unzip the file. By separating the extract_entry function into its own module or file, each component now has a single responsibility: the extract_entry function is responsible solely for extracting a single entry from a zip file. Single Responsibility Principle is followed.

Testing

Testing is done by Rswag in the spec/requests/api/v1/submited_content_spec.rb

The index method is responsible for listing all submitted records. The test case sets up the necessary data for the test in "Setting up data", including a role, a user with the "Student" role, an assignment, a team, and a submission record associated with the user, team, and assignment. The test case sends a GET request to the /api/v1/submitted_content route and tests a successful response with a status code of 200. After the request is made, the test case checks if the response body has a size of 1, which implies that there is one submitted record in the response determined during setup of data.

 path '/api/v1/submitted_content' do
   # # #
   Setting up submitted_content dummy data
   # # #
   # Get /api/v1/submitted_content, returns list of all submitted record.
   get('list all submitted record') do
     tags 'SubmittedContent'
     produces 'application/json'
     response(200, 'successful') do
       after do |example|
         example.metadata[:response][:content] = {
           'application/json' => {
             example: JSON.parse(response.body, symbolize_names: true)
           }
         }
       end
       run_test! do
         expect(response.body.size).to eq(1)
       end
     end
   end
 end

The create method handles the creation of a new submitted record. The test case covers different scenarios, including successful creation with valid parameters, failure with invalid parameters, and server errors. It tests the following scenarios:

  • Creating a new submitted record with valid parameters should return a successful response (status code 201) and the expected response body.
  • Creating a new submitted record with invalid parameters (e.g., missing user field) should return an unprocessable entity response (status code 422).
 path '/api/v1/submitted_content' do
   # # #
   Setting up valid and invalid params for form submit
   # # #
   post('create a submitted record') do
     tags 'SubmittedContent'
     consumes 'application/json'
     produces 'application/json'
     response(201, 'successful') do
       after do |example|
         example.metadata[:response][:content] = {
           'application/json' => {
             example: JSON.parse(response.body, symbolize_names: true)
           }
         }
       end
       run_test! do
         expect(response.body).to include('"type":"test"')
       end
     end
     # Testing Server Error for the endpoint.
     response(422, 'unprocessable entity') do
       let(:valid_params) do
         SubmissionRecord.create(invalid_questionnaire_params)
       end
       run_test!
     end
   end
 end

The show method tests for sepefic submitted_content record. It tests the successful retrieval of a specific submission record with a valid ID (expecting a 200 status code and a specific response body) and the handling of an invalid ID (expecting a 404 "Not Found" error with an appropriate error message in the response body). The test case covers both the happy path and the error handling scenarios for retrieving a submission record by ID.

 path '/api/v1/submitted_content/{id}' do
   # Get a specific submission record based on id.
   get('show a submitted record.') do
     tags 'SubmittedContent'
     produces 'application/json'
     # URL parameters.
     parameter name: 'id', in: :path, type: :string, description: 'id'
     # Get the submission record with specific id.
     response(200, 'successful') do
       let(:id) { '1' }
       after do |example|
         example.metadata[:response][:content] = {
           'application/json' => {
             example: JSON.parse(response.body, symbolize_names: true)
           }
         }
       end
       run_test! do
         expect(response.body).to include('"type":"test"')
       end
     end
     # Testing Not Found 404 Error.
     response(404, 'not_found') do
       let(:id) { 'invalid' }  # Using invalid Id type.
       run_test! do
         expect(response.body).to include("Couldn't find")
       end
     end
   end
 end

Deployment Link

http://152.7.177.13:3000/api-docs

  • Credentials:
   Username: admin 
   Password: admin

Note: The authorization has been skipped to ensure smooth testing.

Github Repo

https://github.com/Neel317/reimplementation-back-end

PR Link

https://github.com/expertiza/reimplementation-back-end/pull/78/files

Future Scope (Phase 2 of Project)

This controller will be further enhanced by doing the following things which will be done in the phase 2 of the project.

  • Implement Update method to submit content
  • Implement Delete method to Delete the submitted content
  • Testing
  • Implement Duty model
  • Implement Late policy model
  • File Helper which is currently a module required by Submitted Content Helper and used for parsing folder names and file names and is required for the next phase
  • Implement third party dependent models such as due date, team node, team user node.
  • Deleting some redundant models such as DisplayOption and removing unwanted instance variables like @currentfolder - stores only a name of a file - which can be refactored and stored as a local variable and does not need to be an instance variable

Links

  1. Github Repo
  2. Pull request

References

  1. Expertiza on GitHub
  2. The live Expertiza website
  3. Expertiza project documentation wiki
  4. Rspec Documentation