CSC/ECE 517 Fall 2020 - E2063. Refactor tree-display.js and tree display controller.rb: Difference between revisions

From Expertiza_Wiki
Jump to navigation Jump to search
 
(25 intermediate revisions by the same user not shown)
Line 19: Line 19:
===Accomplishments===
===Accomplishments===
The following tasks were accomplished in this project:
The following tasks were accomplished in this project:
* Removed methods that were not used
* Removed methods that were not used
* Implemented previous changes from past groups on this assignment if useful
* Modified tree display controller to better parse data prior to being accessed by the react client
* Modified tree display controller to better parse data prior to being accessed by the react client  
* Fixed beta branch issue with showing dropdowns for courses and questionnaires.
 
* Fixed beta branch issue with showing edit option for courses and assignments.
 
 


===About Tree Display Controller===
===About Tree Display Controller===
Line 35: Line 31:


=====Functionality=====
=====Functionality=====
* Instructors can view the course, assignment and questionnaire tabs.
* Instructors can view the course, assignment and questionnaire tabs. Instructors can select rows for questionnaire's and courses to view a dropdown of more information. For course rows, they have options to edit, delete, copy, add TA, create an assignment, create teams, view grades, assign surveys, and view reviews. For assignments, they can edit, delete, copy, assign to course, create teams, assign reviewers, view submissions, view scores, view reports, and view survey responses.  
::
::


=====Drawbacks and Solutions=====
* '''Problem 1''': The dropdown doesn't work for assignments and questionaires. This is due to the handleExpandClick function, as it appears that the get_sub_folder_contents method is not capable of returning the data.


=====Drawbacks and Solutions=====
* '''Problem 1''': Checks in tree_display.jsx are too complicated.
::In
<pre>
<pre>
showElement={_this.state.expandedRow.indexOf(entry.type+'_'(parseInt(entry.nodeinfo.node_object_id)*2).toString()+'_'+i)  
if(this.props.dataType!='assignment') {
> -1 ? "" : "none"}
            _this = this;
            jQuery.get('/tree_display/get_sub_folder_contents',
                {
                    reactParams2: newParams
                },
                function (data) {
                    _this.props.data[id.split("_")[2]]['children'] = data;
                    _this.forceUpdate();
                }, 'json');
        }
</pre>
</pre>
* '''Solution''': For now, assign showElement as true.
 
* '''Solution''': Change the the JQuery route to a post because you can't give parameters to a get route and also change this in routes.rb file.  
 
