CSC/ECE 517 Spring 2019 - Project E1906. Refactor stage deadlines in Assignment.rb: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
No edit summary
No edit summary
 
(36 intermediate revisions by 3 users not shown)
Line 18: Line 18:


[https://github.com/jwboykin/expertiza Expertiza_Link_for_this_Project]
[https://github.com/jwboykin/expertiza Expertiza_Link_for_this_Project]
[https://github.com/expertiza/expertiza/pull/1376 Pull_request_link]


=='''Problem Statement'''==
=='''Problem Statement'''==
Line 29: Line 31:


The goal of the project focuses on refactoring some of the above more complex methods, modifying some of the language to make it more Ruby friendly, removing some redundant code. This project is basically an attempt to make this part of the application easier to read and maintain.
The goal of the project focuses on refactoring some of the above more complex methods, modifying some of the language to make it more Ruby friendly, removing some redundant code. This project is basically an attempt to make this part of the application easier to read and maintain.
=='''Files modified in this project'''==
*assignment.rb
*assignment_spec.rb


=='''Solutions Implemented'''==
=='''Solutions Implemented'''==
Line 34: Line 41:
==='''Problem 1'''===
==='''Problem 1'''===


The above methods were being used at various places to check if the assignment is 'Finished' which made the code very redundant. Assignment is said to be finished if the next_due_date is nil.
Multiple functions in the code were calling DueDate.get_next_due_date(self.id, topic_id).
 
===='''Solution'''====
 
A new private function next_due_date was added. Other calls to the DueDate.get_next_due_date were replaced.
 
<pre>
  #returns the next due date
  def next_due_date(topic_id = nil)
    DueDate.get_next_due_date(self.id, topic_id)
  end
</pre>
 
*One example of refactoring
[[File:Duedate E1906.png|center]]
 
==='''Problem 2'''===
 
The above five methods were being used at various places to check if the assignment is 'Finished' which made the code very redundant. Assignment is said to be finished if the next_due_date is nil.


===='''Solution'''====
===='''Solution'''====
Line 43: Line 68:
   # New function to check if the assignment is finished
   # New function to check if the assignment is finished
   def finished?( topic_id = nil )
   def finished?( topic_id = nil )
     DueDate.get_next_due_date(self.id, topic_id).nil?
     next_due_date(topic_id).nil?
   end
   end
</pre>
</pre>


We have called this method at all the places where previously different methods were being used to check the same thing.
We have called this method at all the places where previously different methods were being used to check the same thing.
*Signup sheet table uses assignment finished? method instead of current_stage_name
*Signup sheet table uses assignment finished? method instead of current_stage_name
[[File:Problem1_1.png‎|center]]
[[File:Problem1_1.png‎|center]]
Line 54: Line 80:
[[File:Problem1_2.png|center]] [[File:Problem1_3.png|center]]
[[File:Problem1_2.png|center]] [[File:Problem1_3.png|center]]


==='''Problem 2'''===
==='''Problem 3'''===


The check for a topic_id to be nil when it is a staggered assignment and return 'Unknown' when it is true was being done at many places.This was not following Ruby and Rails rules.
The check for a topic_id to be nil when it is a staggered assignment and return 'Unknown' when it is true was being done at many places.This was not following Ruby and Rails rules.
Line 73: Line 99:
[[File:Topic missing.png|center]]
[[File:Topic missing.png|center]]


==='''Problem 3'''===
*stage_deadline( topic_id = nil) changed according to the modifications done
 
<pre>
  #Gives the next stage deadline date and nil if its finished assignment
  def stage_deadline(topic_id = nil)
    return 'Unknown' if topic_missing?(topic_id)
    return nil if finished?(topic_id)
    next_due_date(topic_id).due_at.to_s
  end
</pre>
 
==='''Problem 4'''===


find_current_stage(topic_id=nil) method is being used only locally in the assignment.rb and once in the student_teams controller to check to check whether to display or not the reviews option for team members.This method was calling another function to get the next_due_date for an assignment that was creating a chain of calls.
find_current_stage(topic_id=nil) method is being used only locally in the assignment.rb and once in the student_teams controller to check to check whether to display or not the reviews option for team members.This method was calling another function to get the next_due_date for an assignment that was like nesting of calls and not very Ruby standard.


===='''Solution'''====
===='''Solution'''====


We have removed this function.We have replaced it with finished? method in the student_teams controller(shown in screenshot above in Problem 1) and with DueDate.get_next_due_date where the next due date was required.
We have removed this function and replaced it with finished? method in the student_teams controller(shown in screenshot above in Problem 2) and with DueDate.get_next_due_date where the next due date was required.


<pre>
<pre>
Line 89: Line 126:
</pre>
</pre>


==='''Problem 4'''===
==='''Problem 5'''===


get_current_stage method is returning the stage name which the assignment is in at current time.This is not very clear from the name of the method.
get_current_stage method is returning the stage name which the assignment is in at current time.This is not very clear from the name of the method.
Line 95: Line 132:
===='''Solution'''====
===='''Solution'''====


We have changed the method name to get_current_stage_name(topic_id=nil) to make it more clear to understand.
We have changed the method name to current_stage_name(topic_id=nil) to make it more clear to understand.


<pre>
<pre>
   def get_current_stage_name(topic_id = nil)
  #Gives the current stage name of the assignment
   def current_stage_name(topic_id = nil)
     return 'Unknown' if topic_missing?(topic_id)
     return 'Unknown' if topic_missing?(topic_id)
     due_date = DueDate.get_next_due_date(self.id, topic_id)
     due_date = next_due_date(topic_id)
     finished?(topic_id)? "Finished" : DeadlineType.find(due_date.deadline_type_id).name
     finished?(topic_id)? "Finished" : DeadlineType.find(due_date.deadline_type_id).name
   end
   end
</pre>
</pre>


*Refactored get_current_stage with get_current_stage_name
*Refactored get_current_stage with current_stage_name


[[File:Get current stage name.png|center]]
[[File:Current stage name new.png|center]]


==='''Problem 5'''===
==='''Problem 6'''===


current_stage_name(topic_id=nil) method was implementing things which was already being done by other methods and thus this method was redundant.  
current_stage_name(topic_id=nil)[This is the initial one] method was implementing things which was already being done by other methods and thus this method was redundant.  
The initial code given was:
The initial code given was:


Line 139: Line 177:
<pre>
<pre>
   def current_stage_name(topic_id = nil)
   def current_stage_name(topic_id = nil)
     if self.staggered_deadline? and topic_id.nil?
     if topic_missing?(topic_id)
       return 'Unknown'
       return 'Unknown'
     else
     else
Line 147: Line 185:
</pre>
</pre>


Looking at the get_current_stage_name(topic_id=nil) method shown below and comparing it to what we have above, this code was surely redundant.That is why we removed this code and made appropriate changes wherever required within assignment.rb.
Looking at the current_stage_name(topic_id=nil)[New one] method shown below and comparing it to what we have above, this code was surely redundant.That is why we removed this code and made appropriate changes wherever required within assignment.rb.
*Below is the get_current_stage_name method for reference
*Below is the current_stage_name method for reference


<pre>
<pre>
   def get_current_stage_name(topic_id = nil)
  #Gives the current stage name of the assignment
   def current_stage_name(topic_id = nil)
     return 'Unknown' if topic_missing?(topic_id)
     return 'Unknown' if topic_missing?(topic_id)
     due_date = DueDate.get_next_due_date(self.id, topic_id)
     due_date = next_due_date(topic_id)
     finished?(topic_id)? "Finished" : DeadlineType.find(due_date.deadline_type_id).name
     finished?(topic_id)? "Finished" : DeadlineType.find(due_date.deadline_type_id).name
   end
   end
</pre>
</pre>


After removing the current_stage_name method, its uses were replaced with get_current_stage_name. One example of the refactoring is shown below:
The associated rspec tests that tested the old current_stage_name method were also no longer needed.


[[File:Current_stage.png|center]]
==='''Problem 7'''===


link_for_current_stage function served no purpose. The function checked to see if any given due date had a link (presumably web URL) and would return it. The database had no values and there is no where in the application this URL could be provided. There doesn't appear to be any use for having a separate URL for each stage.


The associated rspec tests that tested the current_stage method were also no longer needed.
===='''Solution'''====


==='''Problem 6'''===
Removed function.
 
[[File:Big1.png|center]]
 
Removed test related to the function. Removed the one call to this function.


===='''Solution'''====
[[File:big2.png|center]]


=='''Automated Testing using RSPEC'''==
=='''Automated Testing using RSPEC'''==
We have created and modified the tests as per the changes done in our project.We have added the tests to the file [https://github.com/jwboykin/expertiza/blob/master/spec/models/assignment_spec.rb expertiza/spec/models/assignment_spec.rb] and they are passing 100%.


===='''Tests for get_current_stage_name'''====
===='''Tests for get_current_stage_name'''====


The current version of expertiza did not have any tests for get_current_stage(topic_id=nil) method.Now as we have changed it to get_current_stage_name(topic_id=nil),we have added its tests as well.
The current version of expertiza did not have any tests for get_current_stage(topic_id=nil) method.Now as we have changed it to current_stage_name(topic_id=nil),we have added its tests as well.


<pre>
<pre>
   describe '#get_current_stage_name' do
   describe '#current_stage_name' do
     context 'when topic id is nil and current assignment has staggered deadline' do
     context 'when topic id is nil and current assignment has staggered deadline' do
       it 'returns Unknown' do
       it 'returns Unknown' do
         allow(assignment).to receive(:topic_missing?).and_return(true)
         allow(assignment).to receive(:topic_missing?).and_return(true)
         expect(assignment.get_current_stage_name).to eq('Unknown')
         expect(assignment.current_stage_name).to eq('Unknown')
       end
       end
     end
     end
Line 188: Line 234:
         it 'returns Finished' do
         it 'returns Finished' do
           allow(assignment).to receive(:finished?).with(123).and_return(true)
           allow(assignment).to receive(:finished?).with(123).and_return(true)
           expect(assignment.get_current_stage_name(123)).to eq('Finished')
           expect(assignment.current_stage_name(123)).to eq('Finished')
         end
         end
       end
       end
Line 196: Line 242:
           allow(assignment).to receive(:finished?).with(123).and_return(false)
           allow(assignment).to receive(:finished?).with(123).and_return(false)
           allow(assignment).to receive(:topic_missing?).with(123).and_return(false)
           allow(assignment).to receive(:topic_missing?).with(123).and_return(false)
           allow(DueDate).to receive(:get_next_due_date).with(1,123).and_return(assignment_due_date)
           allow(assignment).to receive(:next_due_date).with(123).and_return(assignment_due_date)
           deadline = create(:deadline_type, id: 1, name:"Review")
           deadline = create(:deadline_type, id: 1, name:"Review")
           allow(DeadlineType).to receive(:find).with(1).and_return(deadline)
           allow(DeadlineType).to receive(:find).with(1).and_return(deadline)
           expect(assignment.get_current_stage_name(123)).to eq('Review')
           expect(assignment.current_stage_name(123)).to eq('Review')
         end
         end
       end  
       end
     end
     end
   end
   end
Line 215: Line 261:
       it 'returns True' do
       it 'returns True' do
         allow(DueDate).to receive(:get_next_due_date).with(1, 123).and_return(nil)
         allow(DueDate).to receive(:get_next_due_date).with(1, 123).and_return(nil)
         expect(assignment.finished?(123)).to eq(TRUE)
         expect(assignment.finished?(123)).to eq(true)
       end
       end
     end
     end
Line 223: Line 269:
     context 'when there is a next due date' do
     context 'when there is a next due date' do
       it 'returns False' do
       it 'returns False' do
         allow(DueDate).to receive(:get_next_due_date).with(1, 123).and_return('2021-11-11 11:11:11')
         allow(DueDate).to receive(:get_next_due_date).with(1,123).and_return('2021-11-11 11:11:11')
         expect(assignment.finished?(123)).to eq(false)
         expect(assignment.finished?(123)).to eq(false)
       end
       end
Line 256: Line 302:
         it 'returns due date' do
         it 'returns due date' do
           allow(assignment).to receive(:finished?).with(123).and_return(false)
           allow(assignment).to receive(:finished?).with(123).and_return(false)
           allow(DueDate).to receive(:get_next_due_date).with(1, 123).and_return(assignment_due_date)
           allow(assignment).to receive(:next_due_date).with(123).and_return(assignment_due_date)
           expect(assignment.stage_deadline(123)).to match('2011-11-11 11:11:11')
           expect(assignment.stage_deadline(123)).to match('2011-11-11 11:11:11')
         end
         end
       end
       end
     end
     end
   end  
   end
</pre>  
</pre>  




=='''Team'''==
=='''Team'''==
[mailto:jwboykin@ncsu.edu Jimmy Boykin]<br>
[mailto:jwboykin@ncsu.edu Jimmy Boykin]<br>
[mailto:rkaur@ncsu.edu Ramandeep Kaur]<br>
[mailto:rkaur@ncsu.edu Ramandeep Kaur]<br>
[mailto:sbasnet2@ncsu.edu Sushan Basnet]
[mailto:sbasnet2@ncsu.edu Sushan Basnet]

Latest revision as of 21:42, 30 March 2019

This wiki page is for the description of changes made under E1906 OSS assignment for Spring 2019, CSC/ECE 517.

Expertiza Background

Expertiza is a web application developed using Ruby on Rails Framework whose creation and maintenance is taken care of by students as well as the faculty of NCSU.It's code is available on Github Expertiza on GitHub.Expertiza allows the instructor to create and edit new as well as existing assignments.This also includes creating a wide range of topics under each assignment which students can sign up for.They can also publish surveys and reviews,view statistical results, set deadlines for assignments and make announcements.It provides a platform for students to signup for topics,form teams,view and submit assignments and give peer reviews and feedback.

Stage Deadline Background

An assignment in Expertiza has many deadlines possible: deadlines for submission and review in each round,deadlines for signing up and dropping topics,submitting meta-reviews,etc.Depending on what the next deadline is,an Expertiza assignment is said to be in a particular kind of what is called "stage".For example,if the next deadline(after the current time) is a submission deadline which is a DueDate object,then the assignment is said to be in a "submission" phase.

Depending on which stage an assignment is in, certain activities may be permissible or impermissible.For example, the default for a submission stage is to allow submission but not review,and in a review stage,the default is to allow review but not submission.These defaults may be changed by checking the relevant boxes on the Due Dates tab of assignment creation or editing.In the figure below it can be seen the "Other's work" link is grayed out as review is not permissible during submission phase.

Important Links

Deployed_Project_Link

Expertiza_Link_for_this_Project

Pull_request_link

Problem Statement

E1906 is an Expertiza based OSS project which deals basically with refactoring stage deadlines in assignment.rb file.Class assignment.rb has several methods for checking what kind of stage an assignment is in at the current time.These are:

  • current_stage_name(topic_id = nil)
  • find_current_stage(topic_id = nil)
  • get_current_stage(topic_id = nil)
  • link_for_current_stage(topic_id = nil)
  • stage_deadline(topic_id = nil)

The goal of the project focuses on refactoring some of the above more complex methods, modifying some of the language to make it more Ruby friendly, removing some redundant code. This project is basically an attempt to make this part of the application easier to read and maintain.

Files modified in this project

  • assignment.rb
  • assignment_spec.rb

Solutions Implemented

Problem 1

Multiple functions in the code were calling DueDate.get_next_due_date(self.id, topic_id).

Solution

A new private function next_due_date was added. Other calls to the DueDate.get_next_due_date were replaced.

  #returns the next due date
  def next_due_date(topic_id = nil)
    DueDate.get_next_due_date(self.id, topic_id)
  end
  • One example of refactoring

Problem 2

The above five methods were being used at various places to check if the assignment is 'Finished' which made the code very redundant. Assignment is said to be finished if the next_due_date is nil.

Solution

We have created a new method "finished?" that checks if the next_due_date is nil (which means that the assignment is "finished").

  # New function to check if the assignment is finished
  def finished?( topic_id = nil )
    next_due_date(topic_id).nil?
  end

We have called this method at all the places where previously different methods were being used to check the same thing.

  • Signup sheet table uses assignment finished? method instead of current_stage_name
  • Use assignment finished? method where only a comparison to "Finished" was being done using get_current_stage and find_current_stage

Problem 3

The check for a topic_id to be nil when it is a staggered assignment and return 'Unknown' when it is true was being done at many places.This was not following Ruby and Rails rules.

Solution

To make the code DRY and more easy to understand,we have created a new private method "topic_missing?" to check if the topic_id is nil in case of staggered assignment.

  # Function to check if topic id is null when its a staggered assignment
  def topic_missing?( topic_id = nil)
    topic_id.nil? and self.staggered_deadline?
  end
  • Refactoring with new method topic_missing?
  • stage_deadline( topic_id = nil) changed according to the modifications done
  #Gives the next stage deadline date and nil if its finished assignment 
  def stage_deadline(topic_id = nil)
    return 'Unknown' if topic_missing?(topic_id)
    return nil if finished?(topic_id)
    next_due_date(topic_id).due_at.to_s
  end

Problem 4

find_current_stage(topic_id=nil) method is being used only locally in the assignment.rb and once in the student_teams controller to check to check whether to display or not the reviews option for team members.This method was calling another function to get the next_due_date for an assignment that was like nesting of calls and not very Ruby standard.

Solution

We have removed this function and replaced it with finished? method in the student_teams controller(shown in screenshot above in Problem 2) and with DueDate.get_next_due_date where the next due date was required.

 Before :
 due_date = find_current_stage(topic_id)
 
 After :
 due_date = DueDate.get_next_due_date(self.id, topic_id)

Problem 5

get_current_stage method is returning the stage name which the assignment is in at current time.This is not very clear from the name of the method.

Solution

We have changed the method name to current_stage_name(topic_id=nil) to make it more clear to understand.

  #Gives the current stage name of the assignment
  def current_stage_name(topic_id = nil)
    return 'Unknown' if topic_missing?(topic_id)
    due_date = next_due_date(topic_id)
    finished?(topic_id)? "Finished" : DeadlineType.find(due_date.deadline_type_id).name
  end
  • Refactored get_current_stage with current_stage_name

Problem 6

current_stage_name(topic_id=nil)[This is the initial one] method was implementing things which was already being done by other methods and thus this method was redundant. The initial code given was:

  def current_stage_name(topic_id = nil)
    if self.staggered_deadline?
      return (topic_id.nil? ? 'Unknown' : get_current_stage(topic_id))
    end
    due_date = find_current_stage(topic_id)

    unless self.staggered_deadline?
      if due_date != 'Finished' && !due_date.nil? && !due_date.deadline_name.nil?
        return due_date.deadline_name
      else
        return get_current_stage(topic_id)
      end
    end
  end

First thing we noticed was that due_date.deadline_name was always nil because there was no field to enter the deadline name on the form, and therefore, the statement

 return due_date.deadline_name 

was never getting executed.

Solution

This method was checking for the due_date.deadline_name which was always nil. After removing the dead code, we get:

  def current_stage_name(topic_id = nil)
    if topic_missing?(topic_id)
      return 'Unknown'
    else
      get_current_stage(topic_id)
    end
  end

Looking at the current_stage_name(topic_id=nil)[New one] method shown below and comparing it to what we have above, this code was surely redundant.That is why we removed this code and made appropriate changes wherever required within assignment.rb.

  • Below is the current_stage_name method for reference
  #Gives the current stage name of the assignment
  def current_stage_name(topic_id = nil)
    return 'Unknown' if topic_missing?(topic_id)
    due_date = next_due_date(topic_id)
    finished?(topic_id)? "Finished" : DeadlineType.find(due_date.deadline_type_id).name
  end

The associated rspec tests that tested the old current_stage_name method were also no longer needed.

Problem 7

link_for_current_stage function served no purpose. The function checked to see if any given due date had a link (presumably web URL) and would return it. The database had no values and there is no where in the application this URL could be provided. There doesn't appear to be any use for having a separate URL for each stage.

Solution

Removed function.

Removed test related to the function. Removed the one call to this function.

Automated Testing using RSPEC

We have created and modified the tests as per the changes done in our project.We have added the tests to the file expertiza/spec/models/assignment_spec.rb and they are passing 100%.

Tests for get_current_stage_name

The current version of expertiza did not have any tests for get_current_stage(topic_id=nil) method.Now as we have changed it to current_stage_name(topic_id=nil),we have added its tests as well.

  describe '#current_stage_name' do
    context 'when topic id is nil and current assignment has staggered deadline' do
      it 'returns Unknown' do
        allow(assignment).to receive(:topic_missing?).and_return(true)
        expect(assignment.current_stage_name).to eq('Unknown')
      end
    end

    context 'when current assignment does not have staggered deadline' do
      context 'when due date is nil' do
        it 'returns Finished' do
          allow(assignment).to receive(:finished?).with(123).and_return(true)
          expect(assignment.current_stage_name(123)).to eq('Finished')
        end
      end

      context 'when due date is not nil and due date is not equal to Finished' do
        it 'returns current stage name' do
          allow(assignment).to receive(:finished?).with(123).and_return(false)
          allow(assignment).to receive(:topic_missing?).with(123).and_return(false)
          allow(assignment).to receive(:next_due_date).with(123).and_return(assignment_due_date)
          deadline = create(:deadline_type, id: 1, name:"Review")
          allow(DeadlineType).to receive(:find).with(1).and_return(deadline)
          expect(assignment.current_stage_name(123)).to eq('Review')
        end
      end
    end
  end

Tests for finished?

We have added tests for the new method that we have added finished?.

  describe '#finished?' do
    context 'when assignment next due date is nil' do
      it 'returns True' do
        allow(DueDate).to receive(:get_next_due_date).with(1, 123).and_return(nil)
        expect(assignment.finished?(123)).to eq(true)
      end
    end
  end

  describe '#finished?' do
    context 'when there is a next due date' do
      it 'returns False' do
        allow(DueDate).to receive(:get_next_due_date).with(1,123).and_return('2021-11-11 11:11:11')
        expect(assignment.finished?(123)).to eq(false)
      end
    end
  end

As topic_missing? method is made private,there was no need to test this.

Tests for stage_deadline

We also have corrected the tests for stage_deadline according to the modifications done(It is using finished? and topic_missing? methods).

  describe '#stage_deadline' do
    context 'when topic id is nil and current assignment has staggered deadline' do
      it 'returns Unknown' do
        allow(assignment).to receive(:topic_missing?).and_return(true)
        expect(assignment.stage_deadline).to eq('Unknown')
      end
    end

    context 'when current assignment does not have staggered deadline' do
      context 'when due date is nil' do
        it 'returns nil' do
          allow(assignment).to receive(:finished?).with(123).and_return(true)
          expect(assignment.stage_deadline(123)).to be nil
        end
      end

      context 'when due date is not nil and due date is not equal to Finished' do
        it 'returns due date' do
          allow(assignment).to receive(:finished?).with(123).and_return(false)
          allow(assignment).to receive(:next_due_date).with(123).and_return(assignment_due_date)
          expect(assignment.stage_deadline(123)).to match('2011-11-11 11:11:11')
        end
      end
    end
  end


Team

Jimmy Boykin
Ramandeep Kaur
Sushan Basnet