CSC/ECE 517 Spring 2016/E1720. UI issues/fixes: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
(Initial)
 
 
(75 intermediate revisions by the same user not shown)
Line 1: Line 1:
This wiki page is for the description of changes made according to the specification of E1613 OSS assignment for Spring 2016.
This wiki page is for the description of changes made according to the specification of E1720 OSS assignment for Spring 2016.


== Peer Review Information ==
== Peer Review Information ==
For testing the changes made, the following credentials are recommended:
For testing the changes made, the following credentials are recommended:
* Instructor Login: '''username:''' ''instructor6'' '''password:''' ''password''
* Instructor Login: '''username:''' ''instructor6'' '''password:''' ''password''
* Student Login: '''username:''' ''student11'' '''password:''' ''password''
 
* Student Login: '''username:''' ''student1862'' '''password:''' ''password''
The above user is suggested for testing because is is unlike to run into issues that are unrelated to our work.
The above users are suggested for testing because there are a few users which would lead to exceptions upon login for unknown reasons completely unrelated to our work.
 
This project does compile.  Reviewers will need to perform "rake db:migrate" as we've added a database table to support notifications.
 
The testing plan is at the end of this document.  There were also specs added; however, they are in the Git repository.  Design principles were not needed since we mostly modified existing work.  However, issue #702 is using the Factory Method design pattern, and our work added content to the existing pattern.


== Introduction ==
== Introduction ==
Line 13: Line 16:


== Problem Statement ==
== Problem Statement ==
The following were the tasks identified to accomplish through this project. These tasks are part of the sign_up_sheet_controller.rb and sign_up_topic.rb
The following were the tasks identified to be accomplish through this project.  
* '''WI-1:''' Method ad_info in the sign_up_sheet_controller.rb users sql query to retrieve the desired objects. This needs to be changed so that action records are used instead and a hash/array of the required object is returned.
* '''Issue #702:''' Add another institution_id to course table and The Create Course page needs to be fixed to tell the creator to specify the institution (course/_course.html.erb).
* '''WI-2:''' Delete the method add_default_microtask after confirming that there is no reference to the method.
* '''Issue #316:''' Remove "Actions" column on signup sheet in a completed assignment (sign_up_sheet/list.html.erb).
* '''WI-3:''' The view_publishing_rights method brings up a webpage to the instructor on whether the course staff has been granted or denied the right to use each submission in the future. The current implementation of the project works only for assignments that have topics assigned to them. This needs to be enhanced to include assignments that do not have associated topics as well. Another change that needs to be brought about is that the view_publishing_rights needs to be refactored and moved to the participants controller.  
* '''Issue #295:''' Add a confirmation on deleting an assignment on the admin screen (tree_display/actions/_shared_actions.html.rb).  
* '''WI-4:''' The methods slotAvailable? implemented in the sign_up_sheet_controller.rb needs to be removed from the controller. The methods should only exist in the corresponding model.
* '''Issue #256:''' Add a one-time notification at the top of the page which links to an article on the details of the change (it might make sense to put a javascript module in the site_controllers/index.html.rb and let other pages call this).
* '''WI-5:''' Method other_confirmed_topic_for_user is only referenced by the method waitlist_teams. The method wait_list_teams is not referenced anywhere. These methods need to be removed if it is confirmed that they are not used anywhere else.
* '''WI-6:''' Expertiza provides a functionality to import topics from a file (.csv or .txt). This feature is currently not tested. Test the functionality of this feature and fix it if broken. Another change that needs to be brought about is that the current implementation requires that each row of the imported file needs to have four columns. However the fourth column which represents "category" is not mandatory and it should be possible to import a document which does not include values for this column.
* '''WI-7:''' Add Rspec testcases to test the changes implemented above.


== Files Modified in this Project ==
== Files Modified in this Project ==
The following files were modified for this project.
The following files were modified for this project.
* app/controllers/participants_controller.rb
* app/controllers/sign_up_sheet_controller.rb
* app/models/sign_up_topic.rb
* app/models/signed_up_team.rb
* app/models/team.rb
* app/models/waitlist.rb
* app/views/participants/view_publishing_rights.html.erb
* app/views/sign_up_sheet/list.html.erb
* app/views/sign_up_sheet/show_team.html.erb
* app/views/sign_up_sheet/view_publishing_rights.html.erb
* app/views/tree_display/actions/_assignments_actions.html.erb
* app/helpers/import_topics_helper.rb
* app/assets/javascripts/tree_display.jsx
* app/assets/javascripts/tree_display.jsx
* config/routes.rb
* app/controllers/auth_controller.rb
* spec/features/instructor_interface_spec.rb
* app/controllers/course_controller.rb
* spec/features/team_creation_spec.rb
* app/controllers/tree_display_controller.rb
* app/models/assignment_node.rb
* app/models/course.rb
* app/models/course_node.rb
* app/models/institution.rb
* app/views/course/_course.html.erb
* app/views/layouts/application.html.erb
* app/views/notifications/index.html.erb
* app/views/shared/_flash_notifications.html.erb
* app/view/sign_up_sheet/_table_line.html.erb
* app/views/tree_display/list.html.erb
* app/views/tree_display/confirm.html.erb
* app/views/tree_display/new.html.erb
* app/views/tree_display/edit.html.erb
 
* database table "notification"
 
== Solutions implemented and Delivered ==
== Solutions implemented and Delivered ==
=== WI-1: ===
=== Issue #702: ===
For this work item the following files were modified:
This task was pretty involved and called for integrating an institution_id into the course table and updating the Course pages to specify the institution.  We modified several parts of the program in order to accomplish this task.
'''''app/controllers/sign_up_sheet_controller.rb:'''''  
 
In this file the method ad_info is defined. This method was defined using sql query to provide its results. To change this method in such a way that it uses active record associations changes needed to be made to the function as shown below. Additionally since the new implementation returns a list of hashes the way in which this result is accessed in show_team method was also changed to accommodate this change.
 
On the controller '''''app/controllers/course_controller''''' we edited the update definition and the create definition to store the institution_id into the course table.


'''''Original Code:'''''
   def update
   def ad_info(assignment_id, topic_id)
     @course = Course.find(params[:id])
     query = "select t.id as team_id,t.comments_for_advertisement,t.name,su.assignment_id, t.advertise_for_partner from teams t, signed_up_teams s,sign_up_topics su "+
    if params[:course][:directory_path] and @course.directory_path != params[:course][:directory_path]
         "where s.topic_id='"+topic_id.to_s+"' and s.team_id=t.id and s.topic_id = su.id;    "
      begin
     SignUpTopic.find_by_sql(query)
        FileHelper.delete_directory(@course)
      rescue
        flash[:error] = $ERROR_INFO
      end
      begin
        FileHelper.create_directory_from_path(params[:course][:directory_path])
      rescue
         flash[:error] = $ERROR_INFO
      end
    end
    @course.name = params[:course][:name]
    '''@course.institutions_id = params[:course][:institutions_id]'''
    @course.directory_path = params[:course][:directory_path]
    @course.info = params[:course][:info]
    @course.save
    undo_link("The course \"#{@course.name}\" has been updated successfully.")
    redirect_to controller: 'tree_display', action: 'list'
  end
  def create
     @course = Course.new(name: params[:course][:name], '''institutions_id: params[:course][:institutions_id],''' directory_path: params[:course][:directory_path], info: params[:course][:info], private: params[:course][:private])
   end
   end