<pre>
<pre>
showElement={true}
jQuery.post('/tree_display/get_sub_folder_contents',
                {
                    reactParams2: newParams
                },
</pre>
</pre>


* '''Problem 2''': The beta branch isn't getting a prop related to the data to be displayed in the dropdown
<pre>               
post :get_sub_folder_contents
</pre>


* '''Problem 2''': Tree display controller needs to better parse the data prior to being access to the react client because currently, there are too many comparisons when checking for the type of tab.
Example:
<pre>
var SimpleTableRow = React.createClass({
    render: function () {
      var creation_date;
      var updated_date;
      var colWidthArray = ["30%", "0%", "0%", "0%", "25%", "25%", "20%"]
      var colDisplayStyle = {
        "display": ""
      }
      if (this.props.dataType === 'questionnaire') {
        colWidthArray = ["30%", "0%", "0%", "0%", "20%", "20%", "30%"]
        colDisplayStyle = {
          "display": "none"
        }
      } else if(this.props.dataType === 'course') {
        colWidthArray = ["20%", "0%", "0%", "20%", "20%", "20%", "20%"]
      }
    ......


<pre>
var FilterableTable = React.createClass({
  ....
}
}
</pre>
* '''Solution''': Centralize the attributes of each tab so that the tree_display doesn't need to keep parsing between the tabs.
* '''Problem 3''': The Assignments and Courses tab isn't showing the edit option for each row. It seems that what's implemented is trying to verify that there was an active user and the active user is null.
<pre>
if (app_variables.currentUserId == null || this.props.instructor_id == app_variables.currentUserId) {
              moreContent.push(
                  <span>
                <a title="Edit" href={"/" + newNodeType + "/" + (parseInt(this.props.id) / 2).toString() + "/edit"}><img
                    src="/assets/tree_view/edit-icon-24.png"/></a>
              </span>
              );
          }
</pre>
</pre>


* '''Solution''':  
* '''Solution''': Remove the active user implementation and replace with a check to make sure the current tab isn't Questionnaire.
<pre>
if(newNodeType != 'questionnaires') { // should only be viewed by either assignments or courses
              moreContent.push(
                  <span>
                <a title="Edit" href={"/" + newNodeType + "/" + (parseInt(this.props.id) / 2).toString() + "/edit"}><img
                    src="/assets/tree_view/edit-icon-24.png"/></a>
              </span>
              );
          }
</pre>


* '''Problem 3''': Tree display controller needs to better parse the data prior to being access to the react client


* '''Solution''':  
* '''Drawback''':The assignments tab did not have any data when we used the reset debugging extension (i.e. the childNodes attribute was null). Thus we assumed there was no data to display underneath each assignment.


* '''Problem 4''': Components in tree-display are passed too far down when only a small amount of it is needed at a time.


* '''Solution''':
-After debugging, we found that the "nodeType" attribute in the :reactParams field of the post request identifies the type of childNodes to be retrieved (i.e. "Courses" or "Questionnaires")
 
-We found that a "FolderNode" value for this attribute equates to a questionnaire.


===New Implementation===
===New Implementation===
*The method paginate_list has been split into 2 methods now.
====Consolidation====
** BuildSearchCriteria – as the name suggests the sole purpose of this method is to build a search criteria based on the input search filters when the current user initiates a search in versions.
 
** paginate_list – this method will call the paginate API.
A lot of the efforts in pushing objects into moreContent could be consolidated into a central format. An example of how its currently implemented for courses is:
:First the search criteria is built, then the criteria is applied to versions in the database to get all versions which matches the criteria and then the retrieved versions are paginated.
 
<pre>
<pre>
  # pagination.
  def paginate_list(versions)
    paginate(versions, VERSIONS_PER_PAGE);
  end


  def BuildSearchCriteria(id, user_id, item_type, event)
if (newNodeType === 'course') {
    # Set up the search criteria
          moreContent.push(
    search_criteria = ''
            <br/>
    search_criteria = search_criteria + add_id_filter_if_valid(id).to_s
          )
    if current_user_role? == 'Super-Administrator'
          if (this.props.is_available) {
      search_criteria = search_criteria + add_user_filter_for_super_admin(user_id).to_s
            moreContent.push(
    end
              <span>
    search_criteria = search_criteria + add_user_filter
                <a title="Add TA" href={"/course/view_teaching_assistants?id="+(parseInt(this.props.id)/2).toString()+"&model=Course"}>
    search_criteria = search_criteria + add_version_type_filter(item_type).to_s
                  <img src="/assets/tree_view/add-ta-24.png" />
    search_criteria = search_criteria + add_event_filter(event).to_s
                </a>
    search_criteria = search_criteria + add_date_time_filter
                <a title="Create assignment" href={"/assignments/new?parent_id="+(parseInt(this.props.id)/2).toString()}>
    search_criteria
                  <img src="/assets/tree_view/add-assignment-24.png" />
  end
                </a>
                <a title="Add participants" href={"/participants/list?id="+(parseInt(this.props.id)/2).toString()+"&model=Course"}>
                  <img src="/assets/tree_view/add-participant-24.png" />
                </a>
                <a title="Create teams" href={"/teams/list?id="+(parseInt(this.props.id)/2).toString()+"&type=Course"}>
                  <img src="/assets/tree_view/create-teams-24.png" />
                </a>
                <a title="View grade summary by student" href={"/assessment360/course_student_grade_summary?course_id="+(parseInt(this.props.id)/2).toString()}>
                  <img src="/assets/tree_view/360-dashboard-24.png" />
                </a>
                <a title="Assign survey" href={"/survey_deployment/new?id="+(parseInt(this.props.id)/2).toString()+"&type=CourseSurveyDeployment"}>
                  <img src="/assets/tree_view/assign-survey-24.png" />
                </a>
                <a title="View aggregated teammate & meta reviews" href={"/assessment360/all_students_all_reviews?course_id="+(parseInt(this.props.id)/2).toString()}>
                  <span style={{"fontSize": "22px", "top": "8px"}} className="glyphicon glyphicon-list-alt"></span>
                </a>
              </span>
            )
          }
</pre>
</pre>
* The string literals and conditions in the method paginate_list were replaced with methods with intuitive names so that the programmer can understand the code more easily. We also removed an empty if clause and a redundant statement.
<pre>
  def add_id_filter_if_valid (id)
    "id = #{id} AND " if id && id.to_i > 0
  end


  def add_user_filter_for_super_admin (user_id)
With a centralized implementation, we consolidated this information into const node_attribute. (See code repository for implementations for questionnaire and assignments, Course only shown for simplicity of demonstrating implementation).
    "whodunnit = #{user_id} AND " if user_id && user_id.to_i > 0
  end


  def add_user_filter
<pre>
    "whodunnit = #{current_user.try(:id)} AND " if current_user.try(:id) && current_user.try(:id).to_i > 0
const node_attributes = {
   end
isCourse(name) {
    return name === 'course' || name === "courses"
   },


  def add_event_filter (event)
course: {
     "event = '#{event}' AND " if event && !(event.eql? 'Any')
    plural: "course",
   end
     actions: [ {
      title: "Add TA",
      href: "/course/view_teaching_assistants?model=Course&id=",
      src: "/assets/tree_view/add-ta-24.png",
    },
    {
      title: "Create assignment",
      href: "/assignments/new?parent_id=",
      src: "/assets/tree_view/add-assignment-24.png",
    },
    {
      title: "Add participants",
      href: "/participants/list?model=Course&id=",
      src: "/assets/tree_view/add-participant-24.png",
    },
    {
      title: "Create teams",
      href: "/teams/list?type=Course&id=",
      src: "/assets/tree_view/create-teams-24.png",
    },
    {
      title: "View grade summary by student",
      href: "/assessment360/course_student_grade_summary?course_id=",
      src: "/assets/tree_view/360-dashboard-24.png",
    },
    {
      title: "Assign survey",
      href: "/survey_deployment/new?type=CourseSurveyDeployment&id=",
      src: "/assets/tree_view/assign-survey-24.png",
    },
    {
      title: "View aggregated teammate & meta reviews",
      href: "/assessment360/all_students_all_reviews?course_id=",
      src: null,
      extra: <span style={{"fontSize": "22px", "top": "8px"}} className="glyphicon glyphicon-list-alt"></span>
    }],
    getActions: function (id) {
    return (node_attributes.course.actions.map((action) =>
        (action.src
          ? <a title={action.title} href={action.href + id}> <img src={action.src} /></a>
          : <a title={action.title} href={action.href + id}> {action.extra} </a>)))
    }
   },


  def add_date_time_filter
</pre>
    "created_at >= '#{time_to_string(params[:start_time])}' AND " +
        "created_at <= '#{time_to_string(params[:end_time])}'"
  end


  def add_version_type_filter (version_type)
With this new implementation to obtain these actions, we just use the getActions function. Additionally now call the is{Tab} to receive the name of the tab.  
    "item_type = '#{version_type}' AND " if version_type && !(version_type.eql? 'Any')
  end
</pre>
* The paginate method has been moved to the helper class Pagination_Helper. This new method can be now reused by the different components like UsersController etc. The method receives two parameters, first the list to paginate and second the number of items to be displayed in a page.


<pre>
<pre>
module PaginationHelper
      if (node_attributes.isCourse(this.props.dataType)) {
 
          moreContent.push(<br/>)
  def paginate (items, number_of_items_per_page)
          moreContent.push(...node_attributes.course.getActions(parseInt(this.props.id)/2))
    items.page(params[:page]).per_page(number_of_items_per_page)
        }
  end
 
end
</pre>
</pre>


===Code improvements===
===Code improvements===
* Introduced a constant VERSIONS_PER_PAGE and assigned the value 25 to it. The pagination algorithm for VersionsController displays at most 25 versions in a page. The existing implementation uses the value 25 straight in the code and there are few problems associated with such an approach.
====Unused Methods/Functions====
** It is not easy to understand what 25 is unless the programmer takes a close look at the code.
Unused Methods and Functions were commented out
** In case if the value 25 is used at more than one places and in future a new requirement comes to show at most 30 versions in a page, all the values will have to be modified. It is not very DRY.
* '''Example 1''':
* The VersionsController was overriding AccessHelper - action_allowed? method to return true in all the cases. This was violating the whole purpose of the method action_allowed?. The purpose of this method is to determine whether the user who is triggering a CRUD operation is allowed to do so. So when the current user invokes a CRUD operation, the action_allowed? method is invoked first and if the method returns true the CRUD operation is triggered or else the user is intimated with a message and gracefully exited. Hence, when the action_allowed? method is overridden to return true always, it results in providing unauthorized access to certain users.
 
<pre>
<pre>
def action_allowed?
  function showIntelligentAssignmentDialog() {
     true
     jQuery( "#intelligent_assignment_dialog" ).dialog({ closeText: "hide", modal: true, resizable: false, width: 500 });
   end
   }
</pre>
</pre>
 
* '''Example 2''':
:With the new implementation the AccessHelper - action_allowed? method has been modified in such a way that unauthorized access is prevented. As per the new algorithm, 'new', 'create', 'edit', 'update' cannot be invoked by any user. These operations can be accessed only by ‘papertrail’ gem. Only an ‘Administrator’ or ‘Super-Administrator’ can call 'destroy_all' method. All the other methods are accessible to ‘Administrator’,  ‘Super-Administrator’, ‘Instructor’, ‘Teaching Assistant’ and ‘Student’.
The assignments tab did not have any data when we used the reset debugging extension (i.e. the childNodes attribute was null). Thus we assumed there was no data to display underneath each assignment
 
<pre>
<pre>
  def action_allowed?
    def children_node_ng
     case params[:action]
     flash[:error] = "Invalid JSON in the TreeList" unless json_valid? params[:reactParams][:child_nodes]
     when 'new', 'create', 'edit', 'update'
     child_nodes = child_nodes_from_params(params[:reactParams][:child_nodes])
     #Modifications can only be done by papertrail
     tmp_res = {}
      return false
     unless child_nodes.blank?
     when 'destroy_all'
       child_nodes.each do |node|
      ['Super-Administrator',
        initialize_fnode_update_children(params, node, tmp_res)
      'Administrator'].include? current_role_name
      end
    else
       #Allow all others
      ['Super-Administrator',
      'Administrator',
      'Instructor',
      'Teaching Assistant',
      'Student'].include? current_role_name
     end
     end
  end
</pre>
</pre>


===Automated Testing using RSPEC===
===Automated Testing using RSPEC===
The current version of expertiza did not have any test for VersionsController. Using the test driven development(TDD) approach, we have added an exhaustive set of RSPEC tests for VersionsController, to test all the modifications we have done to the code of the controller class. The tests use double and stub features of rspec-rails gem, to fake the log in by different users - Administrator, Instructor, Student etc. The tests can be executed "rpec spec" command as shown below.
<pre>
user-expertiza $rspec spec
.
.
.
Finished in 5.39 seconds (files took 25.33 seconds to load)
66 examples, 0 failures


Randomized with seed 19254
.
.
</pre>


===Testing from UI===
===Testing from UI===
Following are a few testcases with respectto our code changes that can be tried from UI:
1. To go to versions index page, type in the following url after logging in:
  http://152.46.16.81:3000/versions
2. After logging in as student/instructor or admin : Try accessing the  new, create, edit, update actions. These actions are not allowed to any of the users.
  http://152.46.16.81:3000/versions/new
  This calls the new action. In the current production version of expertiza, it is unhandled and application gives a default 404 page.
3. Another feature that can be tested from UI is Pagination. Try searching for a user's versions and see if the results are paginated or not. Search here:
  http://152.46.16.81:3000/versions/search


4. Visit the same URL as step 3, you should see only the students under that instructor in the users dropdown.


===References===
===References===


#[https://github.com/expertiza/expertiza Expertiza on GitHub]
#[https://github.com/expertiza/expertiza Expertiza on GitHub]
#[https://github.com/WintersLt/expertiza GitHub Project Repository Fork]
#[https://github.com/nlozevs/expertiza GitHub Project Repository Fork]
#[http://expertiza.ncsu.edu/ The live Expertiza website]
#[http://expertiza.ncsu.edu/ The live Expertiza website]
#[http://bit.ly/myexpertiza Demo link]  
#[http://13.58.196.224:3000/  Demo link]  
#[http://wikis.lib.ncsu.edu/index.php/Expertiza Expertiza project documentation wiki]
#[http://wikis.lib.ncsu.edu/index.php/Expertiza Expertiza project documentation wiki]
#[https://relishapp.com/rspec Rspec Documentation]
#[https://relishapp.com/rspec Rspec Documentation]
#Clean Code: A handbook of agile software craftsmanship. Author: Robert C Martin
#Clean Code: A handbook of agile software craftsmanship. Author: Robert C Martin

Latest revision as of 03:31, 20 October 2020

E2063. Refactoring the tree-display.js and tree display controller.rb

This page provides a description of the Expertiza based OSS project.



About Expertiza

Expertiza is an open source project based on Ruby on Rails framework. Expertiza allows the instructor to create new assignments and customize new or existing assignments. It also allows the instructor to create a list of topics the students can sign up for. Students can form teams in Expertiza to work on various projects and assignments. Students can also peer review other students' submissions. Expertiza supports submission across various document types, including the URLs and wiki pages.

Background

The tree-display.js and its tree_display_controller.rb files are designed to allow Expertiza users to view their Assignments, Courses and Questionnaires at one place. This is the primary control page, as well as the home page for instructors on Expertiza which allows them to create, modify, delete and view Assignments.

The primary problem with this is that both the files, due to their bulky and unoptimized methods, slow the rendering of UI on screen. The methods in these files can be studied and refactored to improve the overall performance of this controller and its corresponding UI. Moreover, any obsolete or unused methods can be removed and DRY principle should be implemented. This project mostly revolves around these 2 files, and would involve refactoring JavaScript more than Ruby on Rails. Knowledge of JavaScript is a prerequisite for this project.


Accomplishments

The following tasks were accomplished in this project:

  • Removed methods that were not used
  • Modified tree display controller to better parse data prior to being accessed by the react client
  • Fixed beta branch issue with showing dropdowns for courses and questionnaires.
  • Fixed beta branch issue with showing edit option for courses and assignments.

About Tree Display Controller

This class manages different the different tabs: courses, assignments, and questionaires.

Current Implementation

Functionality
  • Instructors can view the course, assignment and questionnaire tabs. Instructors can select rows for questionnaire's and courses to view a dropdown of more information. For course rows, they have options to edit, delete, copy, add TA, create an assignment, create teams, view grades, assign surveys, and view reviews. For assignments, they can edit, delete, copy, assign to course, create teams, assign reviewers, view submissions, view scores, view reports, and view survey responses.
Drawbacks and Solutions
  • Problem 1: The dropdown doesn't work for assignments and questionaires. This is due to the handleExpandClick function, as it appears that the get_sub_folder_contents method is not capable of returning the data.
if(this.props.dataType!='assignment') {
            _this = this;
            jQuery.get('/tree_display/get_sub_folder_contents',
                {
                    reactParams2: newParams
                },
                function (data) {
                    _this.props.data[id.split("_")[2]]['children'] = data;
                    _this.forceUpdate();
                }, 'json');
        }
  • Solution: Change the the JQuery route to a post because you can't give parameters to a get route and also change this in routes.rb file.
jQuery.post('/tree_display/get_sub_folder_contents',
                {
                    reactParams2: newParams
                },
                
post :get_sub_folder_contents
  • Problem 2: Tree display controller needs to better parse the data prior to being access to the react client because currently, there are too many comparisons when checking for the type of tab.

Example:

var SimpleTableRow = React.createClass({
    render: function () {
      var creation_date;
      var updated_date;
      var colWidthArray = ["30%", "0%", "0%", "0%", "25%", "25%", "20%"]
      var colDisplayStyle = {
        "display": ""
      }
      if (this.props.dataType === 'questionnaire') {
        colWidthArray = ["30%", "0%", "0%", "0%", "20%", "20%", "30%"]
        colDisplayStyle = {
          "display": "none"
        }
      } else if(this.props.dataType === 'course') {
        colWidthArray = ["20%", "0%", "0%", "20%", "20%", "20%", "20%"]
      }
     ......

}
  • Solution: Centralize the attributes of each tab so that the tree_display doesn't need to keep parsing between the tabs.
  • Problem 3: The Assignments and Courses tab isn't showing the edit option for each row. It seems that what's implemented is trying to verify that there was an active user and the active user is null.
if (app_variables.currentUserId == null || this.props.instructor_id == app_variables.currentUserId) {
              moreContent.push(
                  <span>
                <a title="Edit" href={"/" + newNodeType + "/" + (parseInt(this.props.id) / 2).toString() + "/edit"}><img
                    src="/assets/tree_view/edit-icon-24.png"/></a>
              </span>
              );
          }
  • Solution: Remove the active user implementation and replace with a check to make sure the current tab isn't Questionnaire.
if(newNodeType != 'questionnaires') { // should only be viewed by either assignments or courses
              moreContent.push(
                  <span>
                <a title="Edit" href={"/" + newNodeType + "/" + (parseInt(this.props.id) / 2).toString() + "/edit"}><img
                    src="/assets/tree_view/edit-icon-24.png"/></a>
              </span>
              );
          }


  • Drawback:The assignments tab did not have any data when we used the reset debugging extension (i.e. the childNodes attribute was null). Thus we assumed there was no data to display underneath each assignment.


-After debugging, we found that the "nodeType" attribute in the :reactParams field of the post request identifies the type of childNodes to be retrieved (i.e. "Courses" or "Questionnaires")

-We found that a "FolderNode" value for this attribute equates to a questionnaire.

New Implementation

Consolidation

A lot of the efforts in pushing objects into moreContent could be consolidated into a central format. An example of how its currently implemented for courses is:


if (newNodeType === 'course') {
          moreContent.push(
            <br/>
          )
          if (this.props.is_available) {
            moreContent.push(
              <span>
                <a title="Add TA" href={"/course/view_teaching_assistants?id="+(parseInt(this.props.id)/2).toString()+"&model=Course"}>
                  <img src="/assets/tree_view/add-ta-24.png" />
                </a>
                <a title="Create assignment" href={"/assignments/new?parent_id="+(parseInt(this.props.id)/2).toString()}>
                  <img src="/assets/tree_view/add-assignment-24.png" />
                </a>
                <a title="Add participants" href={"/participants/list?id="+(parseInt(this.props.id)/2).toString()+"&model=Course"}>
                  <img src="/assets/tree_view/add-participant-24.png" />
                </a>
                <a title="Create teams" href={"/teams/list?id="+(parseInt(this.props.id)/2).toString()+"&type=Course"}>
                  <img src="/assets/tree_view/create-teams-24.png" />
                </a>
                <a title="View grade summary by student" href={"/assessment360/course_student_grade_summary?course_id="+(parseInt(this.props.id)/2).toString()}>
                  <img src="/assets/tree_view/360-dashboard-24.png" />
                </a>
                <a title="Assign survey" href={"/survey_deployment/new?id="+(parseInt(this.props.id)/2).toString()+"&type=CourseSurveyDeployment"}>
                  <img src="/assets/tree_view/assign-survey-24.png" />
                </a>
                <a title="View aggregated teammate & meta reviews" href={"/assessment360/all_students_all_reviews?course_id="+(parseInt(this.props.id)/2).toString()}>
                  <span style={{"fontSize": "22px", "top": "8px"}} className="glyphicon glyphicon-list-alt"></span>
                </a>
              </span>
            )
          }

With a centralized implementation, we consolidated this information into const node_attribute. (See code repository for implementations for questionnaire and assignments, Course only shown for simplicity of demonstrating implementation).

const node_attributes = {
isCourse(name) {
    return name === 'course' || name === "courses"
  },

course: {
    plural: "course",
    actions: [ {
      title: "Add TA",
      href: "/course/view_teaching_assistants?model=Course&id=",
      src: "/assets/tree_view/add-ta-24.png",
    },
    {
      title: "Create assignment",
      href: "/assignments/new?parent_id=",
      src: "/assets/tree_view/add-assignment-24.png",
    },
    {
      title: "Add participants",
      href: "/participants/list?model=Course&id=",
      src: "/assets/tree_view/add-participant-24.png",
    },
    {
      title: "Create teams",
      href: "/teams/list?type=Course&id=",
      src: "/assets/tree_view/create-teams-24.png",
    },
    {
      title: "View grade summary by student",
      href: "/assessment360/course_student_grade_summary?course_id=",
      src: "/assets/tree_view/360-dashboard-24.png",
    },
    {
      title: "Assign survey",
      href: "/survey_deployment/new?type=CourseSurveyDeployment&id=",
      src: "/assets/tree_view/assign-survey-24.png",
    },
    {
      title: "View aggregated teammate & meta reviews",
      href: "/assessment360/all_students_all_reviews?course_id=",
      src: null,
      extra: <span style={{"fontSize": "22px", "top": "8px"}} className="glyphicon glyphicon-list-alt"></span>
    }],
    getActions: function (id) {
    return (node_attributes.course.actions.map((action) =>
        (action.src
          ? <a title={action.title} href={action.href + id}> <img src={action.src} /></a>
          : <a title={action.title} href={action.href + id}> {action.extra} </a>)))
    }
  },

With this new implementation to obtain these actions, we just use the getActions function. Additionally now call the is{Tab} to receive the name of the tab.

      if (node_attributes.isCourse(this.props.dataType)) {
          moreContent.push(<br/>)
          moreContent.push(...node_attributes.course.getActions(parseInt(this.props.id)/2))
        }

Code improvements

Unused Methods/Functions

Unused Methods and Functions were commented out

  • Example 1:
  function showIntelligentAssignmentDialog() {
    jQuery( "#intelligent_assignment_dialog" ).dialog({ closeText: "hide", modal: true, resizable: false, width: 500 });
  }
  • Example 2:

The assignments tab did not have any data when we used the reset debugging extension (i.e. the childNodes attribute was null). Thus we assumed there was no data to display underneath each assignment

    def children_node_ng
    flash[:error] = "Invalid JSON in the TreeList" unless json_valid? params[:reactParams][:child_nodes]
    child_nodes = child_nodes_from_params(params[:reactParams][:child_nodes])
    tmp_res = {}
    unless child_nodes.blank?
      child_nodes.each do |node|
        initialize_fnode_update_children(params, node, tmp_res)
      end
    end

Automated Testing using RSPEC

Testing from UI

References

  1. Expertiza on GitHub
  2. GitHub Project Repository Fork
  3. The live Expertiza website
  4. Demo link
  5. Expertiza project documentation wiki
  6. Rspec Documentation
  7. Clean Code: A handbook of agile software craftsmanship. Author: Robert C Martin