CSC/ECE 517 Fall 2013/oss E810 aas: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
 
(94 intermediate revisions by 3 users not shown)
Line 1: Line 1:
=E810 Regularize staggered-deadline assignments=
'''E810 Regularize staggered-deadline assignments'''
 
This page provides a description of the OSS project conducted on Expertiza which was done as the part of the Object Oriented Languages and Systems coursework.
This page provides a description of the OSS project conducted on Expertiza which was done as the part of the Object Oriented Languages and Systems coursework.


=Introduction to Expertiza=
'''Introduction to Expertiza'''<br>
[http://expertiza.ncsu.edu/ Expertiza] is a project developed using Ruby on Rails platform. It provides features like peer review, team assignments and submission of projects. This can be achieved by submitting code base, URL of hosted code on Remote Servers and Wiki submissions. It is open source application and the code can be cloned from [https://github.com/expertiza/expertiza github]. This application provides an efficient way to manage assignments, grades and reviews. This makes the process easier and faster when the class strength is large.  
 
[http://expertiza.ncsu.edu/ Expertiza] is a project developed using Ruby on Rails platform. It provides features like peer review, team assignments and submission of projects. This can be achieved by submitting code base, URL of hosted code on remote server and Wiki submissions. It is open source application and the code can be cloned from [https://github.com/expertiza/expertiza github]. This application provides an efficient way to manage assignments, grades and reviews. This makes the process easier and faster when the class strength is large.  


Expertiza is supported by National Science Foundation under Grant No. 0536558. Additional funding from the NCSU Learning in a Technology-Rich Environment (LITRE) program, the NCSU Faculty Center for Teaching and Learning, the NCSU STEM Initiative, and the Center for Advanced Computing and Communication.
Expertiza is supported by National Science Foundation under Grant No. 0536558. Additional funding from the NCSU Learning in a Technology-Rich Environment (LITRE) program, the NCSU Faculty Center for Teaching and Learning, the NCSU STEM Initiative, and the Center for Advanced Computing and Communication.
= Project Resources =
[https://github.com/abhutan/expertiza GitHub Project Repository]
[http://152.7.99.160:3000/ VCL IP Address]
[http://wikis.lib.ncsu.edu/index.php/Expertiza Expertiza Wiki]


= Problem Statement =
= Problem Statement =
Line 11: Line 21:
On the other hand, If Assignment A has Topic A and Topic B, but they all have the same deadlines i.e. there is no dependency on each other then that's Normal Due Date scenario. In this case an Assignment may or may not have separate Topics.
On the other hand, If Assignment A has Topic A and Topic B, but they all have the same deadlines i.e. there is no dependency on each other then that's Normal Due Date scenario. In this case an Assignment may or may not have separate Topics.


Original code had various check statements for staggered and normal deadlines, which had to eliminated and structured in a way that with minimum checks we could distinguish between Staggered and Normal Due Dates. This required refactoring and restructuring of the whole functionality where we reduce these checks by distributing code into separate actions.
Original code had various check statements for staggered and normal deadlines, which had to eliminated and structured in a way that with minimum checks we could distinguish between Staggered and Normal Due Dates. This required refactoring and restructuring of the whole functionality where we reduce these checks by distributing code into separate actions.
 
Additionally, project required removal of redundant and dead code which enhances code readability and optimizes code performance.


= Re-factored Code Cases =
= Re-factored Code Cases =
Line 24: Line 36:
   def add_signup_topics_staggered
   def add_signup_topics_staggered
     load_add_signup_topics(params[:id])
     load_add_signup_topics(params[:id])
     @review_rounds = Assignment.find(params[:id]).get_review_rounds
     ....
    @topics = SignUpTopic.find_all_by_assignment_id(params[:id])
      
      
     #below functionality finds allows to set due dates for topics of assignment
     #below functionality finds allows to set due dates for topics of assignment
     @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)
     @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)
     if !@topics.nil?
     ....
      i=0
      @topics.each { |topic|
      ......
      i = i + 1
      }
    end
   end
   end


Line 45: Line 50:


<b>Conditional call</b><br>
<b>Conditional call</b><br>
We had to make conditional check every time the function is called for the assignment type. For instance, below method redirects to the /add_signup_topics or the /add_signup_topics_staggered page depending on assignment type
  def redirect_to_sign_up(assignment_id)
  def redirect_to_sign_up(assignment_id)
     assignment = Assignment.find(assignment_id)
     assignment = Assignment.find(assignment_id)
Line 53: Line 59:
     end
     end
  end
  end
===After Changes===
===After Changes===
We have redundant code in two methods so we merged add_signup_topics_staggered and add_signup_topics into add_signup_topics. The action calls single method for both type of assignments and renders views based on condition of staggered or non-staggered assignment.
We have redundant code in two methods so we merged add_signup_topics_staggered and add_signup_topics into add_signup_topics. The action calls single method for both type of assignments and renders views based on condition of staggered or non-staggered assignment.
Line 59: Line 66:
     load_add_signup_topics(params[:id])
     load_add_signup_topics(params[:id])
     if @assignment.staggered_deadline ==true
     if @assignment.staggered_deadline ==true
       @review_rounds = Assignment.find(params[:id]).get_review_rounds
       ....
      @topics = SignUpTopic.find_all_by_assignment_id(params[:id])
        
        
       #below functionality finds allows to set due dates for topics of assignment
       #below functionality finds allows to set due dates for topics of assignment
       @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)
       @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)
       if !@topics.nil?
       ....
        i=0
        @topics.each { |topic|
        ......
        i = i + 1
      end
        
        
       #Render a view different for staggered deadlines assignment from the add_sign_up.html.erb
       #Render a view different for staggered deadlines assignment from the add_sign_up.html.erb
Line 77: Line 78:


<b>Single method call</b><br>
<b>Single method call</b><br>
At several places in the project, there was separate method call based on staggered and non-staggered assignments. Now there is a single method call and condition check in various files for type of assignment has been removed as part of re-factoring.
At several places in the project, there was separate method call based on staggered and non-staggered assignments. We have made a single method call and condition check in various files ,for type of assignment, has been removed as part of re-factoring.
 
def redirect_to_sign_up(assignment_id)
    assignment = Assignment.find(assignment_id)
    redirect_to :action => 'add_signup_topics', :id => assignment_id
end
 
Similarly the method call is re-factored in various views.
For instance, for view _assignments_actions.html.erb
 
<b>Before Changes</b><br>
<% <b>if @assignment.staggered_deadline == true</b> %>
    <%= link_to image_tag('/images/tree_view/edit-signup-sheet-24.png', :title => 'Edit signup sheet'),
      {:controller => 'sign_up_sheet', <b>:action => 'add_signup_topics_staggered'</b>, :id => node.node_object_id},
      {:title => 'Edit signup sheet'} %>
<% else %>
    <%= link_to image_tag('/images/tree_view/edit-signup-sheet-24.png', :title => 'Edit signup sheet'),
      {:controller => 'sign_up_sheet', <b>:action => 'add_signup_topics'</b>, :id => node.node_object_id},
      {:title => 'Edit signup sheet'} %>
<% end %>
 
<b>After Changes</b><br>
  <%= link_to image_tag('/images/tree_view/edit-signup-sheet-24.png', :title => 'Edit signup sheet'),
      {:controller => 'sign_up_sheet', <b>:action => 'add_signup_topics'</b>, :id => node.node_object_id},
      {:title => 'Edit signup sheet'} %>


== Case 2 : Method Extraction  ==
== Case 2 : Method Extraction  ==


Improved Readability by extracting two separate functionalities from the same method into separate methods with appropriate naming convention and responsibilities.
Improved readability by [http://sourcemaking.com/refactoring/extract-method extracting] two separate functionalities from the same method into separate methods with appropriate naming convention and responsibilities.
 
===Current Scenario===


[b]Before Changes to code
The add_signup_topics calculated due dates for separate topics for a Staggered assignment with individual Topic deadlines.
The add_signup_topics , calculated due dates for separate topics for a Staggered assignment with individual Topic deadlines.


  def add_signup_topics
  def add_signup_topics
Line 92: Line 118:
       @topics = SignUpTopic.find_all_by_assignment_id(params[:id])
       @topics = SignUpTopic.find_all_by_assignment_id(params[:id])
        
        
      #below functionality finds allows to set due dates for topics of assignment
    <b><i>#below functionality finds allows to set due dates for topics of assignment
       @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)
       @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)
       if !@topics.nil?
       if !@topics.nil?
Line 99: Line 125:
         ......
         ......
         i = i + 1
         i = i + 1
       end
       end</i></b>
        
        
       #Render a view different for staggered deadlines assignment from the add_sign_up.html.erb
       #Render a view different for staggered deadlines assignment from the add_sign_up.html.erb
Line 106: Line 132:
  end
  end


[b] After Changes to Code
===After Changes===
The logic to calculate due_dates was moved to a separate method called assignment_due_date , as this is only valid for staggered assignments   
 
and hence it should be placed in a different method instead of merging it the add_signup_topics
The logic to calculate due_dates was moved to a separate method called assignment_due_date , as this is only valid for staggered assignments   
and hence it should be placed in a different method instead of merging it the add_signup_topics
   
   
  def add_signup_topics
  def add_signup_topics
Line 116: Line 143:
       @topics = SignUpTopic.find_all_by_assignment_id(params[:id])
       @topics = SignUpTopic.find_all_by_assignment_id(params[:id])
      
      
   #Function call to set up due dates
   <b><i>#Function call to set up due dates
       assignment_due_date
       assignment_due_date</i></b>
        
        
   #Render a view different from the add_sign_up.html.erb  , so rendering a view for staggered deadlines.
   #Render a view different from the add_sign_up.html.erb  , so rendering a view for staggered deadlines.
Line 125: Line 152:


   def assignment_due_date
   def assignment_due_date
      #Use this until you figure out how to initialize this array
    #below functionality finds allows to set due dates for topics of assignment
    @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)
      @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)
 
      if !@topics.nil?
  if !@topics.nil?
        i=0
      i=0
        @topics.each { |topic|
      @topics.each { |topic|
        ......
      .......
         i = i + 1
         i = i + 1
       }
       end
    end
   end
   end


Line 140: Line 165:
===Current Scenario===
===Current Scenario===
In the assignment.rb model, there exists one method(check_condition) which checks whether review, meta-review, etc are allowed or not. This method makes database calls to TopicDeadline table, DueDate and DeadlineType tables. Following is the code which was existing before the changes were made. It has the logic to find the nearest due date that hasn't passed.
In the assignment.rb model, there exists one method(check_condition) which checks whether review, meta-review, etc are allowed or not. This method makes database calls to TopicDeadline table, DueDate and DeadlineType tables. Following is the code which was existing before the changes were made. It has the logic to find the nearest due date that hasn't passed.
<b>assignment.rb</b>


     drop_topic_deadline_id = DeadlineType.find_by_name('drop_topic').id
     drop_topic_deadline_id = DeadlineType.find_by_name('drop_topic').id
Line 158: Line 185:
     end
     end


===After changes===
The database queries have been moved to their respective models. The queries to TopicDeadline table, DueDate table and DeadlineType table have been moved to their models. Below is the new code in assignments.rb model which calls TopicDeadline.rb, DueDate.rb and DeadlineType.rb respectively for the queries in their tables:


===After changes===
<b>assignment.rb</b> model
The database queries have been moved to their respective models. The queries to TopicDeadline table, DueDate table and DeadlineType table have been moved to their models. Below is the code in assignments.rb model which calls TopicDeadline.rb, DueDate.rb and DeadlineType.rb respectively for the queries in their tables:


  if self.staggered_deadline?
  if self.staggered_deadline?
   next_due_date = find_next_due_date(topic_id,self.id)
   next_due_date = TopicDeadline.find_next_due_date(topic_id,self.id)
  else
  else
   next_due_date = find_next_due_date(self.id)
   next_due_date = DueDate.find_next_due_date(self.id)
end
 
New <b>find_next_due_date</b> defined in <b>topic_deadline.rb</b>
def self.find_next_due_date(topic_id,assignment_id)
    drop_topic_deadline_id = DeadlineType.find_drop_topic_deadline_id()
    if topic_id
      TopicDeadline.find(:first, :conditions => ['topic_id = ? and due_at >= ? and deadline_type_id <> ?',
                        topic_id, Time.now, drop_topic_deadline_id], :order => 'due_at')
    else
      TopicDeadline.find(:first, :conditions => ['assignment_id = ? and due_at >= ? and deadline_type_id <> ?',
                        assignment_id, Time.now, drop_topic_deadline_id], :joins => {:topic => :assignment}, :order => 'due_at')
    end
end
 
New <b>find_next_due_date</b> method defined in <b>due_date.rb</b>
 
def self.find_next_due_date(assignment_id
  drop_topic_deadline_id = DeadlineType.find_drop_topic_deadline_id()
  DueDate.find(:first, :conditions => ['assignment_id = ? and due_at >= ? and deadline_type_id <> ?',
                assignment_id, Time.now, drop_topic_deadline_id], :order => 'due_at')
end
 
New <b>find_drop_topic_deadline_id</b> method defined in <b>deadline_type.rb</b>
 
def self.find_drop_topic_deadline_id()
  DeadlineType.find_by_name('drop_topic').id
  end
  end


== Case 4 : DeadCode Elimination in  sign_up_sheet_controller.rb ==
== Case 4 : DeadCode Elimination in  sign_up_sheet_controller.rb ==


The local variables topic_set and topic under the staggered deadline check statement were never used in the entire scope of the project , it introduced code smells. So it was checked if there was any reference to this code, and upon discovering it was not used anywhere, we decided to eliminate this [http://en.wikipedia.org/wiki/Dead_code_elimination deadcode].
===Current Scenario===
def create_default_for_microtask
    .....
 
    if @assignment.staggered_deadline?
      topic_set = Array.new
      topic = @sign_up_topic.id
    end
   
    .....
  end
===After Changes===
def create_default_for_microtask
    .....
 
    .....
  end
=Steps to verify changes=
Log into the application with the user having instructor's role or super user's role.
1. Go to Manage -> Assignments and hover over the assignment “StaggeredDeadlines_Refactoring”.
2. Click on “New Topic”.
3. Enter the details of the topic you want to create. Create at least 2 topics for better analysis of the project.
[[File:AddTopic.png]]
4. Now once you have created two new topics , then Click on  “Show start/due date”.
5. You should be able to view the new start and due dates of your topics confirming these are the staggered deadlines for “StaggeredDeadlines_Refactoring” which has many topics with different deadlines.
[[File:ShowdueDate.png]]
6. Now to view the difference between the Stagerred and Non-Staggered Deadlines, follow steps.
7. Go to Manage -> Assignments and hover over “SALT Demo” and click on “Edit  Signup sheet”.
8. Click on “New Topic” and fill in the details for a new topic.
[[File:NonstaggeredAddTopic.png]]
9. Now you will be able to see that there is no option for “Show Start/Due Date” which is because of the reason that this is not a staggered assignment.
[[File:NodueDate.png]]
10. Hence there is only one due date and has no dependencies on others.
= Concise Report on Refactoring =
The project had redundant code, multiple functionalities in a method and extra conditional statements in controller, model and view files. We analyzed each occurrence of these issues, thus creating a list which needed to be refactored. Controllers like sign_up_sheet_controler, assignment.rb, due_date.rb,deadline_types.rb along with other files were modified. It's very difficult to explain the sequential and detailed procedure, but it has been explained in the sections above within the capacity of the Wiki.


= Additional Learning and Future Work=
= Additional Learning and Future Work=
In current implementation, our database has two tables for staggered and non-staggered deadlines. For any future improvements, we can make use of [http://blog.thirst.co/post/14885390861/rails-single-table-inheritance STI (Single Table Inheritance)] and analyze if applying STI would really impact query performance when application runs and extracts data from database.
= References=
[http://expertiza.ncsu.edu/ Expertiza]<br>
[http://eewang.github.io/blog/2013/03/12/how-and-when-to-use-single-table-inheritance-in-rails/ Single Table Inheritance]<br>
[http://en.wikipedia.org/wiki/Code_refactoring Code Refactoring]<br>
[http://en.wikipedia.org/wiki/Code_smell Code smell]

Latest revision as of 03:43, 31 October 2013

E810 Regularize staggered-deadline assignments

This page provides a description of the OSS project conducted on Expertiza which was done as the part of the Object Oriented Languages and Systems coursework.

Introduction to Expertiza

Expertiza is a project developed using Ruby on Rails platform. It provides features like peer review, team assignments and submission of projects. This can be achieved by submitting code base, URL of hosted code on remote server and Wiki submissions. It is open source application and the code can be cloned from github. This application provides an efficient way to manage assignments, grades and reviews. This makes the process easier and faster when the class strength is large.

Expertiza is supported by National Science Foundation under Grant No. 0536558. Additional funding from the NCSU Learning in a Technology-Rich Environment (LITRE) program, the NCSU Faculty Center for Teaching and Learning, the NCSU STEM Initiative, and the Center for Advanced Computing and Communication.

Project Resources

GitHub Project Repository

VCL IP Address

Expertiza Wiki

Problem Statement

According to the existing code flow, Assignment deadlines belonged to two categories i.e. Staggered Deadlines and Deadlines (Normal Due Date). If an Assignment has different topics which have dependencies i.e. if Assignment A has two topics Topic A and Topic B , where Topic A can have deadline before Topic B, and therefore the Due Date of Assignment A is dependent on deadlines of Topics designed in that particular assignment. Hence these deadlines are called Staggered Deadlines. On the other hand, If Assignment A has Topic A and Topic B, but they all have the same deadlines i.e. there is no dependency on each other then that's Normal Due Date scenario. In this case an Assignment may or may not have separate Topics.

Original code had various check statements for staggered and normal deadlines, which had to eliminated and structured in a way that with minimum checks we could distinguish between Staggered and Normal Due Dates. This required refactoring and restructuring of the whole functionality where we reduce these checks by distributing code into separate actions.

Additionally, project required removal of redundant and dead code which enhances code readability and optimizes code performance.

Re-factored Code Cases

Case 1 : Refactoring in sign_up_sheet_controller.rb

Current Scenario

Different method definitions for setting up staggered and non-staggered assignment sign up sheet.

method:add_signup_topics_staggered
This method lists all the available topics for an assignment and allows admin to set due dates for assignment.Staggered means that different topics can have different deadlines.

 def add_signup_topics_staggered
   load_add_signup_topics(params[:id])
   ....
   
   #below functionality finds allows to set due dates for topics of assignment
   @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)
   ....
 end

method:add_signup_topics
It is similar to the above function except that all the topics and review/submission rounds have the similar deadlines.

 def add_signup_topics
   load_add_signup_topics(params[:id])
 end

Conditional call
We had to make conditional check every time the function is called for the assignment type. For instance, below method redirects to the /add_signup_topics or the /add_signup_topics_staggered page depending on assignment type

def redirect_to_sign_up(assignment_id)
   assignment = Assignment.find(assignment_id)
   if assignment.staggered_deadline == true
     redirect_to :action => 'add_signup_topics_staggered', :id => assignment_id
   else
     redirect_to :action => 'add_signup_topics', :id => assignment_id
   end
end

After Changes

We have redundant code in two methods so we merged add_signup_topics_staggered and add_signup_topics into add_signup_topics. The action calls single method for both type of assignments and renders views based on condition of staggered or non-staggered assignment.

def add_signup_topics
   load_add_signup_topics(params[:id])
   if @assignment.staggered_deadline ==true
      ....
      
      #below functionality finds allows to set due dates for topics of assignment
      @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)
      ....
      
      #Render a view different for staggered deadlines assignment from the add_sign_up.html.erb
      render "add_signup_topics_staggered"
  end
end

Single method call
At several places in the project, there was separate method call based on staggered and non-staggered assignments. We have made a single method call and condition check in various files ,for type of assignment, has been removed as part of re-factoring.

def redirect_to_sign_up(assignment_id)
   assignment = Assignment.find(assignment_id)
   redirect_to :action => 'add_signup_topics', :id => assignment_id
end

Similarly the method call is re-factored in various views. For instance, for view _assignments_actions.html.erb

Before Changes

<% if @assignment.staggered_deadline == true %>
   <%= link_to image_tag('/images/tree_view/edit-signup-sheet-24.png', :title => 'Edit signup sheet'), 
     {:controller => 'sign_up_sheet', :action => 'add_signup_topics_staggered', :id => node.node_object_id}, 
     {:title => 'Edit signup sheet'} %>
<% else %>
   <%= link_to image_tag('/images/tree_view/edit-signup-sheet-24.png', :title => 'Edit signup sheet'), 
     {:controller => 'sign_up_sheet', :action => 'add_signup_topics', :id => node.node_object_id}, 
     {:title => 'Edit signup sheet'} %>
<% end %>

After Changes

 <%= link_to image_tag('/images/tree_view/edit-signup-sheet-24.png', :title => 'Edit signup sheet'), 
     {:controller => 'sign_up_sheet', :action => 'add_signup_topics', :id => node.node_object_id}, 
     {:title => 'Edit signup sheet'} %>

Case 2 : Method Extraction

Improved readability by extracting two separate functionalities from the same method into separate methods with appropriate naming convention and responsibilities.

Current Scenario

The add_signup_topics calculated due dates for separate topics for a Staggered assignment with individual Topic deadlines.

def add_signup_topics
  load_add_signup_topics(params[:id])
  if @assignment.staggered_deadline ==true
     @review_rounds = Assignment.find(params[:id]).get_review_rounds
     @topics = SignUpTopic.find_all_by_assignment_id(params[:id])
     
    #below functionality finds allows to set due dates for topics of assignment
     @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)
     if !@topics.nil?
       i=0
       @topics.each { |topic|
       ......
       i = i + 1
     end
     
     #Render a view different for staggered deadlines assignment from the add_sign_up.html.erb
     render "add_signup_topics_staggered"
 end
end

After Changes

The logic to calculate due_dates was moved to a separate method called assignment_due_date , as this is only valid for staggered assignments and hence it should be placed in a different method instead of merging it the add_signup_topics

def add_signup_topics
   load_add_signup_topics(params[:id])
 if @assignment.staggered_deadline ==true
     @review_rounds = Assignment.find(params[:id]).get_review_rounds
     @topics = SignUpTopic.find_all_by_assignment_id(params[:id])
    
  #Function call to set up due dates
     assignment_due_date
     
  #Render a view different from the add_sign_up.html.erb  , so rendering a view for staggered deadlines.
     render "add_signup_topics_staggered"
   end
 end
 def assignment_due_date
    #below functionality finds allows to set due dates for topics of assignment
     @duedates = SignUpTopic.find_by_sql("SELECT s.id as topic_id FROM sign_up_topics s WHERE s.assignment_id = " + params[:id].to_s)
     if !@topics.nil?
       i=0
       @topics.each { |topic|
       ......
       i = i + 1
     end
 end

Case 3 : Modularize Code in application.rb

Current Scenario

In the assignment.rb model, there exists one method(check_condition) which checks whether review, meta-review, etc are allowed or not. This method makes database calls to TopicDeadline table, DueDate and DeadlineType tables. Following is the code which was existing before the changes were made. It has the logic to find the nearest due date that hasn't passed.

assignment.rb

    drop_topic_deadline_id = DeadlineType.find_by_name('drop_topic').id
   if self.staggered_deadline?
     
     if topic_id
       
       next_due_date = TopicDeadline.find(:first, :conditions => ['topic_id = ? and due_at >= ? and deadline_type_id <> ?', 
                                  topic_id, Time.now, drop_topic_deadline_id], :order => 'due_at')
     else
       
       next_due_date = TopicDeadline.find(:first, :conditions => ['assignment_id = ? and due_at >= ? and deadline_type_id <> ?', 
                                 self.id, Time.now, drop_topic_deadline_id], :joins => {:topic => :assignment}, :order => 'due_at')
     end
   else
     next_due_date = DueDate.find(:first, :conditions => ['assignment_id = ? and due_at >= ? and deadline_type_id <> ?', 
                                 self.id, Time.now, drop_topic_deadline_id], :order => 'due_at')
   end

After changes

The database queries have been moved to their respective models. The queries to TopicDeadline table, DueDate table and DeadlineType table have been moved to their models. Below is the new code in assignments.rb model which calls TopicDeadline.rb, DueDate.rb and DeadlineType.rb respectively for the queries in their tables:

assignment.rb model

if self.staggered_deadline?
  next_due_date = TopicDeadline.find_next_due_date(topic_id,self.id)
else
  next_due_date = DueDate.find_next_due_date(self.id)
end

New find_next_due_date defined in topic_deadline.rb

def self.find_next_due_date(topic_id,assignment_id)
   drop_topic_deadline_id = DeadlineType.find_drop_topic_deadline_id()
   if topic_id
     TopicDeadline.find(:first, :conditions => ['topic_id = ? and due_at >= ? and deadline_type_id <> ?', 
                        topic_id, Time.now, drop_topic_deadline_id], :order => 'due_at')
   else
     TopicDeadline.find(:first, :conditions => ['assignment_id = ? and due_at >= ? and deadline_type_id <> ?', 
                        assignment_id, Time.now, drop_topic_deadline_id], :joins => {:topic => :assignment}, :order => 'due_at')
   end
end

New find_next_due_date method defined in due_date.rb

def self.find_next_due_date(assignment_id
  drop_topic_deadline_id = DeadlineType.find_drop_topic_deadline_id()
  DueDate.find(:first, :conditions => ['assignment_id = ? and due_at >= ? and deadline_type_id <> ?', 
               assignment_id, Time.now, drop_topic_deadline_id], :order => 'due_at')
end

New find_drop_topic_deadline_id method defined in deadline_type.rb

def self.find_drop_topic_deadline_id()
  DeadlineType.find_by_name('drop_topic').id
end

Case 4 : DeadCode Elimination in sign_up_sheet_controller.rb

The local variables topic_set and topic under the staggered deadline check statement were never used in the entire scope of the project , it introduced code smells. So it was checked if there was any reference to this code, and upon discovering it was not used anywhere, we decided to eliminate this deadcode.

Current Scenario

def create_default_for_microtask
   .....
 
   if @assignment.staggered_deadline?
     topic_set = Array.new
     topic = @sign_up_topic.id
   end
   
   .....
 end

After Changes

def create_default_for_microtask
   .....
 
   .....
 end

Steps to verify changes

Log into the application with the user having instructor's role or super user's role.

1. Go to Manage -> Assignments and hover over the assignment “StaggeredDeadlines_Refactoring”.

2. Click on “New Topic”.

3. Enter the details of the topic you want to create. Create at least 2 topics for better analysis of the project.

4. Now once you have created two new topics , then Click on “Show start/due date”.

5. You should be able to view the new start and due dates of your topics confirming these are the staggered deadlines for “StaggeredDeadlines_Refactoring” which has many topics with different deadlines.

6. Now to view the difference between the Stagerred and Non-Staggered Deadlines, follow steps.

7. Go to Manage -> Assignments and hover over “SALT Demo” and click on “Edit Signup sheet”.

8. Click on “New Topic” and fill in the details for a new topic.

9. Now you will be able to see that there is no option for “Show Start/Due Date” which is because of the reason that this is not a staggered assignment.

10. Hence there is only one due date and has no dependencies on others.

Concise Report on Refactoring

The project had redundant code, multiple functionalities in a method and extra conditional statements in controller, model and view files. We analyzed each occurrence of these issues, thus creating a list which needed to be refactored. Controllers like sign_up_sheet_controler, assignment.rb, due_date.rb,deadline_types.rb along with other files were modified. It's very difficult to explain the sequential and detailed procedure, but it has been explained in the sections above within the capacity of the Wiki.

Additional Learning and Future Work

In current implementation, our database has two tables for staggered and non-staggered deadlines. For any future improvements, we can make use of STI (Single Table Inheritance) and analyze if applying STI would really impact query performance when application runs and extracts data from database.

References

Expertiza
Single Table Inheritance
Code Refactoring
Code smell