'''''Modified Code:'''''
 
   def ad_info(assignment_id, topic_id)
On the model '''''app/models/course.rb''''' we created a relationship where course belongs to institution.  This is to allow the linkage of the two tables and allow the drop down to work in the view.
    # List that contains individual result object
 
    @result_list = []
   belongs_to :institution, class_name: 'Institution', foreign_key: 'institutions_id'
    # Get the results
 
    @results = SignedUpTeam.where("topic_id = ?", "#{topic_id}")
 
    # Iterate through the results of the query and get the required attributes
On the model '''''app/models/institution.rb''''' we also created the relationship where institution has many courses.  This is also to allow linkage of the two tables for the drop down to work in the view.
    @results.each do |result|
 
      team = result.team
  class Institution < ActiveRecord::Base
      topic = result.topic
    attr_accessible :name
      resultMap = {}
    has_many :courses
      resultMap[:team_id] = team.id
    validates_length_of :name, minimum: 1
      resultMap[:comments_for_advertisement] = team.comments_for_advertisement
     validates_uniqueness_of :name
      resultMap[:name] = team.name
      resultMap[:assignment_id] = topic.assignment_id
      resultMap[:advertise_for_partner] = team.advertise_for_partner
      # Append to the list
      @result_list.append(resultMap)
     end
    @result_list
   end
   end


Additionally changes were also required in the models signed_up_team.rb and team.rb. This was because these associations were required to fetch required data using active record associations. The following changes were made to the files:


'''''app/models/signed_up_team.rb:'''''
On the view '''''app/views/course/_course.html.erb''''' we added the collection box to display the linked name from the institution table to the institution_id in the course table.  This completed the ability to select and store the institution_id into the database.
   class SignedUpTeam < ActiveRecord::Base
<pre>
    belongs_to :topic, :class_name => 'SignUpTopic'
   <p><label for="institution_name">Institution Name</label><br/>
    belongs_to :team, :class_name => 'Team' #Added this association between SignedUpTeam and Team.
  <%= select("course", "institutions_id", Institution.all.collect{ |c| [ c.name, c.id] }) %></p>
</pre>


'''''app/models/team.rb'''''
  class Team < ActiveRecord::Base
    has_many :teams_users, :dependent => :destroy
    has_many :users, :through => :teams_users
    has_many :join_team_requests
    has_one :team_node,:foreign_key => :node_object_id,:dependent => :destroy
    has_many :bids, :dependent => :destroy
    has_many :signed_up_teams #Added this association between Team and SignedUpTeam.


=== WI-2: ===
In order to properly display the institution_id associated with the courses on the view '''''app/views/tree_display/list.html.erb''''' it was necessary to understand the Factory Method design pattern. A subclass of '''''app/models/node.rb''''' is '''''app/models/course_node.rb''''' and '''''app/models/assigment_node.rb'''''.  Both need similar functions to be used by '''''app/assets/javascripts/tree_display.jsx''''' for displaying rows of courses and assignments. This was added to both, but only used by '''''app/models/course_node.rb''''':
For this work item the file ''app/controllers/sign_up_sheet_controller.rb'' was modified. After performing a code-analysis it was concluded that the method ''add_default_microtask'' was not referenced from any other part of the project. Hence it was safely removed.


=== WI-3: ===
   def get_institution_id
