CSC 517 S2016 E1611 Refactor team.rb, course team.rb, and assignment team.rb

From Expertiza_Wiki
Jump to navigation Jump to search

E1611 Refactor team.rb, course_team.rb, and assignment_team.rb

This page provides the description of the Expertiza based OSS project on refactoring.


Introduction to Expertiza

Expertiza is an open source project developed on Ruby on Rails framework. It is primarily developed as a platform for instructors to create, assign and grade assignments and quizzes and for the students to view, submit assignments and take quizzes. Other activities involved in a course can also be done via Expertiza, like creating teams, reviewing assignments among others.


Problem Statement

The three classes in question- team, assignment team and course team, deal with teams of students that do assignments."team.rb" is the superclass; course_team is a team created for a course (if students are expected to stay in the same team all semester), and assignment_team is a team created for an assignment. Either the instructor can set up course or assignment teams (using the “Create teams” icon on the course or assignment Actions menu), or the students can, by issuing and accepting invitations.


The code for 'importing', 'exporting', and 'copying' teams for assignment and course violates the DRY principle. There is a paucity of comments and there are deprecated methods and other methods that may not be used anymore, and could be deleted.

Tasks Identified

The following tasks were accomplished in this project:

  • Create a common method for importing and exporting teams in the team.rb file instead of having 2 separate functions in the assignment_team.rb and course_team.rb files using the prototype pattern.
  • Move the create_team_and_method to superclass (team.rb).
  • Delete deprecated methods.
  • Rewrite the method for checking if a team has submission.
  • Remove unnecessary white spaces and add comments before methods to describe what the method does.

System Design

This project deals with the "team" module of Expertiza which handles the creation of teams and other aspects related to teams like submission, importing and exporting. Expertiza has 3 models, team.rb, course_team.rb and assignment_team.rb for teams in general, teams for a course and teams for each assignment.

The design specific to these models is as follows::

Here the course_team and assignment_team are subclasses of team. The parent_id field in the database of teams refer to the id of course or assignment based on the type for which it was created. Hence in course_team instances the parent_id contains the course ID and for assignment_team the parent_id contains the assignment ID. The main table stores the team members is TeamUsers, where each line is uniquely identified by the user_id and the team_id. This table is linked to Team, Participant & Users.

The understanding of this system design is core to understanding the functionality of team.rb, course_team.rb and assignment_team.rb models.

Solutions Provided

Refactoring

Import :This method was refactored to the parent class team.rb from the assignment_team.rb and the course_team.rb. The new code is :

def self.import(row, id, options,teamtype)
 raise ArgumentError, "Not enough fields on this line" if (row.length < 2 && options[:has_column_names] == "true") || (row.length < 1 && options[:has_column_names] != "true")

 if options[:has_column_names] == "true"
   name = row[0].to_s.strip
   team = where(["name =? && parent_id =?", name, id]).first
   team_exists = !team.nil?
   name = handle_duplicate(team, name, id, options[:handle_dups],teamtype)
   index = 1
 else
   if teamtype.is_a?(CourseTeam)
     name = self.generate_team_name(Course.find(id).name)
   elsif teamtype.is_a?(AssignmentTeam)
     name = self.generate_team_name(Assignment.find(id).name)
   end
   index = 0
 end

 if name
   team = Team.create_team_and_node(id,teamtype)
   team.name = name
   team.save
 end

 # insert team members into team unless team was pre-existing & we ignore duplicate teams
 team.import_team_members(index, row) if !(team_exists && options[:handle_dups] == "ignore")
end

Export :This method was refactored to the parent class team.rb from the assignment_team.rb and the course_team.rb. The new code is :

def self.export(csv, parent_id, options, teamtype)
 if teamtype.is_a?(CourseTeam)
   teams = CourseTeam.where(["parent_id =?", parent_id])
 elsif teamtype.is_a?(AssignmentTeam)
   teams = AssignmentTeam.where(["parent_id =?", parent_id])
 end
 teams.each do |team|
   output = Array.new
   output.push(team.name)
   if options["team_name"] == "false"
     team_members = TeamsUser.where(['team_id = ?', team.id])
     team_members.each do |user|
       output.push(user.name)
     end
   end
   output.push(teams.name)
   csv << output
 end