The first part of this task is it enable the ''view_publishing_rights'' view to all the assignments. When logged in as an instructor, (s)he can go to the list of assignments by navigating through ''Manage > Assignments''. Here, only the assignments which have topics associated with them have the icon to go to the ''view_publishing_rights'' page. This icon should be available to all the assignments. This view of the assignments/courses is known as the ''tree_display''. The part of the code that made sure that only the assignments with topics have this icon is in ''app/assets/javascripts/tree_display.jsx''.
     # Course.find(self.node_object_id).course_id
   if (this.props.is_available) {
    @course = Course.find(self.node_object_id) unless @course
     ...
     @course.institutions_id
    if (this. if (this.props.has_topic) {
   end
      moreContent.push(
        <span>
          <a title="View publishing rights" href={"/sign_up_sheet/view_publishing_rights?id="+(parseInt(this.props.id)/2).toString()}>
            <img src="/assets/tree_view/view-publish-rights-24.png" />
          </a>
        </span>
      )
     }
   }


Here we see that the code that adds the icon to the content if two conditions are met. To get the icon for all the assignments, the change to made is pretty simple. Move the code outside of the two ''if'' conditions.
  if (this.props.is_available) {
    ...
    if (this. if (this.props.has_topic) {
      // Move it outside these ifs
    }
  }
  moreContent.push(
    <span>
      <a title="View publishing rights" href={"/sign_up_sheet/view_publishing_rights?id="+(parseInt(this.props.id)/2).toString()}>
        <img src="/assets/tree_view/view-publish-rights-24.png" />
      </a>
    </span>
  )


All the business logic that will be used to display content in the ''view_publishing_rights.html.erb'' is in the ''sign_up_sheet_controller.rb''. Logically speaking, this should be in the ''participants_controller.rb'', since we are taking about the publishing rights provided by the ''participants'' of the assignment. The next part of the tasks involves moving the ''view_publishing_rights'' method to the ''participants_controller.rb''.  
'''''app/controllers/tree_display_controller.rb''''' was modified to include a local variable for used by the view in displaying the institution_id.


'''''sign_up_sheet_controller.rb'''''
   def update_tmp_obj(tmp_object, node)
   def view_publishing_rights
     tmp={
     load_add_signup_topics(params[:id])
      "directory" => node.get_directory,
      "creation_date" => node.get_creation_date,
      "updated_date" => node.get_modified_date,
      "institution" => Institution.where(:id => node.get_institution_id),
      "private" => node.get_instructor_id == session[:user].id ? true : false
      }
  tmp_object.merge!(tmp)
   end
   end
'''''app/assets/javascript/tree_display.jsx''''' was also modified to show different rows between courses and assignments, since only courses were required to show the institution_id.  This was added in order to show the institution_id which is displayed on '''''app/views/tree_display/list.html.erb''''':
  jQuery.each(this.props.data, function (i, entry) {
    if (((entry.name && entry.name.indexOf(_this.props.filterText) !== -1) ||
      (entry.directory && entry.directory.indexOf(_this.props.filterText) !== -1) ||
      (entry.creation_date && entry.creation_date.indexOf(_this.props.filterText) !== -1) ||
      (entry.instructor && entry.instructor.indexOf(_this.props.filterText) !== -1) ||
      '''(entry.institution && entry.institution.indexOf(_this.props.filterText) !== -1) ||'''
      (entry.updated_date && entry.updated_date.indexOf(_this.props.filterText) !== -1)) &&
      (entry.private == true || entry.type == 'FolderNode')) {
      _rows.push(<ContentTableRow
        key={entry.type+'_'+(parseInt(entry.nodeinfo.id)*2).toString()+'_'+i}
        id={entry.type+'_'+(parseInt(entry.nodeinfo.node_object_id)*2).toString()+'_'+i}
        name={entry.name}
        directory={entry.directory}
        instructor={entry.instructor}
        '''institution={entry.institution}'''
        creation_date={entry.creation_date}
        updated_date={entry.updated_date}
        actions={entry.actions}
        is_available={entry.is_available}
        course_id={entry.course_id}
        max_team_size={entry.max_team_size}
        is_intelligent={entry.is_intelligent}
        require_quiz={entry.require_quiz}
        dataType={_this.props.dataType}
        //this is just a hack. All current users courses are marked as private during fetch for display purpose.
        private={entry.private}
        allow_suggestions={entry.allow_suggestions}
        has_topic={entry.has_topic}
        rowClicked={_this.handleExpandClick}
        newParams={entry.newParams}
      />)
<br>
=== Issue #316: ===
This task called for removing the 'Actions' column on the signup sheet for assignments that have been completed (sign_up_sheet/list.html.erb).
(sign_up_sheet/list.html.erb) renders '''app/view/sign_up_sheet/_table_line.html.erb''' and an if statement is used to not show finished assignements.
  <% if @assignment.current_stage_name(@topic_id) != 'Finished' %>
    <td align="center"><%= render :partial => '/sign_up_sheet/actions', :locals => {:i=>i,:topic=>topic, :is_suggested_topic=>is_suggested_topic ||= false} %></td>
  <% end %>
<br>
=== Issue #295: ===
This task called for adding a confirmation when deleting an assignment on the admin screen. While the instructions indicated (app/views/tree_display/actions/_shared_actions.html.rb), this file is no longer in use in the project.  In fact, all assignments and courses on (app/views/tree_display/list.html.erb) are displayed by (app/assets/javascripts/tree_display.jsx) and handled in JavaScript.  Moreover, the normal confirmation actions are overridden by (app/assets/javascript/userDeleteConfirmBox.js).  The overridden confirmation actions were not compatible with the JavaScript deletion link.  We overcame this issue by creating a new view (app/views/tree_display/confirm.html.erb), and redirecting the deletion link to the view for confirmation.  We passed the nodeType (assignment or course) and id of the node to be deleted in the URL to be stored by the controller as local variables for use on the confirmation view.  The selected assignment (or course) is deleted only after confirmed as per the requirement.
The part of the code that redirected deletions for assignments or courses to the confirmation page is in ''''''app/assets/javascripts/tree_display.jsx''''''. (Item in bold was modified)
    
    
   def load_add_signup_topics(assignment_id)
   if (this.props.is_available || newNodeType == 'questionnaires') {
     @id = assignment_id
     moreContent.push(
    @sign_up_topics = SignUpTopic.where( ['assignment_id = ?', assignment_id])
      <span>
    @slots_filled = SignUpTopic.find_slots_filled(assignment_id)
        <a title="Edit" href={"/"+newNodeType+"/"+(parseInt(this.props.id)/2).toString()+"/edit"}><img src="/assets/tree_view/edit-icon-24.png" /></a>
     @slots_waitlisted = SignUpTopic.find_slots_waitlisted(assignment_id)
        '''<a title="Delete" href={"/tree_display/confirm?id="+(parseInt(this.props.id)/2).toString()+"&nodeType="+newNodeType}><img src="/assets/tree_view/delete-icon-24.png" /></a>'''
    
        <a title={this.props.private? "Make public" : "Make private"} href={"/"+newNodeType+"/toggle_access?id="+(parseInt(this.props.id)/2).toString()}><img src={"/assets/tree_view/lock-"+(this.props.private? "off-" : "")+"disabled-icon-24.png"} /></a>
    @assignment = Assignment.find(assignment_id)
      </span>
    #ACS Removed the if condition (and corresponding else) which differentiate assignments as team and individual assignments
     )
    # to treat all assignments as team assignments
  }
     #Though called participants, @participants are actually records in signed_up_teams table, which
 
    #is a mapping table between teams and topics (waitlisted recored are also counted)
 
     @participants = SignedUpTeam.find_team_participants(assignment_id)
'''''Original code''''' (Delete link without confirmation.)
 
   <a title="Delete" href={"/"+newNodeType+"/delete?id="+(parseInt(this.props.id)/2).toString()}><img src="/assets/tree_view/delete-icon-24.png" /></a>
 
 
Definition added to ''''''app/controllers/tree_display_controller.rb'''''' for business logic.
 
  def confirm
     @id = params[:id]
     @nodeType = params[:nodeType]
   end
   end


''view_publishing_rights'' then calls the ''load_add_signup_topics'' method. In that, a lot of attributes are set to be used in the view. But in reality, the view just made use of the ''@sign_up_topics'' and ''@assignment''. But the ''load_add_signup_topics'' is used in multiple places within this controller, and to cater for the needs of all those views, a lot of extra attributes are set. In other words, the method is doing more than one thing. Also, the two relevant attributes to be used in the ''view_publishing_rights'' views are not set up ideally. Only those assignments that have topics can be shown in the view. This should be changed so that all assignments should show up, because a ''participant'' can be in any type of assignment, and this view is for viewing the publishing rights that the participant has provided. So, by using ''@sign_up_topics", the scope is limited to just the assignments with topics in them.
So, apart from moving the method to the ''participant_controller.rb'', the following code was added so that the participants of any type of assignment can be viewed.


'''''Original code''''' (what was in place and moved, but did not work as expected)
Here we see that the code inside of the new view ''''''app/views/tree_display/confirm.html.erb''''''.
   def view_publishing_rights
<pre>
    @sign_up_topics = SignUpTopic.where( ['assignment_id = ?', assignment_id])
   <% if @nodeType == 'course' %>
     @assignment = Assignment.find(assignment_id)
    <h1>Are you sure you want to delete this course?</h1>
   end
  <% else %>
'''''Modified code'''''
     <h1>Are you sure you want to delete this assignment?</h1>
  def view_publishing_rights
   <% end %>
    # Get the assignment ID from the params
 
     assignment_id = params[:id]
  <table align="center">
    
    <tr align="center"><tr></tr><tr></tr>
    # Get the assignment object for the above ID and set the @assignment_name object for the view
      <td align="center" width="120px" style="font-size: xx-large"><%= link_to 'NO', list_tree_display_index_url %></td>
    assignment = Assignment.find(assignment_id)
      <td align="center" width="120px" style="font-size: xx-large">
    @assignment_name = assignment.name
        <% if @nodeType == 'course' %>
 
          <%= link_to 'YES', :controller => :course, :action => :delete, :id => @id %>
    # Initially set to false, will be true if the assignment has any topics
        <% else %>
    @has_topics = false
          <%= link_to 'YES', :controller => :assignments, :action => :delete, :id => @id %>
 
        <% end %>
    # Attribute that contains the list of the teams and their info related to this assignment
      </td>
    @teams_info = []
     </tr>
 
   </table>
    # Get all the teams that work on the assignment with ID assignment_id
</pre>
    teams = Team.find_by_sql(["select * from teams where parent_id = ?", assignment_id])
 
 
<br>
    # For each of the teams, do
 
    teams.each do |team|
=== Issue #256: ===
      team_info = {}
This task called for adding a one-time notification at the top of the page to show changes/notifications.  It necessitated adding a table to the database called notifications.  Therefore, when conducting peer reviews, a migration must be performed to add the new table.  Then we used scaffolding to add notifications (controller, model, and views).  Next we added a link on the view (app/view/tree_display/index.html.erb) to go to the view (app/view/notification/index.html.erb) in order to manage notifications.
      # Set the team name
 
      team_info[:name] = team.name
 
      # List that hold the details of the users in the team
A migration to create a '''notifications''' table was added.
       users = []
 
      # For each of the users, do
<pre>
      team.users.each do |team_user|     
  class CreateNotifications < ActiveRecord::Migration
         # Append the user info to the users list
    def change
         users.append(get_user_info(team_user, assignment))
       create_table :notifications do |t|
        t.string :subject
        t.text :description
        t.date :expiration_date
         t.boolean :active_flag
         t.timestamps null: false
       end
       end
      # Append the users list to the team_info object
      team_info[:users] = users 
 
      # Get the signup topics for the assignment
      @has_topics = get_signup_topics_for_assignment(assignment_id, team_info, team.id) 
 
      # Choose only those teams that have signed up for topics
      team_without_topic = !SignedUpTeam.where(["team_id = ?", team.id]).any?
      if @has_topics && team_without_topic
        next
      end
     
      # Append the hashmap to the list of hashmaps
      @teams_info.append(team_info)
     end
     end
    @teams_info = @teams_info.sort_by {|hashmap| [hashmap[:topic_id] ? 0 :1,hashmap[:topic_id] || 0 ] }
 
   end
   end
</pre>


The final part of this task was to move the corresponding view to the ''views/participants''
Using scaffolding, a new index was added '''app/view/notifications/index.html.erb'''.


'''''views/sign_up_sheet/view_publishing_rights.html.erb'''''
<pre>
<pre>
   <h1>Publishing rights for <%= @assignment.name %> assignment</h1>
   <h1>Listing Notifications</h1>
 
 
   <% if flash[:notice] %>
   <table>
    <div class="flash_note"><%= flash[:notice] %></div>
     <thead>
  <% end %>
 
  <% if @sign_up_topics.any? %>
     <table class="general">
       <tr>
       <tr>
         <th width="5%" rowspan="2">Topic #</th>
         <th>Subject</th>
         <th width="40" rowspan="2">Topic name(s)</th>
        <th>Description</th>
         <th width="20%" rowspan="2">Team name</th>
         <th>Expiration date</th>
         <th width="35%" colspan="4">Participant</th>
         <th>Active flag</th>
         <th colspan="3"></th>
       </tr>
       </tr>
      <tr>
    </thead>
        <th>Name</th>
 
        <th>Fullname</th>
    <tbody>
        <th>Publish Rights</th>
       <% @notifications.each do |notification| %>
        <th>Verified</th>
      </tr>
 
       <% @sign_up_topics.each_with_index do |topic, i| %>
         <tr>
         <tr>
           <td><%= topic.topic_identifier %></td>
           <td><%= notification.subject %></td>
           <td><%= render :partial => 'topic_names', :locals => {:i => i, :topic => topic} %></td>
           <td><%= notification.description %></td>
           <th colspan=<%= (@assignment.team_assignment? ? "5" : "4") %>>&nbsp;</th>
          <td><%= notification.expiration_date %></td>
          <td><%= notification.active_flag %></td>
          <td><%= link_to 'Show', notification %></td>
          <td><%= link_to 'Edit', edit_notification_path(notification) %></td>
           <td><%= link_to 'Destroy', notification, method: :delete, :confirm => 'Are you sure?' %></td>
         </tr>
         </tr>
 
        <% if (@assignment.participants.size > 0)
            for signed_up_team in topic.signed_up_teams
              #ACS Find the user(s) signed up for a topic
              # removed code that handles team and individual assignments differently
              # An unknown bug causes teams to disappear. Don't crash if this happens
              team = Team.find(signed_up_team.team_id)
              users = []
              users = team.users if team
              showed_team = false
              for user in users
            %>
          <tr>
            <th>&nbsp;</th>
            <th>&nbsp;</th>
            <%  if !showed_team
          %>
        <td align="center" rowspan="<%= team.users.size %>"><b><%= team.name %></b></td>
      <% end %>
      <% showed_team = true %>
      <td><%=user.name%></td>
      <td><%=user.fullname%></td>
      <%
          permissionGranted = false
      hasSignature = false
      signatureValid = false
      @assignment.participants.each do |participant|
        if (user.id == participant.user.id)
          permissionGranted = participant.permission_granted?
        end
      end
    %>
  <td><%= (permissionGranted ? "granted" : "denied") %></td>
  <td>
    <% if (permissionGranted && hasSignature && signatureValid) %>
      <img src="/assets/Check-icon.png" title="verified with digitigal signature" alt="verified"/>
    <% else %>
      <img src="/assets/delete_icon.png" title="no digitigal signature" alt="unverified"/>
    <% end %>
    &nbsp;
  </td>
            </tr>
          <% end %>
        <% end %>
       <% end %>
       <% end %>
     <% end %>
     </tbody>
   </table>
   </table>
  <% end %>
  </pre>
The above view had a lot of controller related logic in it. Since it is not the best of practices to have such logic in the ''view'' code, the changes shown above in the ''participants_controller.rb'' had to be made, so that the ''view'' code is just responsible for ''displaying'' the information rather than computing it as well.


'''''views/participants/view_publishing_rights.html.erb'''''
   <br>
  <pre>
 
   <h1>Publishing rights for <%= @assignment_name %> assignment</h1>
   <%= link_to 'New Notification', new_notification_path %>
 
</pre>
   <% if flash[:notice] %>
 
      <div class="flash_note"><%= flash[:notice] %></div>
 
  <% end %>
Added link on page for managers to manage notifications '''app/view/tree_display/list.html.erb'''.
 
 
  <table class="general">
[[File:256.png]]
      <tr>
 
          <% if @has_topics %>
 
              <th width="5%" rowspan="2">Topic #</th>
Notifications are added and modified in similar views '''app/view/tree_display/new.html.erb''' or '''app/view/tree_display/edit.html.erb'''.
              <th width="40" rowspan="2">Topic name(s)</th>
[[File:Notifications.png]]
          <% end %>
 
          <th width="20%" rowspan="2">Team name</th>
 
          <th width="35%" colspan="4">Participant</th>
When any type of user successfully logs in, flash notifications called from the controller '''app/controllers/auth_controller.rb'''.
      </tr>
<pre>
      <tr>
  def after_login(user)
          <th>Name</th>
    session[:user] = user
          <th>Fullname</th>
    AuthController.set_current_role(user.role_id, session)
          <th>Publish Rights</th>
          <th>Verified</th>
      </tr>
 
      <% @teams_info.each do |team_info| %>
          <%
              users = team_info[:users]
              num_students = users.size
          %>       
          <tr>
              <% if @has_topics %>
                  <td rowspan="<%= num_students %>"><%= team_info[:topic_id] %> </td>
                  <td rowspan="<%= num_students %>"><%= team_info[:topic_name] %></td>
              <% end %>
              <td align="center" rowspan="<%= num_students %>">
                  <b><%= team_info[:name] %></b>
              </td>
             
              <% users.each do |user| %>
                  <td> <%=user[:name]%> </td>
                  <td> <%=user[:fullname]%> </td>
                  <td> <%=user[:pub_rights]%> </td>
                  <td>
                      <% if user[:verfied] %>
                          <img src="/assets/Check-icon.png" title="verified with digitigal signature" alt="verified"/>
                      <% else %>
                          <img src="/assets/delete_icon.png" title="no digitigal signature" alt="unverified"/>
                      <% end %>
                  </td> </tr><tr>
              <% end %>
 
          </tr>
 
      <% end %>
  </table>
  </pre>
=== WI-4: ===
For this work-item the slotAvailable? method was removed from the file ''app/controllers/sign_up_sheet_controller.rb''. This method is already implemented in the model ''app/models/sign_up_topic.rb''.  


=== WI-5: ===
    flash[:notification] = 'This will display notifications on login'
For this work item the file ''app/controllers/sign_up_sheet_controller.rb'' was modified. After performing a code-analysis it was concluded that the method ''other_confirmed_topic_for_user'' was not referenced from any other part of the project. Hence it was safely removed.


=== WI-6: ===
    redirect_to controller: AuthHelper.get_home_controller(session[:user]),
For this work-item the files that were modified included ''app/helpers/import_topics_helper.rb''. In this file one thing that has been changed is the variable used to represent the row array. This was done in order to remove any confusion. The array represents the individual columns of a particular row. Additionally since the category column is an optional column a check has been added to verify its existence before assigning it to the attributes hash.
                action: AuthHelper.get_home_action(session[:user])
'''''Original Code:'''''
  def self.define_attributes(row)
    attributes = {}
    attributes["topic_identifier"] = row[0].strip
    attributes["topic_name"] = row[1].strip
    attributes["max_choosers"] = row[2]
    attributes["category"] = row[3].strip
    attributes
   end
   end
</pre>
Flash notifications are displayed once at login due to a line in the view '''app/view/layouts/application.html.erb''' which is used by all other views.
<%= render 'shared/flash_notifications' %>
Each individual flash notification that is active and is not expired is displayed through the use of the view '''app/view/shared/_flash_notifications.html.erb'''.


'''''Modified Code:'''''
<pre>
   def self.define_attributes(columns)
   <% if  (flash_message :notification) != nil %>
     attributes = {}
     <% @notifications = Notification.where(:active_flag => true) %>
     attributes["topic_identifier"] = columns[0].strip
     <% if !@notifications.nil? %>
    attributes["topic_name"] = columns[1].strip
      <% @notifications.each do |notification| %>
    attributes["max_choosers"] = columns[2].strip
        <% if notification.expiration_date >= Date.today %>
    if columns.length > 3
          <table style="background-color: #faebcc">
      attributes["category"] = columns[3].strip
            <tbody style="background-color: #faebcc"><br>
    end
              <tr><p style="color:black; font-width:bold; background-color: #faebcc"><%= notification.subject.upcase %></p></tr>
     attributes
              <tr><%= notification.description %></tr>
   end
            </tbody>
          </table>
        <% end %>
      <% end %>
     <% end %>
   <% end %>
</pre>


Another file that was modified for this work-item is ''app/models/sign_up_topic.rb''. This was also modified to remove the ambiguity caused by the variable name ''row''. This was changed to ''columns''. Additionally the original check which verifies whether the number of columns in each row is 4 was modified so that it also works if the imported file has only 3 columns.


'''''Original Code:'''''
The notifications are displayed on whatever page the login screen goes to first depending of the type of user logging into the system. They are only display once and are gone whenever the page is refreshed or changed.
  def self.import(row,session,id = nil)
    if row.length != 4
      raise ArgumentError, "CSV File expects the format: Topic identifier, Topic name, Max choosers, Topic Category"
    end
    topic = SignUpTopic.where(topic_name: row[1], assignment_id: session[:assignment_id]).first
    if topic == nil
      attributes = ImportTopicsHelper::define_attributes(row)
      ImportTopicsHelper::create_new_sign_up_topic(attributes,session)
    else
      topic.max_choosers = row[2]
      topic.topic_identifier = row[0]
      #topic.assignment_id = session[:assignment_id]
      topic.save
    end
  end
'''''Modified Code:'''''
  def self.import(columns,session,id = nil)
    if columns.length < 3
      raise ArgumentError, "CSV File expects the format: Topic identifier, Topic name, Max choosers, Topic Category (optional)"
    end
    topic = SignUpTopic.where(topic_name: columns[1], assignment_id: session[:assignment_id]).first
    if topic == nil
      attributes = ImportTopicsHelper::define_attributes(columns)
      ImportTopicsHelper::create_new_sign_up_topic(attributes,session)
    else
      topic.max_choosers = columns[2]
      topic.topic_identifier = columns[0]
      #topic.assignment_id = session[:assignment_id]
      topic.save
    end
  end


=== WI-7: ===
[[File:Notifications_view.png]]
We have added Rspec test cases to check functionality of import feature and view_publishing_rights view. These Rspec tests are present in "spec/features/instructor_interface_spec.rb". The tests use csv files that are present in "spec/features/assignment_topic_csvs/" folder.
'''View Publishing Rights''': This test case checks whether view_publishing_rights page has column headers "Topic name(s)" and "Topic #". Since an assignment created without a topic does not have topic name and topic id. The test case will fail if the page contains topic name and topic id.
  describe "View Publishing Rights" do
    it 'should display teams for assignment without topic' do
      login_as("instructor6")
      visit '/participants/view_publishing_rights?id=1'
      expect(page).to have_content('Team name')
      expect(page).should_not have_content('Topic name(s)')
      expect(page).should_not have_content('Topic #')
    end
  end


'''Import tests for assignment topics''': There are 4 tests written under this category.They check the topics import feature. The file which is to be uploaded should contain 3 compulsory fields and the fourth field is optional. The tests perform the following tasks:
<br>
'''Check the import pass when the import file has 3 columns.'''
  describe "Import tests for assignment topics" do
    it 'should be valid file with 3 columns' do
      login_as("instructor6")
      visit '/assignments/1/edit'
      click_link "Topics"
      click_link "Import topics"
      file_path=Rails.root+"spec/features/assignment_topic_csvs/3-col-valid_topics_import.csv"
      attach_file('file',file_path)
      click_button "Import"
      click_link "Topics"
      expect(page).to have_content('expertiza')
      expect(page).to have_content('mozilla')
    end
   
'''Check the import pass when the import file has 3 or 4 columns.'''
    it 'should be a valid file with 3 or more columns' do
      login_as("instructor6")
      visit '/assignments/1/edit'
      click_link "Topics"
      click_link "Import topics"
      file_path=Rails.root+"spec/features/assignment_topic_csvs/3or4-col-valid_topics_import.csv"
      attach_file('file',file_path)
      click_button "Import"
      click_link "Topics"
      expect(page).to have_content('capybara')
      expect(page).to have_content('cucumber')
    end
'''Check the import fail when the import file has 2 columns.'''
  it 'should be a invalid csv file' do
      login_as("instructor6")
      visit '/assignments/1/edit'
      click_link "Topics"
      click_link "Import topics"
      file_path=Rails.root+"spec/features/assignment_topic_csvs/invalid_topics_import.csv"
      attach_file('file',file_path)
      click_button "Import"
      click_link "Topics"
      expect(page).should_not have_content('airtable')
      expect(page).should_not have_content('devise')
    end
'''Check the import fail when the import file doesn't have valid data.'''
  it 'should be an random text file' do
      login_as("instructor6")
      visit '/assignments/1/edit'
      click_link "Topics"
      click_link "Import topics"
      file_path=Rails.root+"spec/features/assignment_topic_csvs/random.txt"
      attach_file('file',file_path)
      click_button "Import"
      click_link "Topics"
      expect(page).should_not have_content('this is a random file which should fail')
    end
  end


== Testing from the UI ==
== Testing Plan:  Testing from the UI ==
=== UI Testing ===
=== UI Testing ===
Since majority of the tasks for this assignment was code refactoring, only a few of these can be seen and tested through the UI. Follow the instructions below to check the:
The majority of the changes can be tested via the UI. Follow the instructions below to check the tasks:
* view_publishing_rights
 
# Login as a instructor (better to log in as an instructor that has assignments in the tree_desiplay view. For eg. instructor6)
* '''Issue #702:''' Add another institution_id to course table and The Create Course page needs to be fixed to tell the creator to specify the institution (course/_course.html.erb).
# Navigate 'Manage > Assignments'
# Login as an instructor.  It will automatically go to the manage courses page.
# Against each assignment in the table, an icon for 'view_publishing_rights' can be seen
# Click the "New Public Course" or "New Private Course" button.
# Click on the 'view_publishing_rights' icon against any assignment
# You will notice a drop down for institutions is available.
# If the assignment has topics (eg. Wikipedia contribution), the table in the view will have 'Topic Name' and 'Topic #' displayed.
# Create a new course, and the UI will automatically return to the list which displays the courses.  You will see the institution listed.
# If the assignment does not have topics, the table will not have the above two columns
# Click on the edit button and you will be able to modify the institution for the course.
 
* '''Issue #316:''' Remove "Actions" column on signup sheet in a completed assignment (sign_up_sheet/list.html.erb).
# Log in as any user or instructor.
# Go to the assignments list.  Note: this is not the manage assignments for instructors.
# Click into a finished assignment.  You will notice the actions column is gone.
# Click into a current assignment.  You will notice the actions column appears.
 
* '''Issue #295:''' Add a confirmation on deleting an assignment on the admin screen (tree_display/actions/_shared_actions.html.rb).
# Login as an instructor.  It will automatically go to the manage courses page.
# Click the Assignments link to switch the view from courses to assignments.
# Add a new assignment.
# Click the delete button for an assignment.  Note: please add an assignment first.  Most of the current assignments have dependencies that prevent them from being deleted... this is not an error.
# The UI will be directed to a page to confirm the deletion.  Select "Yes" if you wish to delete the assignment.


* Topics import feature for an assignment
* '''Issue #256:''' Add a one-time notification at the top of the page which links to an article on the details of the change (it might make sense to put a javascript module in the site_controllers/index.html.rb and let other pages call this).
# In the tree_display view of assignments, click on the edit icon. (Or while creating a new assignment)
# Login as an instructor.  It will automatically go to the manage courses page.
# Click on 'Topics' tab
# Click on the "Manage Notifications" link above the "Courses" and "Assignments" links.  Note: The link was placed here because the system has numerous issues when adding menu items.  It would require us to do more work that the entirety of this project to correct the menu additions in the superuser menu.
# Click on 'Import topics', towards the bottom of the page
# You will be directed to a creation page for notifications. Note: in order for a notification to display the expiration date must be the current date or later, and the active checkbox must be selected.
# Select a valid CSV file. The first, second and third columns should be the topic identifier, topic name and number of slots available, respectively. Note that the topic identifier should be more than 10 characters long, else import will fail. The CSV can have an optional 4th column for 'category' but this is displayed in the UI
# Log out once the notification is created.
# In case of invalid CSV import, an error message will be shown.
# Log in using any account.  The notification will display on the first page the user is shown.  It will disappear when they change or reload the page.  Note: instructors on the management page will retain the notification if they switch between Assignments, Courses, and Questionnaires.  This is because the JavaScript does not actually reload the page.

Latest revision as of 22:25, 31 March 2017

This wiki page is for the description of changes made according to the specification of E1720 OSS assignment for Spring 2016.

Peer Review Information

For testing the changes made, the following credentials are recommended:

  • Instructor Login: username: instructor6 password: password

The above user is suggested for testing because is is unlike to run into issues that are unrelated to our work.

This project does compile. Reviewers will need to perform "rake db:migrate" as we've added a database table to support notifications.

The testing plan is at the end of this document. There were also specs added; however, they are in the Git repository. Design principles were not needed since we mostly modified existing work. However, issue #702 is using the Factory Method design pattern, and our work added content to the existing pattern.

Introduction

Background

Expertiza is a web portal which can be used to manage assignments related to a course. It provides a platform to view assignments, manage teams, select topics and work improvement through anonymous peer reviews. For the instructor it provides complete control to create assignments, view reviews submitted and provide feedback. The instructors also have an option to publish the students work based on the rights provided by the student.

Problem Statement

The following were the tasks identified to be accomplish through this project.

  • Issue #702: Add another institution_id to course table and The Create Course page needs to be fixed to tell the creator to specify the institution (course/_course.html.erb).
  • Issue #316: Remove "Actions" column on signup sheet in a completed assignment (sign_up_sheet/list.html.erb).
  • Issue #295: Add a confirmation on deleting an assignment on the admin screen (tree_display/actions/_shared_actions.html.rb).
  • Issue #256: Add a one-time notification at the top of the page which links to an article on the details of the change (it might make sense to put a javascript module in the site_controllers/index.html.rb and let other pages call this).

Files Modified in this Project

The following files were modified for this project.

  • app/assets/javascripts/tree_display.jsx
  • app/controllers/auth_controller.rb
  • app/controllers/course_controller.rb
  • app/controllers/tree_display_controller.rb
  • app/models/assignment_node.rb
  • app/models/course.rb
  • app/models/course_node.rb
  • app/models/institution.rb
  • app/views/course/_course.html.erb
  • app/views/layouts/application.html.erb
  • app/views/notifications/index.html.erb
  • app/views/shared/_flash_notifications.html.erb
  • app/view/sign_up_sheet/_table_line.html.erb
  • app/views/tree_display/list.html.erb
  • app/views/tree_display/confirm.html.erb
  • app/views/tree_display/new.html.erb
  • app/views/tree_display/edit.html.erb
  • database table "notification"

Solutions implemented and Delivered

Issue #702:

This task was pretty involved and called for integrating an institution_id into the course table and updating the Course pages to specify the institution. We modified several parts of the program in order to accomplish this task.


On the controller app/controllers/course_controller we edited the update definition and the create definition to store the institution_id into the course table.

 def update
   @course = Course.find(params[:id])
   if params[:course][:directory_path] and @course.directory_path != params[:course][:directory_path]
     begin
       FileHelper.delete_directory(@course)
     rescue
       flash[:error] = $ERROR_INFO
     end
     begin
       FileHelper.create_directory_from_path(params[:course][:directory_path])
     rescue
       flash[:error] = $ERROR_INFO
     end
   end
   @course.name = params[:course][:name]
   @course.institutions_id = params[:course][:institutions_id]
   @course.directory_path = params[:course][:directory_path]
   @course.info = params[:course][:info]
   @course.save
   undo_link("The course \"#{@course.name}\" has been updated successfully.")
   redirect_to controller: 'tree_display', action: 'list'
 end
 def create
   @course = Course.new(name: params[:course][:name], institutions_id: params[:course][:institutions_id], directory_path: params[:course][:directory_path], info: params[:course][:info], private: params[:course][:private])
 end


On the model app/models/course.rb we created a relationship where course belongs to institution. This is to allow the linkage of the two tables and allow the drop down to work in the view.

 belongs_to :institution, class_name: 'Institution', foreign_key: 'institutions_id'


On the model app/models/institution.rb we also created the relationship where institution has many courses. This is also to allow linkage of the two tables for the drop down to work in the view.

 class Institution < ActiveRecord::Base
   attr_accessible :name
   has_many :courses
   validates_length_of :name, minimum: 1
   validates_uniqueness_of :name
 end


On the view app/views/course/_course.html.erb we added the collection box to display the linked name from the institution table to the institution_id in the course table. This completed the ability to select and store the institution_id into the database.

  <p><label for="institution_name">Institution Name</label><br/>
  <%= select("course", "institutions_id", Institution.all.collect{ |c| [ c.name, c.id] }) %></p>


In order to properly display the institution_id associated with the courses on the view app/views/tree_display/list.html.erb it was necessary to understand the Factory Method design pattern. A subclass of app/models/node.rb is app/models/course_node.rb and app/models/assigment_node.rb. Both need similar functions to be used by app/assets/javascripts/tree_display.jsx for displaying rows of courses and assignments. This was added to both, but only used by app/models/course_node.rb:

 def get_institution_id
   # Course.find(self.node_object_id).course_id
   @course = Course.find(self.node_object_id) unless @course
   @course.institutions_id
 end


app/controllers/tree_display_controller.rb was modified to include a local variable for used by the view in displaying the institution_id.

 def update_tmp_obj(tmp_object, node)
   tmp={
     "directory" => node.get_directory,
     "creation_date" => node.get_creation_date,
     "updated_date" => node.get_modified_date,
     "institution" => Institution.where(:id => node.get_institution_id),
     "private" => node.get_instructor_id == session[:user].id ? true : false
      }
  tmp_object.merge!(tmp)
 end


app/assets/javascript/tree_display.jsx was also modified to show different rows between courses and assignments, since only courses were required to show the institution_id. This was added in order to show the institution_id which is displayed on app/views/tree_display/list.html.erb:

 jQuery.each(this.props.data, function (i, entry) {
   if (((entry.name && entry.name.indexOf(_this.props.filterText) !== -1) ||
     (entry.directory && entry.directory.indexOf(_this.props.filterText) !== -1) ||
     (entry.creation_date && entry.creation_date.indexOf(_this.props.filterText) !== -1) ||
     (entry.instructor && entry.instructor.indexOf(_this.props.filterText) !== -1) ||
     (entry.institution && entry.institution.indexOf(_this.props.filterText) !== -1) ||
     (entry.updated_date && entry.updated_date.indexOf(_this.props.filterText) !== -1)) &&
     (entry.private == true || entry.type == 'FolderNode')) {
     _rows.push(<ContentTableRow
       key={entry.type+'_'+(parseInt(entry.nodeinfo.id)*2).toString()+'_'+i}
       id={entry.type+'_'+(parseInt(entry.nodeinfo.node_object_id)*2).toString()+'_'+i}
       name={entry.name}
       directory={entry.directory}
       instructor={entry.instructor}
       institution={entry.institution}
       creation_date={entry.creation_date}
       updated_date={entry.updated_date}
       actions={entry.actions}
       is_available={entry.is_available}
       course_id={entry.course_id}
       max_team_size={entry.max_team_size}
       is_intelligent={entry.is_intelligent}
       require_quiz={entry.require_quiz}
       dataType={_this.props.dataType}
       //this is just a hack. All current users courses are marked as private during fetch for display purpose.
       private={entry.private}
       allow_suggestions={entry.allow_suggestions}
       has_topic={entry.has_topic}
       rowClicked={_this.handleExpandClick}
       newParams={entry.newParams}
     />)


Issue #316:

This task called for removing the 'Actions' column on the signup sheet for assignments that have been completed (sign_up_sheet/list.html.erb).


(sign_up_sheet/list.html.erb) renders app/view/sign_up_sheet/_table_line.html.erb and an if statement is used to not show finished assignements.

 <% if @assignment.current_stage_name(@topic_id) != 'Finished' %>

<%= render :partial => '/sign_up_sheet/actions', :locals => {:i=>i,:topic=>topic, :is_suggested_topic=>is_suggested_topic ||= false} %>

 <% end %>


Issue #295:

This task called for adding a confirmation when deleting an assignment on the admin screen. While the instructions indicated (app/views/tree_display/actions/_shared_actions.html.rb), this file is no longer in use in the project. In fact, all assignments and courses on (app/views/tree_display/list.html.erb) are displayed by (app/assets/javascripts/tree_display.jsx) and handled in JavaScript. Moreover, the normal confirmation actions are overridden by (app/assets/javascript/userDeleteConfirmBox.js). The overridden confirmation actions were not compatible with the JavaScript deletion link. We overcame this issue by creating a new view (app/views/tree_display/confirm.html.erb), and redirecting the deletion link to the view for confirmation. We passed the nodeType (assignment or course) and id of the node to be deleted in the URL to be stored by the controller as local variables for use on the confirmation view. The selected assignment (or course) is deleted only after confirmed as per the requirement.

The part of the code that redirected deletions for assignments or courses to the confirmation page is in 'app/assets/javascripts/tree_display.jsx'. (Item in bold was modified)

 if (this.props.is_available || newNodeType == 'questionnaires') {
   moreContent.push(
     
       <a title="Edit" href={"/"+newNodeType+"/"+(parseInt(this.props.id)/2).toString()+"/edit"}><img src="/assets/tree_view/edit-icon-24.png" /></a>
       <a title="Delete" href={"/tree_display/confirm?id="+(parseInt(this.props.id)/2).toString()+"&nodeType="+newNodeType}><img src="/assets/tree_view/delete-icon-24.png" /></a>
       <a title={this.props.private? "Make public" : "Make private"} href={"/"+newNodeType+"/toggle_access?id="+(parseInt(this.props.id)/2).toString()}><img src={"/assets/tree_view/lock-"+(this.props.private? "off-" : "")+"disabled-icon-24.png"} /></a>
     
   )
 }


Original code (Delete link without confirmation.)

 <a title="Delete" href={"/"+newNodeType+"/delete?id="+(parseInt(this.props.id)/2).toString()}><img src="/assets/tree_view/delete-icon-24.png" /></a>


Definition added to 'app/controllers/tree_display_controller.rb' for business logic.

 def confirm
   @id = params[:id]
   @nodeType = params[:nodeType]
 end


Here we see that the code inside of the new view 'app/views/tree_display/confirm.html.erb'.

  <% if @nodeType == 'course' %>
    <h1>Are you sure you want to delete this course?</h1>
  <% else %>
    <h1>Are you sure you want to delete this assignment?</h1>
  <% end %>

  <table align="center">
    <tr align="center"><tr></tr><tr></tr>
      <td align="center" width="120px" style="font-size: xx-large"><%= link_to 'NO', list_tree_display_index_url %></td>
      <td align="center" width="120px" style="font-size: xx-large">
        <% if @nodeType == 'course' %>
          <%= link_to 'YES', :controller => :course, :action => :delete, :id => @id %>
        <% else %>
          <%= link_to 'YES', :controller => :assignments, :action => :delete, :id => @id %>
        <% end %>
      </td>
    </tr>
  </table>


Issue #256:

This task called for adding a one-time notification at the top of the page to show changes/notifications. It necessitated adding a table to the database called notifications. Therefore, when conducting peer reviews, a migration must be performed to add the new table. Then we used scaffolding to add notifications (controller, model, and views). Next we added a link on the view (app/view/tree_display/index.html.erb) to go to the view (app/view/notification/index.html.erb) in order to manage notifications.


A migration to create a notifications table was added.

  class CreateNotifications < ActiveRecord::Migration
    def change
      create_table :notifications do |t|
        t.string :subject
        t.text :description
        t.date :expiration_date
        t.boolean :active_flag
        t.timestamps null: false
      end
    end
  end


Using scaffolding, a new index was added app/view/notifications/index.html.erb.

  <h1>Listing Notifications</h1>

  <table>
    <thead>
      <tr>
        <th>Subject</th>
        <th>Description</th>
        <th>Expiration date</th>
        <th>Active flag</th>
        <th colspan="3"></th>
      </tr>
    </thead>

    <tbody>
      <% @notifications.each do |notification| %>
        <tr>
          <td><%= notification.subject %></td>
          <td><%= notification.description %></td>
          <td><%= notification.expiration_date %></td>
          <td><%= notification.active_flag %></td>
          <td><%= link_to 'Show', notification %></td>
          <td><%= link_to 'Edit', edit_notification_path(notification) %></td>
          <td><%= link_to 'Destroy', notification, method: :delete, :confirm => 'Are you sure?' %></td>
        </tr>
      <% end %>
    </tbody>
  </table>

  <br>

  <%= link_to 'New Notification', new_notification_path %>


Added link on page for managers to manage notifications app/view/tree_display/list.html.erb.



Notifications are added and modified in similar views app/view/tree_display/new.html.erb or app/view/tree_display/edit.html.erb.



When any type of user successfully logs in, flash notifications called from the controller app/controllers/auth_controller.rb.

  def after_login(user)
    session[:user] = user
    AuthController.set_current_role(user.role_id, session)

    flash[:notification] = 'This will display notifications on login'

    redirect_to controller: AuthHelper.get_home_controller(session[:user]),
                action: AuthHelper.get_home_action(session[:user])
  end


Flash notifications are displayed once at login due to a line in the view app/view/layouts/application.html.erb which is used by all other views.

<%= render 'shared/flash_notifications' %>


Each individual flash notification that is active and is not expired is displayed through the use of the view app/view/shared/_flash_notifications.html.erb.

  <% if  (flash_message :notification) != nil %>
    <% @notifications = Notification.where(:active_flag => true) %>
    <% if !@notifications.nil? %>
      <% @notifications.each do |notification| %>
        <% if notification.expiration_date >= Date.today %>
          <table style="background-color: #faebcc">
            <tbody style="background-color: #faebcc"><br>
              <tr><p style="color:black; font-width:bold; background-color: #faebcc"><%= notification.subject.upcase %></p></tr>
              <tr><%= notification.description %></tr>
            </tbody>
          </table>
        <% end %>
      <% end %>
    <% end %>
  <% end %>


The notifications are displayed on whatever page the login screen goes to first depending of the type of user logging into the system. They are only display once and are gone whenever the page is refreshed or changed.


Testing Plan: Testing from the UI

UI Testing

The majority of the changes can be tested via the UI. Follow the instructions below to check the tasks:

  • Issue #702: Add another institution_id to course table and The Create Course page needs to be fixed to tell the creator to specify the institution (course/_course.html.erb).
  1. Login as an instructor. It will automatically go to the manage courses page.
  2. Click the "New Public Course" or "New Private Course" button.
  3. You will notice a drop down for institutions is available.
  4. Create a new course, and the UI will automatically return to the list which displays the courses. You will see the institution listed.
  5. Click on the edit button and you will be able to modify the institution for the course.
  • Issue #316: Remove "Actions" column on signup sheet in a completed assignment (sign_up_sheet/list.html.erb).
  1. Log in as any user or instructor.
  2. Go to the assignments list. Note: this is not the manage assignments for instructors.
  3. Click into a finished assignment. You will notice the actions column is gone.
  4. Click into a current assignment. You will notice the actions column appears.
  • Issue #295: Add a confirmation on deleting an assignment on the admin screen (tree_display/actions/_shared_actions.html.rb).
  1. Login as an instructor. It will automatically go to the manage courses page.
  2. Click the Assignments link to switch the view from courses to assignments.
  3. Add a new assignment.
  4. Click the delete button for an assignment. Note: please add an assignment first. Most of the current assignments have dependencies that prevent them from being deleted... this is not an error.
  5. The UI will be directed to a page to confirm the deletion. Select "Yes" if you wish to delete the assignment.
  • Issue #256: Add a one-time notification at the top of the page which links to an article on the details of the change (it might make sense to put a javascript module in the site_controllers/index.html.rb and let other pages call this).
  1. Login as an instructor. It will automatically go to the manage courses page.
  2. Click on the "Manage Notifications" link above the "Courses" and "Assignments" links. Note: The link was placed here because the system has numerous issues when adding menu items. It would require us to do more work that the entirety of this project to correct the menu additions in the superuser menu.
  3. You will be directed to a creation page for notifications. Note: in order for a notification to display the expiration date must be the current date or later, and the active checkbox must be selected.
  4. Log out once the notification is created.
  5. Log in using any account. The notification will display on the first page the user is shown. It will disappear when they change or reload the page. Note: instructors on the management page will retain the notification if they switch between Assignments, Courses, and Questionnaires. This is because the JavaScript does not actually reload the page.