end

Helper Methods: Some of the helper methods also had to be refactored to the parent class.

def self.create_team_and_node(id,teamtype)
 if teamtype.is_a?(CourseTeam)
   curr_course = Course.find(id)
   team_name = Team.generate_team_name(curr_course.name)
   team = CourseTeam.create(name: team_name, parent_id: id)
   TeamNode.create(parent_id: id, node_object_id: team.id)
 elsif teamtype.is_a?(AssignmentTeam)
   curr_assignment = Assignment.find(id)
   team_name = Team.generate_team_name(curr_assignment.name)
   team = AssignmentTeam.create(name: team_name, parent_id: id)
   TeamNode.create(parent_id: id, node_object_id: team.id)
 end
 team
end
def self.handle_duplicate(team, name, id, handle_dups, teamtype)
 if team.nil? #no duplicate
   return name
 end
 if handle_dups == "ignore" #ignore: do not create the new team
   p '>>>setting name to nil ...'
   return nil
 end
 if handle_dups == "rename" #rename: rename new team
   if teamtype.is_a?(CourseTeam)
     return self.generate_team_name(Course.find(id).name)
   elsif teamtype.is_a?(AssignmentTeam)
     return self.generate_team_name(Assignment.find(id).name)
   end
 end
 if handle_dups == "replace" #replace: delete old team
   team.delete
   return name
 else # handle_dups = "insert"
   return nil
 end
end


Changes to the child classes:

def self.prototype
 AssignmentTeam.new
end
def self.prototype
 CourseTeam.new
end

Refactored has_submissions? In Assignment Team to :

def has_submissions?
 (self.submitted_files.length > 0) or self.submitted_hyperlinks.blank?
end

Prototype Design Pattern is an ideal fit to handle this child parent relationship, but prototype pattern one needs to clone the child objects and then pass them to the parent’s calling method. But since Ruby allows for deep copy but does not allow for cloning objects, this is not possible. Hence a similar behavior has been simulated by creating objects in child classes and passing them as parameters to the parent class. A prototype method is defined in child classes that returns an object of that class which is then passed to the parent where reflection is used to identify the source calling method and act based on it.


Deleting Deprecated Methods

The following methods have been deleted as they are not in use anymore:

  • add_participant​ in course_team.rb.
  • participant_type in assignment_team.rb.
  • assignment in team.rb.
  • email method was removed.

The following methods were not removed as they are still used elsewhere in the code:

  • self.first_member(team_id): Shows if a team is empty or not.
  • set_student_directory_number: Sets the team to submission relation.


Pretty-Printing the code

  • Deleted unnecessary white spaces, and pretty printed the whole code in all the 3 classes.
  • Debugged and figured out the usage of every method in the classes and have provided small comments for reader to understand the operation of the defined method.


Testing from UI

This project deals entirely with the creation, deletion, modification, import and export of teams on Expertiza. Following is the step-by-step guide to test functionalities.

For testing, the following parameters can be used:

For Admin:

Username: administrator5

Password: password

For Student:

Username: student5884

Password: password

Username: student6420

Password: password

Creating teams

1) Login in with administrator.

2) Go to course/assignment tab.

3) Click on create team against the desired course/assignment, it will redirect to a new page where all the teams are displayed.

4) On the bottom of the page, there is a link to create new team.

5) Fill in the required details and save the team.

6) Verify whether the new team has been created.

Exporting teams

1) Login in with administrator.

2) Go to course/assignment tab.

3) Click on create team against the desired course/assignment, it will redirect to a new page where all the teams are displayed.

4) On the bottom of the page, there is a link to export all the teams.

5) Verify whether .csv file was successfully exported to the system.

Importing teams

1) Login in with administrator.

2) Go to course/assignment tab.

3) Click on create team against the desired course/assignment, it will redirect to a new page where all the teams are displayed.

4) On the bottom of the page, there is a link to import new team.

5) Select the .csv file from the system and click on import.

6) Verify whether import was successful.

References

  1. Expertiza on GitHub
  2. GitHub Project Repository Fork
  3. The live Expertiza website
  4. Expertiza project documentation wiki
  5. Rspec Documentation
  6. Ruby Documentation
  7. Prototype Pattern