<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Svsakham</id>
	<title>Expertiza_Wiki - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://wiki.expertiza.ncsu.edu/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Svsakham"/>
	<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=Special:Contributions/Svsakham"/>
	<updated>2026-05-15T23:42:07Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.41.0</generator>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142303</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142303"/>
		<updated>2021-11-30T04:02:50Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place.&lt;br /&gt;
*Note:- We have not removed the previous implementation of the import as some of the models are yet to be moved to new import framework and removing the previous code will break backward compatibility. The following code changes are made:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Refactored the Course_participant.rb to use newly added import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.import(row_hash, session, id)&lt;br /&gt;
    byebug&lt;br /&gt;
    raise ArgumentError, &amp;quot;The record does not have enough items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    if user.nil?&lt;br /&gt;
      raise ArgumentError, &amp;quot;The record containing #{row_hash[:name]} does not have enough items.&amp;quot; if row_hash.length &amp;lt; 4&lt;br /&gt;
      attributes = ImportFileHelper.define_attributes(row_hash)&lt;br /&gt;
      user = ImportFileHelper.create_new_user(attributes, session)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    course = Course.find_by(id)&lt;br /&gt;
    raise ImportError, &amp;quot;The course with id &amp;quot; + id.to_s + &amp;quot; was not found.&amp;quot; if course.nil?&lt;br /&gt;
    unless CourseParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      CourseParticipant.create(user_id: user.id, parent_id: id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb,course_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
=== Manual Testing ===&lt;br /&gt;
* Login with expertiza credentials.&lt;br /&gt;
* Navigate to Course Participant page by clicking on Manage =&amp;gt; Courses =&amp;gt; Add Participant.&lt;br /&gt;
* Click on Import course participant link and and browse for the file to import. Select the correct options for the type of file to be imported from Import Course Participant page. Please make sure the CSV file is in correct format as expected by the Import functionality. For course participant the csv should contain username | fullname | e-mail | password. Also make sure the users that are going to be imported are already present in expertiza database.&lt;br /&gt;
* Click in import participant button and the selected users should be imported.&lt;br /&gt;
&lt;br /&gt;
=== Automated Tests ===&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Added test for the assignment_participant_team.rb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context 'when record is empty' do&lt;br /&gt;
      it 'raises an ArgumentError' do&lt;br /&gt;
        expect { AssignmentParticipant.import({}, nil, nil, nil) }.to raise_error(ArgumentError, 'No user id has been specified.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when no user is found by offered username' do&lt;br /&gt;
      context 'when the record has less than 4 items' do&lt;br /&gt;
        it 'raises an ArgumentError' do&lt;br /&gt;
          row = {name: 'no one', fullname: 'no one', email: 'no_one@email.com'}&lt;br /&gt;
          expect(ImportFileHelper).not_to receive(:create_new_user)&lt;br /&gt;
          expect { AssignmentParticipant.import(row, nil, nil, nil) }.to raise_error('The record containing no one does not have enough items.')&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      context 'when new user needs to be created' do&lt;br /&gt;
        let(:row) do&lt;br /&gt;
          {name: 'no one', fullname: 'no one', email: 'name@email.com', role:'user_role_name', parent: 'user_parent_name'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:attributes) do&lt;br /&gt;
          {role_id: 1, name: 'no one', fullname: 'no one', email: 'name@email.com', email_on_submission: 'name@email.com',&lt;br /&gt;
           email_on_review: 'name@email.com', email_on_review_of_review: 'name@email.com'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:test_user) do&lt;br /&gt;
          {name: 'abc', email: 'abcbbc@gmail.com'}&lt;br /&gt;
        end&lt;br /&gt;
        it 'create the user and number of mails sent should be 1' do&lt;br /&gt;
          ActionMailer::Base.deliveries.clear&lt;br /&gt;
          allow(ImportFileHelper).to receive(:define_attributes).with(row).and_return(attributes)&lt;br /&gt;
          allow(ImportFileHelper).to receive(:create_new_user) do&lt;br /&gt;
            test_user = User.new(name: 'abc', fullname: 'abc bbc', email: 'abcbbc@gmail.com')&lt;br /&gt;
            test_user.id = 123&lt;br /&gt;
            test_user.save!&lt;br /&gt;
            test_user&lt;br /&gt;
          end&lt;br /&gt;
          #allow(ImportFileHelper).to receive(:create_new_user).with(attributes, {}).and_return()&lt;br /&gt;
          allow(Assignment).to receive(:find).with(1).and_return(assignment)&lt;br /&gt;
          allow(User).to receive(:exists?).with(name: 'no one').and_return(false)&lt;br /&gt;
          allow(participant).to receive(:set_handle).and_return('handle')&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:exists?).and_return(false)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:set_handle)&lt;br /&gt;
          expect{(AssignmentParticipant.import(row, nil, {}, 1))}.to change { ActionMailer::Base.deliveries.count }.by(1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Future Scope ==&lt;br /&gt;
There are still some models that have import function but are not using the new framework like:-&lt;br /&gt;
* team&lt;br /&gt;
* metareview_response_map&lt;br /&gt;
* sign_up_sheet&lt;br /&gt;
* sign_up_topic&lt;br /&gt;
* user&lt;br /&gt;
Future work will include migrating these models to use new import framework and writing test cases for these scenarios. Also the previous implementation can then be safely removed.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
*Pull Request: https://github.com/expertiza/expertiza/pull/2153&lt;br /&gt;
*Video: https://drive.google.com/file/d/19zPSOhh2AzHR-EF8v9wiwWrtGwtsUJL0/view?usp=sharing&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142302</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142302"/>
		<updated>2021-11-30T04:02:36Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place.&lt;br /&gt;
*Note:- We have not removed the previous implementation of the import as some of the models are yet to be moved to new import framework and removing the previous code will break backward compatibility. The following code changes are made:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Refactored the Course_participant.rb to use newly added import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.import(row_hash, session, id)&lt;br /&gt;
    byebug&lt;br /&gt;
    raise ArgumentError, &amp;quot;The record does not have enough items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    if user.nil?&lt;br /&gt;
      raise ArgumentError, &amp;quot;The record containing #{row_hash[:name]} does not have enough items.&amp;quot; if row_hash.length &amp;lt; 4&lt;br /&gt;
      attributes = ImportFileHelper.define_attributes(row_hash)&lt;br /&gt;
      user = ImportFileHelper.create_new_user(attributes, session)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    course = Course.find_by(id)&lt;br /&gt;
    raise ImportError, &amp;quot;The course with id &amp;quot; + id.to_s + &amp;quot; was not found.&amp;quot; if course.nil?&lt;br /&gt;
    unless CourseParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      CourseParticipant.create(user_id: user.id, parent_id: id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb,course_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
=== Manual Testing ===&lt;br /&gt;
* Login with expertiza credentials.&lt;br /&gt;
* Navigate to Course Participant page by clicking on Manage =&amp;gt; Courses =&amp;gt; Add Participant.&lt;br /&gt;
* Click on Import course participant link and and browse for the file to import. Select the correct options for the type of file to be imported from Import Course Participant page. Please make sure the CSV file is in correct format as expected by the Import functionality. For course participant the csv should contain username | fullname | e-mail | password. Also make sure the users that are going to be imported are already present in expertiza database.&lt;br /&gt;
* Click in import participant button and the selected users should be imported.&lt;br /&gt;
&lt;br /&gt;
=== Automated Tests ===&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Added test for the assignment_participant_team.rb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context 'when record is empty' do&lt;br /&gt;
      it 'raises an ArgumentError' do&lt;br /&gt;
        expect { AssignmentParticipant.import({}, nil, nil, nil) }.to raise_error(ArgumentError, 'No user id has been specified.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when no user is found by offered username' do&lt;br /&gt;
      context 'when the record has less than 4 items' do&lt;br /&gt;
        it 'raises an ArgumentError' do&lt;br /&gt;
          row = {name: 'no one', fullname: 'no one', email: 'no_one@email.com'}&lt;br /&gt;
          expect(ImportFileHelper).not_to receive(:create_new_user)&lt;br /&gt;
          expect { AssignmentParticipant.import(row, nil, nil, nil) }.to raise_error('The record containing no one does not have enough items.')&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      context 'when new user needs to be created' do&lt;br /&gt;
        let(:row) do&lt;br /&gt;
          {name: 'no one', fullname: 'no one', email: 'name@email.com', role:'user_role_name', parent: 'user_parent_name'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:attributes) do&lt;br /&gt;
          {role_id: 1, name: 'no one', fullname: 'no one', email: 'name@email.com', email_on_submission: 'name@email.com',&lt;br /&gt;
           email_on_review: 'name@email.com', email_on_review_of_review: 'name@email.com'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:test_user) do&lt;br /&gt;
          {name: 'abc', email: 'abcbbc@gmail.com'}&lt;br /&gt;
        end&lt;br /&gt;
        it 'create the user and number of mails sent should be 1' do&lt;br /&gt;
          ActionMailer::Base.deliveries.clear&lt;br /&gt;
          allow(ImportFileHelper).to receive(:define_attributes).with(row).and_return(attributes)&lt;br /&gt;
          allow(ImportFileHelper).to receive(:create_new_user) do&lt;br /&gt;
            test_user = User.new(name: 'abc', fullname: 'abc bbc', email: 'abcbbc@gmail.com')&lt;br /&gt;
            test_user.id = 123&lt;br /&gt;
            test_user.save!&lt;br /&gt;
            test_user&lt;br /&gt;
          end&lt;br /&gt;
          #allow(ImportFileHelper).to receive(:create_new_user).with(attributes, {}).and_return()&lt;br /&gt;
          allow(Assignment).to receive(:find).with(1).and_return(assignment)&lt;br /&gt;
          allow(User).to receive(:exists?).with(name: 'no one').and_return(false)&lt;br /&gt;
          allow(participant).to receive(:set_handle).and_return('handle')&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:exists?).and_return(false)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:set_handle)&lt;br /&gt;
          expect{(AssignmentParticipant.import(row, nil, {}, 1))}.to change { ActionMailer::Base.deliveries.count }.by(1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Future Scope ==&lt;br /&gt;
There are still some models that have import function but are not using the new framework like:-&lt;br /&gt;
* team&lt;br /&gt;
* metareview_response_map&lt;br /&gt;
* sign_up_sheet&lt;br /&gt;
* sign_up_topic&lt;br /&gt;
* user&lt;br /&gt;
Future work will include migrating these models to use new import framework and writing test cases for these scenarios. Also the previous implementation can then be safely removed.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
*'''Pull Request: https://github.com/expertiza/expertiza/pull/2153&lt;br /&gt;
*'''Video: https://drive.google.com/file/d/19zPSOhh2AzHR-EF8v9wiwWrtGwtsUJL0/view?usp=sharing&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142297</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142297"/>
		<updated>2021-11-30T03:58:24Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place.&lt;br /&gt;
*Note:- We have not removed the previous implementation of the import as some of the models are yet to be moved to new import framework and removing the previous code will break backward compatibility. The following code changes are made:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Refactored the Course_participant.rb to use newly added import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.import(row_hash, session, id)&lt;br /&gt;
    byebug&lt;br /&gt;
    raise ArgumentError, &amp;quot;The record does not have enough items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    if user.nil?&lt;br /&gt;
      raise ArgumentError, &amp;quot;The record containing #{row_hash[:name]} does not have enough items.&amp;quot; if row_hash.length &amp;lt; 4&lt;br /&gt;
      attributes = ImportFileHelper.define_attributes(row_hash)&lt;br /&gt;
      user = ImportFileHelper.create_new_user(attributes, session)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    course = Course.find_by(id)&lt;br /&gt;
    raise ImportError, &amp;quot;The course with id &amp;quot; + id.to_s + &amp;quot; was not found.&amp;quot; if course.nil?&lt;br /&gt;
    unless CourseParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      CourseParticipant.create(user_id: user.id, parent_id: id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb,course_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
=== Manual Testing ===&lt;br /&gt;
* Login with expertiza credentials.&lt;br /&gt;
* Navigate to Course Participant page by clicking on Manage =&amp;gt; Courses =&amp;gt; Add Participant.&lt;br /&gt;
* Click on Import course participant link and and browse for the file to import. Select the correct options for the type of file to be imported from Import Course Participant page. Please make sure the CSV file is in correct format as expected by the Import functionality. For course participant the csv should contain username | fullname | e-mail | password. Also make sure the users that are going to be imported are already present in expertiza database.&lt;br /&gt;
* Click in import participant button and the selected users should be imported.&lt;br /&gt;
&lt;br /&gt;
=== Automated Tests ===&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Added test for the assignment_participant_team.rb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context 'when record is empty' do&lt;br /&gt;
      it 'raises an ArgumentError' do&lt;br /&gt;
        expect { AssignmentParticipant.import({}, nil, nil, nil) }.to raise_error(ArgumentError, 'No user id has been specified.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when no user is found by offered username' do&lt;br /&gt;
      context 'when the record has less than 4 items' do&lt;br /&gt;
        it 'raises an ArgumentError' do&lt;br /&gt;
          row = {name: 'no one', fullname: 'no one', email: 'no_one@email.com'}&lt;br /&gt;
          expect(ImportFileHelper).not_to receive(:create_new_user)&lt;br /&gt;
          expect { AssignmentParticipant.import(row, nil, nil, nil) }.to raise_error('The record containing no one does not have enough items.')&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      context 'when new user needs to be created' do&lt;br /&gt;
        let(:row) do&lt;br /&gt;
          {name: 'no one', fullname: 'no one', email: 'name@email.com', role:'user_role_name', parent: 'user_parent_name'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:attributes) do&lt;br /&gt;
          {role_id: 1, name: 'no one', fullname: 'no one', email: 'name@email.com', email_on_submission: 'name@email.com',&lt;br /&gt;
           email_on_review: 'name@email.com', email_on_review_of_review: 'name@email.com'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:test_user) do&lt;br /&gt;
          {name: 'abc', email: 'abcbbc@gmail.com'}&lt;br /&gt;
        end&lt;br /&gt;
        it 'create the user and number of mails sent should be 1' do&lt;br /&gt;
          ActionMailer::Base.deliveries.clear&lt;br /&gt;
          allow(ImportFileHelper).to receive(:define_attributes).with(row).and_return(attributes)&lt;br /&gt;
          allow(ImportFileHelper).to receive(:create_new_user) do&lt;br /&gt;
            test_user = User.new(name: 'abc', fullname: 'abc bbc', email: 'abcbbc@gmail.com')&lt;br /&gt;
            test_user.id = 123&lt;br /&gt;
            test_user.save!&lt;br /&gt;
            test_user&lt;br /&gt;
          end&lt;br /&gt;
          #allow(ImportFileHelper).to receive(:create_new_user).with(attributes, {}).and_return()&lt;br /&gt;
          allow(Assignment).to receive(:find).with(1).and_return(assignment)&lt;br /&gt;
          allow(User).to receive(:exists?).with(name: 'no one').and_return(false)&lt;br /&gt;
          allow(participant).to receive(:set_handle).and_return('handle')&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:exists?).and_return(false)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:set_handle)&lt;br /&gt;
          expect{(AssignmentParticipant.import(row, nil, {}, 1))}.to change { ActionMailer::Base.deliveries.count }.by(1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Future Scope ==&lt;br /&gt;
There are still some models that have import function but are not using the new framework like:-&lt;br /&gt;
* team&lt;br /&gt;
* metareview_response_map&lt;br /&gt;
* sign_up_sheet&lt;br /&gt;
* sign_up_topic&lt;br /&gt;
* user&lt;br /&gt;
Future work will include migrating these models to use new import framework and writing test cases for these scenarios. Also the previous implementation can then be safely removed.&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
*'''Pull Request: https://github.com/expertiza/expertiza/pull/2153&lt;br /&gt;
*'''Video: https://drive.google.com/file/d/19zPSOhh2AzHR-EF8v9wiwWrtGwtsUJL0/view?usp=sharing&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142283</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142283"/>
		<updated>2021-11-30T03:45:53Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Refactored the Course_participant.rb to use newly added import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.import(row_hash, session, id)&lt;br /&gt;
    byebug&lt;br /&gt;
    raise ArgumentError, &amp;quot;The record does not have enough items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    if user.nil?&lt;br /&gt;
      raise ArgumentError, &amp;quot;The record containing #{row_hash[:name]} does not have enough items.&amp;quot; if row_hash.length &amp;lt; 4&lt;br /&gt;
      attributes = ImportFileHelper.define_attributes(row_hash)&lt;br /&gt;
      user = ImportFileHelper.create_new_user(attributes, session)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    course = Course.find_by(id)&lt;br /&gt;
    raise ImportError, &amp;quot;The course with id &amp;quot; + id.to_s + &amp;quot; was not found.&amp;quot; if course.nil?&lt;br /&gt;
    unless CourseParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      CourseParticipant.create(user_id: user.id, parent_id: id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb,course_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
=== Manual Testing ===&lt;br /&gt;
* Login with expertiza credentials.&lt;br /&gt;
* Navigate to Course Participant page by clicking on Manage =&amp;gt; Courses =&amp;gt; Add Participant.&lt;br /&gt;
* Click on Import course participant link and and browse for the file to import. Select the correct options for the type of file to be imported from Import Course Participant page. Please make sure the CSV file is in correct format as expected by the Import functionality. For course participant the csv should contain username | fullname | e-mail | password. Also make sure the users that are going to be imported are already present in expertiza database.&lt;br /&gt;
* Click in import participant button and the selected users should be imported.&lt;br /&gt;
&lt;br /&gt;
=== Automated Tests ===&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Added test for the assignment_participant_team.rb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context 'when record is empty' do&lt;br /&gt;
      it 'raises an ArgumentError' do&lt;br /&gt;
        expect { AssignmentParticipant.import({}, nil, nil, nil) }.to raise_error(ArgumentError, 'No user id has been specified.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when no user is found by offered username' do&lt;br /&gt;
      context 'when the record has less than 4 items' do&lt;br /&gt;
        it 'raises an ArgumentError' do&lt;br /&gt;
          row = {name: 'no one', fullname: 'no one', email: 'no_one@email.com'}&lt;br /&gt;
          expect(ImportFileHelper).not_to receive(:create_new_user)&lt;br /&gt;
          expect { AssignmentParticipant.import(row, nil, nil, nil) }.to raise_error('The record containing no one does not have enough items.')&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      context 'when new user needs to be created' do&lt;br /&gt;
        let(:row) do&lt;br /&gt;
          {name: 'no one', fullname: 'no one', email: 'name@email.com', role:'user_role_name', parent: 'user_parent_name'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:attributes) do&lt;br /&gt;
          {role_id: 1, name: 'no one', fullname: 'no one', email: 'name@email.com', email_on_submission: 'name@email.com',&lt;br /&gt;
           email_on_review: 'name@email.com', email_on_review_of_review: 'name@email.com'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:test_user) do&lt;br /&gt;
          {name: 'abc', email: 'abcbbc@gmail.com'}&lt;br /&gt;
        end&lt;br /&gt;
        it 'create the user and number of mails sent should be 1' do&lt;br /&gt;
          ActionMailer::Base.deliveries.clear&lt;br /&gt;
          allow(ImportFileHelper).to receive(:define_attributes).with(row).and_return(attributes)&lt;br /&gt;
          allow(ImportFileHelper).to receive(:create_new_user) do&lt;br /&gt;
            test_user = User.new(name: 'abc', fullname: 'abc bbc', email: 'abcbbc@gmail.com')&lt;br /&gt;
            test_user.id = 123&lt;br /&gt;
            test_user.save!&lt;br /&gt;
            test_user&lt;br /&gt;
          end&lt;br /&gt;
          #allow(ImportFileHelper).to receive(:create_new_user).with(attributes, {}).and_return()&lt;br /&gt;
          allow(Assignment).to receive(:find).with(1).and_return(assignment)&lt;br /&gt;
          allow(User).to receive(:exists?).with(name: 'no one').and_return(false)&lt;br /&gt;
          allow(participant).to receive(:set_handle).and_return('handle')&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:exists?).and_return(false)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:set_handle)&lt;br /&gt;
          expect{(AssignmentParticipant.import(row, nil, {}, 1))}.to change { ActionMailer::Base.deliveries.count }.by(1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
*'''Pull Request: https://github.com/expertiza/expertiza/pull/2153&lt;br /&gt;
*'''Video: https://drive.google.com/file/d/19zPSOhh2AzHR-EF8v9wiwWrtGwtsUJL0/view?usp=sharing&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142281</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142281"/>
		<updated>2021-11-30T03:44:33Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Refactored the Course_participant.rb to use newly added import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.import(row_hash, session, id)&lt;br /&gt;
    byebug&lt;br /&gt;
    raise ArgumentError, &amp;quot;The record does not have enough items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    if user.nil?&lt;br /&gt;
      raise ArgumentError, &amp;quot;The record containing #{row_hash[:name]} does not have enough items.&amp;quot; if row_hash.length &amp;lt; 4&lt;br /&gt;
      attributes = ImportFileHelper.define_attributes(row_hash)&lt;br /&gt;
      user = ImportFileHelper.create_new_user(attributes, session)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    course = Course.find_by(id)&lt;br /&gt;
    raise ImportError, &amp;quot;The course with id &amp;quot; + id.to_s + &amp;quot; was not found.&amp;quot; if course.nil?&lt;br /&gt;
    unless CourseParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      CourseParticipant.create(user_id: user.id, parent_id: id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb,course_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
=== Manual Testing ===&lt;br /&gt;
* Login with expertiza credentials.&lt;br /&gt;
* Navigate to Course Participant page by clicking on Manage =&amp;gt; Courses =&amp;gt; Add Participant.&lt;br /&gt;
* Click on Import course participant link and and browse for the file to import. Select the correct options for the type of file to be imported from Import Course Participant page. Please make sure the CSV file is in correct format as expected by the Import functionality. For course participant the csv should contain username | fullname | e-mail | password. Also make sure the users that are going to be imported are already present in expertiza database.&lt;br /&gt;
* Click in import participant button and the selected users should be imported.&lt;br /&gt;
&lt;br /&gt;
=== Automated Tests ===&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Added test for the assignment_participant_team.rb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context 'when record is empty' do&lt;br /&gt;
      it 'raises an ArgumentError' do&lt;br /&gt;
        expect { AssignmentParticipant.import({}, nil, nil, nil) }.to raise_error(ArgumentError, 'No user id has been specified.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when no user is found by offered username' do&lt;br /&gt;
      context 'when the record has less than 4 items' do&lt;br /&gt;
        it 'raises an ArgumentError' do&lt;br /&gt;
          row = {name: 'no one', fullname: 'no one', email: 'no_one@email.com'}&lt;br /&gt;
          expect(ImportFileHelper).not_to receive(:create_new_user)&lt;br /&gt;
          expect { AssignmentParticipant.import(row, nil, nil, nil) }.to raise_error('The record containing no one does not have enough items.')&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      context 'when new user needs to be created' do&lt;br /&gt;
        let(:row) do&lt;br /&gt;
          {name: 'no one', fullname: 'no one', email: 'name@email.com', role:'user_role_name', parent: 'user_parent_name'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:attributes) do&lt;br /&gt;
          {role_id: 1, name: 'no one', fullname: 'no one', email: 'name@email.com', email_on_submission: 'name@email.com',&lt;br /&gt;
           email_on_review: 'name@email.com', email_on_review_of_review: 'name@email.com'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:test_user) do&lt;br /&gt;
          {name: 'abc', email: 'abcbbc@gmail.com'}&lt;br /&gt;
        end&lt;br /&gt;
        it 'create the user and number of mails sent should be 1' do&lt;br /&gt;
          ActionMailer::Base.deliveries.clear&lt;br /&gt;
          allow(ImportFileHelper).to receive(:define_attributes).with(row).and_return(attributes)&lt;br /&gt;
          allow(ImportFileHelper).to receive(:create_new_user) do&lt;br /&gt;
            test_user = User.new(name: 'abc', fullname: 'abc bbc', email: 'abcbbc@gmail.com')&lt;br /&gt;
            test_user.id = 123&lt;br /&gt;
            test_user.save!&lt;br /&gt;
            test_user&lt;br /&gt;
          end&lt;br /&gt;
          #allow(ImportFileHelper).to receive(:create_new_user).with(attributes, {}).and_return()&lt;br /&gt;
          allow(Assignment).to receive(:find).with(1).and_return(assignment)&lt;br /&gt;
          allow(User).to receive(:exists?).with(name: 'no one').and_return(false)&lt;br /&gt;
          allow(participant).to receive(:set_handle).and_return('handle')&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:exists?).and_return(false)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:set_handle)&lt;br /&gt;
          expect{(AssignmentParticipant.import(row, nil, {}, 1))}.to change { ActionMailer::Base.deliveries.count }.by(1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142280</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142280"/>
		<updated>2021-11-30T03:44:06Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* Links */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Refactored the Course_participant.rb to use newly added import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.import(row_hash, session, id)&lt;br /&gt;
    byebug&lt;br /&gt;
    raise ArgumentError, &amp;quot;The record does not have enough items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    if user.nil?&lt;br /&gt;
      raise ArgumentError, &amp;quot;The record containing #{row_hash[:name]} does not have enough items.&amp;quot; if row_hash.length &amp;lt; 4&lt;br /&gt;
      attributes = ImportFileHelper.define_attributes(row_hash)&lt;br /&gt;
      user = ImportFileHelper.create_new_user(attributes, session)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    course = Course.find_by(id)&lt;br /&gt;
    raise ImportError, &amp;quot;The course with id &amp;quot; + id.to_s + &amp;quot; was not found.&amp;quot; if course.nil?&lt;br /&gt;
    unless CourseParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      CourseParticipant.create(user_id: user.id, parent_id: id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb,course_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Added test for the assignment_participant_team.rb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context 'when record is empty' do&lt;br /&gt;
      it 'raises an ArgumentError' do&lt;br /&gt;
        expect { AssignmentParticipant.import({}, nil, nil, nil) }.to raise_error(ArgumentError, 'No user id has been specified.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when no user is found by offered username' do&lt;br /&gt;
      context 'when the record has less than 4 items' do&lt;br /&gt;
        it 'raises an ArgumentError' do&lt;br /&gt;
          row = {name: 'no one', fullname: 'no one', email: 'no_one@email.com'}&lt;br /&gt;
          expect(ImportFileHelper).not_to receive(:create_new_user)&lt;br /&gt;
          expect { AssignmentParticipant.import(row, nil, nil, nil) }.to raise_error('The record containing no one does not have enough items.')&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      context 'when new user needs to be created' do&lt;br /&gt;
        let(:row) do&lt;br /&gt;
          {name: 'no one', fullname: 'no one', email: 'name@email.com', role:'user_role_name', parent: 'user_parent_name'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:attributes) do&lt;br /&gt;
          {role_id: 1, name: 'no one', fullname: 'no one', email: 'name@email.com', email_on_submission: 'name@email.com',&lt;br /&gt;
           email_on_review: 'name@email.com', email_on_review_of_review: 'name@email.com'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:test_user) do&lt;br /&gt;
          {name: 'abc', email: 'abcbbc@gmail.com'}&lt;br /&gt;
        end&lt;br /&gt;
        it 'create the user and number of mails sent should be 1' do&lt;br /&gt;
          ActionMailer::Base.deliveries.clear&lt;br /&gt;
          allow(ImportFileHelper).to receive(:define_attributes).with(row).and_return(attributes)&lt;br /&gt;
          allow(ImportFileHelper).to receive(:create_new_user) do&lt;br /&gt;
            test_user = User.new(name: 'abc', fullname: 'abc bbc', email: 'abcbbc@gmail.com')&lt;br /&gt;
            test_user.id = 123&lt;br /&gt;
            test_user.save!&lt;br /&gt;
            test_user&lt;br /&gt;
          end&lt;br /&gt;
          #allow(ImportFileHelper).to receive(:create_new_user).with(attributes, {}).and_return()&lt;br /&gt;
          allow(Assignment).to receive(:find).with(1).and_return(assignment)&lt;br /&gt;
          allow(User).to receive(:exists?).with(name: 'no one').and_return(false)&lt;br /&gt;
          allow(participant).to receive(:set_handle).and_return('handle')&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:exists?).and_return(false)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:set_handle)&lt;br /&gt;
          expect{(AssignmentParticipant.import(row, nil, {}, 1))}.to change { ActionMailer::Base.deliveries.count }.by(1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
*'''Pull Request: https://github.com/expertiza/expertiza/pull/2153&lt;br /&gt;
*'''Video: https://drive.google.com/file/d/19zPSOhh2AzHR-EF8v9wiwWrtGwtsUJL0/view?usp=sharing&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142279</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142279"/>
		<updated>2021-11-30T03:43:49Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Refactored the Course_participant.rb to use newly added import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.import(row_hash, session, id)&lt;br /&gt;
    byebug&lt;br /&gt;
    raise ArgumentError, &amp;quot;The record does not have enough items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    if user.nil?&lt;br /&gt;
      raise ArgumentError, &amp;quot;The record containing #{row_hash[:name]} does not have enough items.&amp;quot; if row_hash.length &amp;lt; 4&lt;br /&gt;
      attributes = ImportFileHelper.define_attributes(row_hash)&lt;br /&gt;
      user = ImportFileHelper.create_new_user(attributes, session)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    course = Course.find_by(id)&lt;br /&gt;
    raise ImportError, &amp;quot;The course with id &amp;quot; + id.to_s + &amp;quot; was not found.&amp;quot; if course.nil?&lt;br /&gt;
    unless CourseParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      CourseParticipant.create(user_id: user.id, parent_id: id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb,course_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Added test for the assignment_participant_team.rb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context 'when record is empty' do&lt;br /&gt;
      it 'raises an ArgumentError' do&lt;br /&gt;
        expect { AssignmentParticipant.import({}, nil, nil, nil) }.to raise_error(ArgumentError, 'No user id has been specified.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when no user is found by offered username' do&lt;br /&gt;
      context 'when the record has less than 4 items' do&lt;br /&gt;
        it 'raises an ArgumentError' do&lt;br /&gt;
          row = {name: 'no one', fullname: 'no one', email: 'no_one@email.com'}&lt;br /&gt;
          expect(ImportFileHelper).not_to receive(:create_new_user)&lt;br /&gt;
          expect { AssignmentParticipant.import(row, nil, nil, nil) }.to raise_error('The record containing no one does not have enough items.')&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      context 'when new user needs to be created' do&lt;br /&gt;
        let(:row) do&lt;br /&gt;
          {name: 'no one', fullname: 'no one', email: 'name@email.com', role:'user_role_name', parent: 'user_parent_name'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:attributes) do&lt;br /&gt;
          {role_id: 1, name: 'no one', fullname: 'no one', email: 'name@email.com', email_on_submission: 'name@email.com',&lt;br /&gt;
           email_on_review: 'name@email.com', email_on_review_of_review: 'name@email.com'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:test_user) do&lt;br /&gt;
          {name: 'abc', email: 'abcbbc@gmail.com'}&lt;br /&gt;
        end&lt;br /&gt;
        it 'create the user and number of mails sent should be 1' do&lt;br /&gt;
          ActionMailer::Base.deliveries.clear&lt;br /&gt;
          allow(ImportFileHelper).to receive(:define_attributes).with(row).and_return(attributes)&lt;br /&gt;
          allow(ImportFileHelper).to receive(:create_new_user) do&lt;br /&gt;
            test_user = User.new(name: 'abc', fullname: 'abc bbc', email: 'abcbbc@gmail.com')&lt;br /&gt;
            test_user.id = 123&lt;br /&gt;
            test_user.save!&lt;br /&gt;
            test_user&lt;br /&gt;
          end&lt;br /&gt;
          #allow(ImportFileHelper).to receive(:create_new_user).with(attributes, {}).and_return()&lt;br /&gt;
          allow(Assignment).to receive(:find).with(1).and_return(assignment)&lt;br /&gt;
          allow(User).to receive(:exists?).with(name: 'no one').and_return(false)&lt;br /&gt;
          allow(participant).to receive(:set_handle).and_return('handle')&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:exists?).and_return(false)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:set_handle)&lt;br /&gt;
          expect{(AssignmentParticipant.import(row, nil, {}, 1))}.to change { ActionMailer::Base.deliveries.count }.by(1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Links ==&lt;br /&gt;
&lt;br /&gt;
*Pull Request: https://github.com/expertiza/expertiza/pull/2153&lt;br /&gt;
*Video: https://drive.google.com/file/d/19zPSOhh2AzHR-EF8v9wiwWrtGwtsUJL0/view?usp=sharing&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142028</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142028"/>
		<updated>2021-11-29T23:21:29Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== Implementation Details ==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Refactored the Course_participant.rb to use newly added import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.import(row_hash, session, id)&lt;br /&gt;
    byebug&lt;br /&gt;
    raise ArgumentError, &amp;quot;The record does not have enough items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    if user.nil?&lt;br /&gt;
      raise ArgumentError, &amp;quot;The record containing #{row_hash[:name]} does not have enough items.&amp;quot; if row_hash.length &amp;lt; 4&lt;br /&gt;
      attributes = ImportFileHelper.define_attributes(row_hash)&lt;br /&gt;
      user = ImportFileHelper.create_new_user(attributes, session)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    course = Course.find_by(id)&lt;br /&gt;
    raise ImportError, &amp;quot;The course with id &amp;quot; + id.to_s + &amp;quot; was not found.&amp;quot; if course.nil?&lt;br /&gt;
    unless CourseParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      CourseParticipant.create(user_id: user.id, parent_id: id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb,course_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Added test for the assignment_participant_team.rb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context 'when record is empty' do&lt;br /&gt;
      it 'raises an ArgumentError' do&lt;br /&gt;
        expect { AssignmentParticipant.import({}, nil, nil, nil) }.to raise_error(ArgumentError, 'No user id has been specified.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when no user is found by offered username' do&lt;br /&gt;
      context 'when the record has less than 4 items' do&lt;br /&gt;
        it 'raises an ArgumentError' do&lt;br /&gt;
          row = {name: 'no one', fullname: 'no one', email: 'no_one@email.com'}&lt;br /&gt;
          expect(ImportFileHelper).not_to receive(:create_new_user)&lt;br /&gt;
          expect { AssignmentParticipant.import(row, nil, nil, nil) }.to raise_error('The record containing no one does not have enough items.')&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      context 'when new user needs to be created' do&lt;br /&gt;
        let(:row) do&lt;br /&gt;
          {name: 'no one', fullname: 'no one', email: 'name@email.com', role:'user_role_name', parent: 'user_parent_name'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:attributes) do&lt;br /&gt;
          {role_id: 1, name: 'no one', fullname: 'no one', email: 'name@email.com', email_on_submission: 'name@email.com',&lt;br /&gt;
           email_on_review: 'name@email.com', email_on_review_of_review: 'name@email.com'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:test_user) do&lt;br /&gt;
          {name: 'abc', email: 'abcbbc@gmail.com'}&lt;br /&gt;
        end&lt;br /&gt;
        it 'create the user and number of mails sent should be 1' do&lt;br /&gt;
          ActionMailer::Base.deliveries.clear&lt;br /&gt;
          allow(ImportFileHelper).to receive(:define_attributes).with(row).and_return(attributes)&lt;br /&gt;
          allow(ImportFileHelper).to receive(:create_new_user) do&lt;br /&gt;
            test_user = User.new(name: 'abc', fullname: 'abc bbc', email: 'abcbbc@gmail.com')&lt;br /&gt;
            test_user.id = 123&lt;br /&gt;
            test_user.save!&lt;br /&gt;
            test_user&lt;br /&gt;
          end&lt;br /&gt;
          #allow(ImportFileHelper).to receive(:create_new_user).with(attributes, {}).and_return()&lt;br /&gt;
          allow(Assignment).to receive(:find).with(1).and_return(assignment)&lt;br /&gt;
          allow(User).to receive(:exists?).with(name: 'no one').and_return(false)&lt;br /&gt;
          allow(participant).to receive(:set_handle).and_return('handle')&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:exists?).and_return(false)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:set_handle)&lt;br /&gt;
          expect{(AssignmentParticipant.import(row, nil, {}, 1))}.to change { ActionMailer::Base.deliveries.count }.by(1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142026</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142026"/>
		<updated>2021-11-29T23:21:03Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Refactored the Course_participant.rb to use newly added import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.import(row_hash, session, id)&lt;br /&gt;
    byebug&lt;br /&gt;
    raise ArgumentError, &amp;quot;The record does not have enough items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    if user.nil?&lt;br /&gt;
      raise ArgumentError, &amp;quot;The record containing #{row_hash[:name]} does not have enough items.&amp;quot; if row_hash.length &amp;lt; 4&lt;br /&gt;
      attributes = ImportFileHelper.define_attributes(row_hash)&lt;br /&gt;
      user = ImportFileHelper.create_new_user(attributes, session)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    course = Course.find_by(id)&lt;br /&gt;
    raise ImportError, &amp;quot;The course with id &amp;quot; + id.to_s + &amp;quot; was not found.&amp;quot; if course.nil?&lt;br /&gt;
    unless CourseParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      CourseParticipant.create(user_id: user.id, parent_id: id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb,course_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Added test for the assignment_participant_team.rb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context 'when record is empty' do&lt;br /&gt;
      it 'raises an ArgumentError' do&lt;br /&gt;
        expect { AssignmentParticipant.import({}, nil, nil, nil) }.to raise_error(ArgumentError, 'No user id has been specified.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when no user is found by offered username' do&lt;br /&gt;
      context 'when the record has less than 4 items' do&lt;br /&gt;
        it 'raises an ArgumentError' do&lt;br /&gt;
          row = {name: 'no one', fullname: 'no one', email: 'no_one@email.com'}&lt;br /&gt;
          expect(ImportFileHelper).not_to receive(:create_new_user)&lt;br /&gt;
          expect { AssignmentParticipant.import(row, nil, nil, nil) }.to raise_error('The record containing no one does not have enough items.')&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      context 'when new user needs to be created' do&lt;br /&gt;
        let(:row) do&lt;br /&gt;
          {name: 'no one', fullname: 'no one', email: 'name@email.com', role:'user_role_name', parent: 'user_parent_name'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:attributes) do&lt;br /&gt;
          {role_id: 1, name: 'no one', fullname: 'no one', email: 'name@email.com', email_on_submission: 'name@email.com',&lt;br /&gt;
           email_on_review: 'name@email.com', email_on_review_of_review: 'name@email.com'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:test_user) do&lt;br /&gt;
          {name: 'abc', email: 'abcbbc@gmail.com'}&lt;br /&gt;
        end&lt;br /&gt;
        it 'create the user and number of mails sent should be 1' do&lt;br /&gt;
          ActionMailer::Base.deliveries.clear&lt;br /&gt;
          allow(ImportFileHelper).to receive(:define_attributes).with(row).and_return(attributes)&lt;br /&gt;
          allow(ImportFileHelper).to receive(:create_new_user) do&lt;br /&gt;
            test_user = User.new(name: 'abc', fullname: 'abc bbc', email: 'abcbbc@gmail.com')&lt;br /&gt;
            test_user.id = 123&lt;br /&gt;
            test_user.save!&lt;br /&gt;
            test_user&lt;br /&gt;
          end&lt;br /&gt;
          #allow(ImportFileHelper).to receive(:create_new_user).with(attributes, {}).and_return()&lt;br /&gt;
          allow(Assignment).to receive(:find).with(1).and_return(assignment)&lt;br /&gt;
          allow(User).to receive(:exists?).with(name: 'no one').and_return(false)&lt;br /&gt;
          allow(participant).to receive(:set_handle).and_return('handle')&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:exists?).and_return(false)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:set_handle)&lt;br /&gt;
          expect{(AssignmentParticipant.import(row, nil, {}, 1))}.to change { ActionMailer::Base.deliveries.count }.by(1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142024</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142024"/>
		<updated>2021-11-29T23:20:28Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Refactored the Course_participant.rb to use newly added import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.import(row_hash, session, id)&lt;br /&gt;
    byebug&lt;br /&gt;
    raise ArgumentError, &amp;quot;The record does not have enough items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    if user.nil?&lt;br /&gt;
      raise ArgumentError, &amp;quot;The record containing #{row_hash[:name]} does not have enough items.&amp;quot; if row_hash.length &amp;lt; 4&lt;br /&gt;
      attributes = ImportFileHelper.define_attributes(row_hash)&lt;br /&gt;
      user = ImportFileHelper.create_new_user(attributes, session)&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    course = Course.find_by(id)&lt;br /&gt;
    raise ImportError, &amp;quot;The course with id &amp;quot; + id.to_s + &amp;quot; was not found.&amp;quot; if course.nil?&lt;br /&gt;
    unless CourseParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      CourseParticipant.create(user_id: user.id, parent_id: id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Added test for the assignment_participant_team.rb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context 'when record is empty' do&lt;br /&gt;
      it 'raises an ArgumentError' do&lt;br /&gt;
        expect { AssignmentParticipant.import({}, nil, nil, nil) }.to raise_error(ArgumentError, 'No user id has been specified.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when no user is found by offered username' do&lt;br /&gt;
      context 'when the record has less than 4 items' do&lt;br /&gt;
        it 'raises an ArgumentError' do&lt;br /&gt;
          row = {name: 'no one', fullname: 'no one', email: 'no_one@email.com'}&lt;br /&gt;
          expect(ImportFileHelper).not_to receive(:create_new_user)&lt;br /&gt;
          expect { AssignmentParticipant.import(row, nil, nil, nil) }.to raise_error('The record containing no one does not have enough items.')&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      context 'when new user needs to be created' do&lt;br /&gt;
        let(:row) do&lt;br /&gt;
          {name: 'no one', fullname: 'no one', email: 'name@email.com', role:'user_role_name', parent: 'user_parent_name'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:attributes) do&lt;br /&gt;
          {role_id: 1, name: 'no one', fullname: 'no one', email: 'name@email.com', email_on_submission: 'name@email.com',&lt;br /&gt;
           email_on_review: 'name@email.com', email_on_review_of_review: 'name@email.com'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:test_user) do&lt;br /&gt;
          {name: 'abc', email: 'abcbbc@gmail.com'}&lt;br /&gt;
        end&lt;br /&gt;
        it 'create the user and number of mails sent should be 1' do&lt;br /&gt;
          ActionMailer::Base.deliveries.clear&lt;br /&gt;
          allow(ImportFileHelper).to receive(:define_attributes).with(row).and_return(attributes)&lt;br /&gt;
          allow(ImportFileHelper).to receive(:create_new_user) do&lt;br /&gt;
            test_user = User.new(name: 'abc', fullname: 'abc bbc', email: 'abcbbc@gmail.com')&lt;br /&gt;
            test_user.id = 123&lt;br /&gt;
            test_user.save!&lt;br /&gt;
            test_user&lt;br /&gt;
          end&lt;br /&gt;
          #allow(ImportFileHelper).to receive(:create_new_user).with(attributes, {}).and_return()&lt;br /&gt;
          allow(Assignment).to receive(:find).with(1).and_return(assignment)&lt;br /&gt;
          allow(User).to receive(:exists?).with(name: 'no one').and_return(false)&lt;br /&gt;
          allow(participant).to receive(:set_handle).and_return('handle')&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:exists?).and_return(false)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:set_handle)&lt;br /&gt;
          expect{(AssignmentParticipant.import(row, nil, {}, 1))}.to change { ActionMailer::Base.deliveries.count }.by(1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142021</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142021"/>
		<updated>2021-11-29T23:17:10Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Added test for the assignment_participant_team.rb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context 'when record is empty' do&lt;br /&gt;
      it 'raises an ArgumentError' do&lt;br /&gt;
        expect { AssignmentParticipant.import({}, nil, nil, nil) }.to raise_error(ArgumentError, 'No user id has been specified.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when no user is found by offered username' do&lt;br /&gt;
      context 'when the record has less than 4 items' do&lt;br /&gt;
        it 'raises an ArgumentError' do&lt;br /&gt;
          row = {name: 'no one', fullname: 'no one', email: 'no_one@email.com'}&lt;br /&gt;
          expect(ImportFileHelper).not_to receive(:create_new_user)&lt;br /&gt;
          expect { AssignmentParticipant.import(row, nil, nil, nil) }.to raise_error('The record containing no one does not have enough items.')&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      context 'when new user needs to be created' do&lt;br /&gt;
        let(:row) do&lt;br /&gt;
          {name: 'no one', fullname: 'no one', email: 'name@email.com', role:'user_role_name', parent: 'user_parent_name'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:attributes) do&lt;br /&gt;
          {role_id: 1, name: 'no one', fullname: 'no one', email: 'name@email.com', email_on_submission: 'name@email.com',&lt;br /&gt;
           email_on_review: 'name@email.com', email_on_review_of_review: 'name@email.com'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:test_user) do&lt;br /&gt;
          {name: 'abc', email: 'abcbbc@gmail.com'}&lt;br /&gt;
        end&lt;br /&gt;
        it 'create the user and number of mails sent should be 1' do&lt;br /&gt;
          ActionMailer::Base.deliveries.clear&lt;br /&gt;
          allow(ImportFileHelper).to receive(:define_attributes).with(row).and_return(attributes)&lt;br /&gt;
          allow(ImportFileHelper).to receive(:create_new_user) do&lt;br /&gt;
            test_user = User.new(name: 'abc', fullname: 'abc bbc', email: 'abcbbc@gmail.com')&lt;br /&gt;
            test_user.id = 123&lt;br /&gt;
            test_user.save!&lt;br /&gt;
            test_user&lt;br /&gt;
          end&lt;br /&gt;
          #allow(ImportFileHelper).to receive(:create_new_user).with(attributes, {}).and_return()&lt;br /&gt;
          allow(Assignment).to receive(:find).with(1).and_return(assignment)&lt;br /&gt;
          allow(User).to receive(:exists?).with(name: 'no one').and_return(false)&lt;br /&gt;
          allow(participant).to receive(:set_handle).and_return('handle')&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:exists?).and_return(false)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:set_handle)&lt;br /&gt;
          expect{(AssignmentParticipant.import(row, nil, {}, 1))}.to change { ActionMailer::Base.deliveries.count }.by(1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142020</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142020"/>
		<updated>2021-11-29T23:16:46Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Added test for the assignment_participant_team.rb&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context 'when record is empty' do&lt;br /&gt;
      it 'raises an ArgumentError' do&lt;br /&gt;
        expect { AssignmentParticipant.import({}, nil, nil, nil) }.to raise_error(ArgumentError, 'No user id has been specified.')&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context 'when no user is found by offered username' do&lt;br /&gt;
      context 'when the record has less than 4 items' do&lt;br /&gt;
        it 'raises an ArgumentError' do&lt;br /&gt;
          row = {name: 'no one', fullname: 'no one', email: 'no_one@email.com'}&lt;br /&gt;
          expect(ImportFileHelper).not_to receive(:create_new_user)&lt;br /&gt;
          expect { AssignmentParticipant.import(row, nil, nil, nil) }.to raise_error('The record containing no one does not have enough items.')&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
      context 'when new user needs to be created' do&lt;br /&gt;
        let(:row) do&lt;br /&gt;
          {name: 'no one', fullname: 'no one', email: 'name@email.com', role:'user_role_name', parent: 'user_parent_name'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:attributes) do&lt;br /&gt;
          {role_id: 1, name: 'no one', fullname: 'no one', email: 'name@email.com', email_on_submission: 'name@email.com',&lt;br /&gt;
           email_on_review: 'name@email.com', email_on_review_of_review: 'name@email.com'}&lt;br /&gt;
        end&lt;br /&gt;
        let(:test_user) do&lt;br /&gt;
          {name: 'abc', email: 'abcbbc@gmail.com'}&lt;br /&gt;
        end&lt;br /&gt;
        it 'create the user and number of mails sent should be 1' do&lt;br /&gt;
          ActionMailer::Base.deliveries.clear&lt;br /&gt;
          allow(ImportFileHelper).to receive(:define_attributes).with(row).and_return(attributes)&lt;br /&gt;
          allow(ImportFileHelper).to receive(:create_new_user) do&lt;br /&gt;
            test_user = User.new(name: 'abc', fullname: 'abc bbc', email: 'abcbbc@gmail.com')&lt;br /&gt;
            test_user.id = 123&lt;br /&gt;
            test_user.save!&lt;br /&gt;
            test_user&lt;br /&gt;
          end&lt;br /&gt;
          #allow(ImportFileHelper).to receive(:create_new_user).with(attributes, {}).and_return()&lt;br /&gt;
          allow(Assignment).to receive(:find).with(1).and_return(assignment)&lt;br /&gt;
          allow(User).to receive(:exists?).with(name: 'no one').and_return(false)&lt;br /&gt;
          allow(participant).to receive(:set_handle).and_return('handle')&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:exists?).and_return(false)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
          allow(AssignmentParticipant).to receive(:set_handle)&lt;br /&gt;
          expect{(AssignmentParticipant.import(row, nil, {}, 1))}.to change { ActionMailer::Base.deliveries.count }.by(1)&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142016</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142016"/>
		<updated>2021-11-29T23:15:03Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Test cases for Assignment_team.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    context &amp;quot;when an assignment team does not already exist with the same id&amp;quot; do&lt;br /&gt;
      it &amp;quot;cannot be imported&amp;quot; do&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(nil)&lt;br /&gt;
        error_message = &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + assignment_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot;&lt;br /&gt;
        expect { AssignmentTeam.import([], assignment_id, []) }.&lt;br /&gt;
          to raise_error(ImportError, error_message)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when an assignment team with the same id already exists&amp;quot; do&lt;br /&gt;
      it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
        row = []&lt;br /&gt;
        assignment_id = 1&lt;br /&gt;
        options = []&lt;br /&gt;
        allow(Assignment).to receive(:find_by).with(id: assignment_id).and_return(assignment)&lt;br /&gt;
        allow(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        expect(Team).to receive(:import).with(row, assignment_id, options, instance_of(AssignmentTeam))&lt;br /&gt;
        AssignmentTeam.import(row, assignment_id, options)&lt;br /&gt;
      end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142011</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142011"/>
		<updated>2021-11-29T23:09:30Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in review_response_map_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_team_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;.import&amp;quot; do&lt;br /&gt;
    let(:row) do&lt;br /&gt;
        {teammembers: 'none'}&lt;br /&gt;
    end&lt;br /&gt;
    context &amp;quot;when a course team does not exist with id&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ImportError&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(nil)&lt;br /&gt;
            error_message = &amp;quot;The course with the id \&amp;quot;&amp;quot; + course_id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot;&lt;br /&gt;
            expect { CourseTeam.import(row, nil, course_id, nil) }.&lt;br /&gt;
                to raise_error(ImportError, error_message)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when the course team does not have the required fields&amp;quot; do&lt;br /&gt;
        it &amp;quot;raises ArgumentError&amp;quot; do&lt;br /&gt;
            expect { CourseTeam.import([], nil, 1, nil) }.&lt;br /&gt;
                to raise_error(ArgumentError)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
&lt;br /&gt;
    context &amp;quot;when a course team with the same id already exists&amp;quot; do&lt;br /&gt;
        it &amp;quot;gets imported through Team.import&amp;quot; do&lt;br /&gt;
            course_id = 1&lt;br /&gt;
            options = []&lt;br /&gt;
            allow(Course).to receive(:find).with(course_id).and_return(course)&lt;br /&gt;
            expect(Team).to receive(:import_helper).with(row, course_id, options, instance_of(CourseTeam))&lt;br /&gt;
            CourseTeam.import(row, nil, course_id, options)&lt;br /&gt;
        end&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Newly added rspec test case to test the #import functionality in course_participant_spec.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe &amp;quot;CourseParticipant&amp;quot; do&lt;br /&gt;
  let(:course) { build(:course, id: 1, name: 'ECE517')  }&lt;br /&gt;
  let(:assignment) { build(:assignment, id: 1, name: 'no assignment', participants: [participant], teams: [team])  } &lt;br /&gt;
  let(:assignment_participant) {build(:participant, id: 1)}&lt;br /&gt;
  '''describe &amp;quot;#copy&amp;quot; do&lt;br /&gt;
    #before(:each) do&lt;br /&gt;
    #  byebug&lt;br /&gt;
    #  assignment = build(:assignment)&lt;br /&gt;
    #  course_participant = build(:course_participant)&lt;br /&gt;
    #  @assignment_participant = build(:participant)&lt;br /&gt;
    #end&lt;br /&gt;
    it &amp;quot;create a copy of participant&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:create).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(@assignment.id)).to be_an_instance_of(AssignmentParticipant)&lt;br /&gt;
    end&lt;br /&gt;
    it &amp;quot;returns nil if copy exist&amp;quot; do&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:where).and_return(AssignmentParticipant)&lt;br /&gt;
      allow(AssignmentParticipant).to receive(:first).and_return(participant)&lt;br /&gt;
      allow(assignment_participant).to receive(:set_handle).and_return(true)&lt;br /&gt;
      expect(course_participant.copy(assignment.id)).to be_nil&lt;br /&gt;
    end&lt;br /&gt;
  end'''&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142007</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142007"/>
		<updated>2021-11-29T23:03:49Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* Test Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
Newly added test case to test the functionality in review_response_map.rb&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 it '#import' do&lt;br /&gt;
    expect {ReviewResponseMap.import({reviewee: &amp;quot;name&amp;quot;}, nil, 1)}.to raise_error(ArgumentError, &amp;quot;Record does not contain required items.&amp;quot;)&lt;br /&gt;
    row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: &amp;quot;name1&amp;quot;}&lt;br /&gt;
    #row_hash = {reviewee: &amp;quot;name&amp;quot;, reviewers: [&amp;quot;name1&amp;quot;]}&lt;br /&gt;
    session = nil&lt;br /&gt;
    assignment_id = 1&lt;br /&gt;
    # when reviewee user = nil&lt;br /&gt;
    allow(User).to receive(:find_by).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot;)&lt;br /&gt;
    # when reviewee user exists but reviewee user is not a participant in this assignment&lt;br /&gt;
    allow(User).to receive(:find_by).with(name: &amp;quot;name&amp;quot;).and_return(student)&lt;br /&gt;
    allow(AssignmentParticipant).to receive(:find_by).with(user_id: 1, parent_id: 1).and_return(nil)&lt;br /&gt;
    expect { ReviewResponseMap.import(row_hash, session, 1) }.to raise_error(ArgumentError, &amp;quot;Reviewee user is not a participant in this assignment.&amp;quot;)&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142006</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=142006"/>
		<updated>2021-11-29T22:54:23Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The import functionality is moved into the corresponding models. Previously  code was present in different modules which was confusing now the functionality was all in one place. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
With these new changes to #import functionality. The following modules review_response_map.rb, course_team.rb,assignment_team.rb,assignment_participant.rb have the  &amp;quot;self.import&amp;quot; which will use the import functionality in the ImportFileController and they are made as concise as possible. Each module has specific checks while importing the file, so we can't have same code for all the module.&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141989</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141989"/>
		<updated>2021-11-29T22:00:08Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed and has been made more generalized, many case statements are removed. The ImprortFileController is made so small and understandable. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Assignment Team is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session = nil, id, options)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/assignment/new'&amp;gt;Create&amp;lt;/a&amp;gt; this assignment?&amp;quot; if Assignment.find_by(id: id).nil?&lt;br /&gt;
    Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
     {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                      &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                      &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                      &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Course_team is is also changed to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def self.import(row_hash, session, id, options)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
      raise ImportError, &amp;quot;The course with the id \&amp;quot;&amp;quot; + id.to_s + &amp;quot;\&amp;quot; was not found. &amp;lt;a href='/course/new'&amp;gt;Create&amp;lt;/a&amp;gt; this course?&amp;quot; if Course.find(id).nil?&lt;br /&gt;
      Team.import_helper(row_hash, id, options, prototype)&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
      {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
      {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
      {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                         &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                       &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                       &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Review response map's #import functionality is also updated to use the newly implemented #import functionality.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    reviewee_user_name = row_hash[:reviewee].to_s&lt;br /&gt;
    reviewee_user = User.find_by(name: reviewee_user_name)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Cannot find reviewee user.&amp;quot; unless reviewee_user&lt;br /&gt;
	@@ -57,7 +58,7 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
      team_node = TeamNode.create(parent_id: assignment_id, node_object_id: reviewee_team.id)&lt;br /&gt;
      TeamUserNode.create(parent_id: team_node.id, node_object_id: t_user.id)&lt;br /&gt;
    end&lt;br /&gt;
    row_hash[:reviewers].split.each do |reviewer|&lt;br /&gt;
      reviewer_user_name = reviewer.to_s&lt;br /&gt;
      reviewer_user = User.find_by(name: reviewer_user_name)&lt;br /&gt;
      raise ArgumentError, &amp;quot;Cannot find reviewer user.&amp;quot; unless reviewer_user&lt;br /&gt;
	@@ -71,6 +72,20 @@ def self.import(row_hash, _session, assignment_id)&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141985</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141985"/>
		<updated>2021-11-29T21:52:32Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The ImprortFileController is made so small and understandable. Many case statements are removed. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141981</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141981"/>
		<updated>2021-11-29T21:49:22Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The ImprortFileController is made so small and understandable. Many case statements are removed. The following code changes are follows:&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*Assignment Participant is changed to use the newly implemented #import functionality. Changes are as shown below:&lt;br /&gt;
&lt;br /&gt;
  def self.import(row_hash, session, id)&lt;br /&gt;
    raise ArgumentError, &amp;quot;Record does not contain required items.&amp;quot; if row_hash.length &amp;lt; self.required_import_fields.length&lt;br /&gt;
    user = User.find_by(name: row_hash[:name])&lt;br /&gt;
    user = User.import(row_hash, session, nil) if user.nil?&lt;br /&gt;
    raise ImportError, &amp;quot;The assignment with id #{id} was not found.&amp;quot; if Assignment.find(id).nil?&lt;br /&gt;
    unless AssignmentParticipant.exists?(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part = AssignmentParticipant.new(user_id: user.id, parent_id: id)&lt;br /&gt;
      new_part.set_handle&lt;br /&gt;
    end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.required_import_fields&lt;br /&gt;
    {&amp;quot;name&amp;quot; =&amp;gt; &amp;quot;Name&amp;quot;,&lt;br /&gt;
     &amp;quot;fullname&amp;quot; =&amp;gt; &amp;quot;Full Name&amp;quot;,&lt;br /&gt;
     &amp;quot;email&amp;quot; =&amp;gt; &amp;quot;Email&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141959</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141959"/>
		<updated>2021-11-29T21:36:51Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The ImprortFileController is made so small and understandable. Many case statements are removed.&lt;br /&gt;
&lt;br /&gt;
*Previous Implmentation&lt;br /&gt;
&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*Current Implementation&lt;br /&gt;
&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141957</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141957"/>
		<updated>2021-11-29T21:34:47Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141956</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141956"/>
		<updated>2021-11-29T21:33:51Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
def import_from_hash(session, params)&lt;br /&gt;
    if params[:model] == &amp;quot;AssignmentTeam&amp;quot; or params[:model] == &amp;quot;CourseTeam&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          if params[:model] == &amp;quot;AssignmentTeam&amp;quot;&lt;br /&gt;
            teamtype = AssignmentTeam&lt;br /&gt;
          else&lt;br /&gt;
            teamtype = CourseTeam&lt;br /&gt;
          end&lt;br /&gt;
          options = eval(params[:options])&lt;br /&gt;
          options[:has_teamname] = params[:has_teamname]&lt;br /&gt;
          Team.import(row_hash, params[:id], options, teamtype)&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
      elsif params[:model] == &amp;quot;ReviewResponseMap&amp;quot;&lt;br /&gt;
        contents_hash = eval(params[:contents_hash])&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
        errors = []&lt;br /&gt;
        begin&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            ReviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        rescue&lt;br /&gt;
          errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
        end&lt;br /&gt;
    elsif params[:model] == &amp;quot;MetareviewResponseMap&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          MetareviewResponseMap.import(row_hash,session,params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'SignUpTopic' || params[:model] == 'SignUpSheet'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        if params[:optional_count] == '0'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '1'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '2'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        elsif params[:optional_count] == '3'&lt;br /&gt;
          new_header = [params[:select1], params[:select2], params[:select3], params[:select4], params[:select5], params[:select6]]&lt;br /&gt;
          @header_integrated_body = hash_rows_with_headers(new_header,contents_hash[:body])&lt;br /&gt;
        end&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          session[:assignment_id] = params[:id]&lt;br /&gt;
          Object.const_get(params[:model]).import(row_hash, session, params[:id])&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    elsif params[:model] == 'AssignmentParticipant' || params[:model] == 'CourseParticipant'&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header], contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3], params[:select4]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        if params[:model] == 'AssignmentParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            AssignmentParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        elsif params[:model] == 'CourseParticipant'&lt;br /&gt;
          @header_integrated_body.each do |row_hash|&lt;br /&gt;
            CourseParticipant.import(row_hash, session, params[:id])&lt;br /&gt;
          end&lt;br /&gt;
        end&lt;br /&gt;
      rescue&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
    else # params[:model] = &amp;quot;User&amp;quot;&lt;br /&gt;
      contents_hash = eval(params[:contents_hash])&lt;br /&gt;
      if params[:has_header] == 'true'&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(contents_hash[:header],contents_hash[:body])&lt;br /&gt;
      else&lt;br /&gt;
        new_header = [params[:select1], params[:select2], params[:select3]]&lt;br /&gt;
        @header_integrated_body = hash_rows_with_headers(new_header, contents_hash[:body])&lt;br /&gt;
      end&lt;br /&gt;
      errors = []&lt;br /&gt;
      begin&lt;br /&gt;
        @header_integrated_body.each do |row_hash|&lt;br /&gt;
          User.import(row_hash, nil, session)&lt;br /&gt;
        end&lt;br /&gt;
      rescue StandardError&lt;br /&gt;
        errors &amp;lt;&amp;lt; $ERROR_INFO&lt;br /&gt;
      end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141952</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141952"/>
		<updated>2021-11-29T21:32:19Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141951</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141951"/>
		<updated>2021-11-29T21:31:59Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
== &lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
 ==&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141950</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141950"/>
		<updated>2021-11-29T21:31:38Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
== &lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + &amp;lt;li&amp;gt; + error.to_s + &amp;lt;li&amp;gt;&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
 ==&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141949</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141949"/>
		<updated>2021-11-29T21:31:27Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
== &lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + &amp;lt;li&amp;gt; + error.to_s + &amp;lt;/li&amp;gt;&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
 ==&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141947</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141947"/>
		<updated>2021-11-29T21:30:43Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
== &lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s &lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;/ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
 ==&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141946</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141946"/>
		<updated>2021-11-29T21:30:27Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
== &lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s &lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;lt;\ul&amp;gt;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
 ==&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141944</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141944"/>
		<updated>2021-11-29T21:29:54Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
== &lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.Other records may have been added. A second submission will not duplicate these records.&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s &lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
 ==&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141943</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141943"/>
		<updated>2021-11-29T21:29:15Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
== &lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.&amp;lt;br/&amp;gt;Other records may have been added. A second submission will not duplicate these records.&amp;lt;br/&amp;gt;&amp;lt;ul&amp;gt;&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + error.to_s &lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot; &lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
 ==&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141941</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141941"/>
		<updated>2021-11-29T21:28:51Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
== &lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.&amp;lt;br/&amp;gt;Other records may have been added. A second submission will not duplicate these records.&amp;lt;br/&amp;gt;&amp;lt;ul&amp;gt;&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + \&amp;quot;&amp;lt;li&amp;gt;\&amp;quot; + error.to_s + \&amp;quot;&amp;lt;br/&amp;gt;\&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot; &lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
 ==&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141939</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141939"/>
		<updated>2021-11-29T21:27:46Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
== &lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.&amp;lt;br/&amp;gt;Other records may have been added. A second submission will not duplicate these records.&amp;lt;br/&amp;gt;&amp;lt;ul&amp;gt;&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + &amp;quot;&amp;lt;li&amp;gt;&amp;quot; + error.to_s + &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot; &lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
 ==&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141938</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141938"/>
		<updated>2021-11-29T21:26:34Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &lt;br /&gt;
&lt;br /&gt;
  def import&lt;br /&gt;
    &lt;br /&gt;
== errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.&amp;lt;br/&amp;gt;Other records may have been added. A second submission will not duplicate these records.&amp;lt;br/&amp;gt;&amp;lt;ul&amp;gt;&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + &amp;quot;&amp;lt;li&amp;gt;&amp;quot; + error.to_s + &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot; &lt;br /&gt;
==&lt;br /&gt;
&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
 ==&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141937</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141937"/>
		<updated>2021-11-29T21:25:50Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== &lt;br /&gt;
&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.&amp;lt;br/&amp;gt;Other records may have been added. A second submission will not duplicate these records.&amp;lt;br/&amp;gt;&amp;lt;ul&amp;gt;&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + &amp;quot;&amp;lt;li&amp;gt;&amp;quot; + error.to_s + &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
 ==&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141936</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141936"/>
		<updated>2021-11-29T21:25:17Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been Implemented */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.&amp;lt;br/&amp;gt;Other records may have been added. A second submission will not duplicate these records.&amp;lt;br/&amp;gt;&amp;lt;ul&amp;gt;&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + &amp;quot;&amp;lt;li&amp;gt;&amp;quot; + error.to_s + &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141934</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141934"/>
		<updated>2021-11-29T21:24:12Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* Implementation Details */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been Implemented==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
  def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.&amp;lt;br/&amp;gt;Other records may have been added. A second submission will not duplicate these records.&amp;lt;br/&amp;gt;&amp;lt;ul&amp;gt;&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + &amp;quot;&amp;lt;li&amp;gt;&amp;quot; + error.to_s + &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141933</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141933"/>
		<updated>2021-11-29T21:23:33Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* What has been done */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== Implementation Details==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
*ImportFileController is changed to implement new #import functionality. The previous code used several If and Else cases to &lt;br /&gt;
&lt;br /&gt;
  [[def import&lt;br /&gt;
    errors = import_from_hash(session, params)&lt;br /&gt;
    err_msg = &amp;quot;The following errors were encountered during import.&amp;lt;br/&amp;gt;Other records may have been added. A second submission will not duplicate these records.&amp;lt;br/&amp;gt;&amp;lt;ul&amp;gt;&amp;quot;&lt;br /&gt;
    errors.each do |error|&lt;br /&gt;
      err_msg = err_msg + &amp;quot;&amp;lt;li&amp;gt;&amp;quot; + error.to_s + &amp;quot;&amp;lt;br/&amp;gt;&amp;quot;&lt;br /&gt;
    end&lt;br /&gt;
    err_msg += &amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;&lt;br /&gt;
    if errors.empty?&lt;br /&gt;
      ExpertizaLogger.info LoggerMessage.new(controller_name, session[:user].name, &amp;quot;The file has been successfully imported.&amp;quot;, request)&lt;br /&gt;
      undo_link(&amp;quot;The file has been successfully imported.&amp;quot;)&lt;br /&gt;
    else&lt;br /&gt;
      ExpertizaLogger.error LoggerMessage.new(controller_name, session[:user].name, err_msg, request)&lt;br /&gt;
      flash[:error] = err_msg&lt;br /&gt;
    end&lt;br /&gt;
    redirect_to session[:return_to]&lt;br /&gt;
  end&lt;br /&gt;
]]&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141914</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=141914"/>
		<updated>2021-11-29T21:07:19Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
The export functionality is however is already quite refactored to suit the strategy pattern, which is not changed by the previous group as well.&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
== What has been done==&lt;br /&gt;
=== Models ===&lt;br /&gt;
*In the shared.js file there was a method #checkIfUserColumnDuplicate() which we changed to #checkForDuplicates(field_count). This method now checks to make sure that each column header is unique regardless of which model you are in.&lt;br /&gt;
&lt;br /&gt;
*In the ImportFileController we moved the #start method to the beginning of the file for consistency and readability. &lt;br /&gt;
&lt;br /&gt;
*The #import_from_hash and the #show method inside the ImportFileController is markedly shorter now then when we found it. We removed most of the case statements. The below images are in the respective order. &lt;br /&gt;
&lt;br /&gt;
[[Image: importmethod.png]]&lt;br /&gt;
&lt;br /&gt;
[[Image: showmethod.png]]&lt;br /&gt;
&lt;br /&gt;
*Questionnaire.rb has a #import method that is now routed through the ImportFileController and the actual act of importing works. We removed all functionality related to importing from the QuestionnaireController and QuestionnaireHelper. &lt;br /&gt;
&lt;br /&gt;
*We removed the ImportFileHelper and ImportTopicsHelper and moved that functionality into the corresponding models. Having the code segmented made things confusing since none of the functionality was all in one place.   &lt;br /&gt;
&lt;br /&gt;
*We removed import functionality from '''question.rb''' because there is no way to import a question out of context from a questionnaire. Importing a questionnaire, means importing questions to fill that questionnaire. &lt;br /&gt;
&lt;br /&gt;
*SignUpSheet no longer has an import method. That functionality was never used and does not actually currently work in the production version of Expertiza. After discussing with our mentor, we were instructed to removed the import link on the front-end, the import method, and all related tests. &lt;br /&gt;
&lt;br /&gt;
*The previous way of determining required import fields were to populate the @expected_fields variable which was incredibly hard to find in the code. We have eliminated the need for that variable and have removed all instances of it. &lt;br /&gt;
&lt;br /&gt;
*We have made things as generic as possible in the .html.erb files so that you can be in any model and the code works on the front end seamlessly. This is done by adding these three methods to each model that has an import method:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 def self.required_import_fields&lt;br /&gt;
    {&amp;quot;teammembers&amp;quot; =&amp;gt; &amp;quot;Team Members&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.optional_import_fields(id=nil)&lt;br /&gt;
    {&amp;quot;teamname&amp;quot; =&amp;gt; &amp;quot;Team Name&amp;quot;}&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  def self.import_options&lt;br /&gt;
    {&amp;quot;handle_dups&amp;quot; =&amp;gt; {&amp;quot;display&amp;quot; =&amp;gt; &amp;quot;Handle Duplicates&amp;quot;,&lt;br /&gt;
                       &amp;quot;options&amp;quot; =&amp;gt; {&amp;quot;ignore&amp;quot; =&amp;gt; &amp;quot;Ignore new team name&amp;quot;,&lt;br /&gt;
                                     &amp;quot;replace&amp;quot; =&amp;gt; &amp;quot;Replace the existing team with the new team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;insert&amp;quot; =&amp;gt; &amp;quot;Insert any new team members into the existing team&amp;quot;,&lt;br /&gt;
                                     &amp;quot;rename&amp;quot; =&amp;gt; &amp;quot;Rename the new team and import&amp;quot;}}}&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The above content is specific to the course_team.rb but the three method names are consistent to all the models and are called on the front end. &lt;br /&gt;
&lt;br /&gt;
*The models that have a &amp;quot;self.import&amp;quot; method that does not branch out into other controllers beside ImportFileController will be looked at to make sure they are as concise as possible. None of them can be all the same because they all need to have checks specific to what they need to import. We can, however, make sure that similar models, like assignment_team and course_team, have similar imports. That is what we have done for assignment_team/course_team and assignment_participant/course_participant.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now, these are the final models/places where a user may import a file into Expertiza:&lt;br /&gt;
&lt;br /&gt;
- assignment_participant&lt;br /&gt;
&lt;br /&gt;
- assignment_team&lt;br /&gt;
&lt;br /&gt;
- course_participant&lt;br /&gt;
&lt;br /&gt;
- course_team&lt;br /&gt;
&lt;br /&gt;
- review_response_map&lt;br /&gt;
&lt;br /&gt;
- metareview_response_map&lt;br /&gt;
&lt;br /&gt;
- sign_up_topic&lt;br /&gt;
&lt;br /&gt;
- user&lt;br /&gt;
&lt;br /&gt;
- questionnaire&lt;br /&gt;
&lt;br /&gt;
=== Views ===&lt;br /&gt;
&lt;br /&gt;
*'''start.html.erb''' file has no more case statements by the @model type and everything has become generic.&lt;br /&gt;
&lt;br /&gt;
*We removed all the partials related to import_file. Which has lead the '''show.html.erb''' file to have no more case statements and everything has become generic. The removed files are:&lt;br /&gt;
&lt;br /&gt;
- _metareviewer.html.erb&lt;br /&gt;
&lt;br /&gt;
- _participant.html.erb&lt;br /&gt;
&lt;br /&gt;
- _reviewer.html.erb&lt;br /&gt;
&lt;br /&gt;
- _sign_up_topic.html.erb&lt;br /&gt;
&lt;br /&gt;
- _team.html.erb&lt;br /&gt;
&lt;br /&gt;
- _user.html.erb&lt;br /&gt;
&lt;br /&gt;
*We have updated some text on the front end related to questionnaire. The import link did not match the capitalization format in the rest Expertiza.&lt;br /&gt;
&lt;br /&gt;
=== Code Climate ===&lt;br /&gt;
'''Note: Code Climate was not running on Expertiza's beta branch for the last four days of the project. We have done the best we could to removed extraneous lines and white spaces.''' &lt;br /&gt;
&lt;br /&gt;
There were many code climate issues in reference to our project. We have managed to fix 46 issues as a byproduct of refactoring the code. Here is a list of them with their frequency put in parenthesis': &lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has a Cognitive Complexity of 62 (exceeds 5 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Method get_questions_from_csv has 41 lines of code (exceeds 25 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* File import_file_controller.rb has 278 lines of code (exceeds 250 allowed). Consider refactoring.&lt;br /&gt;
&lt;br /&gt;
* Avoid deeply nested control flow statements. (3)&lt;br /&gt;
&lt;br /&gt;
* Similar blocks of code found in 2 locations. Consider refactoring. (2)&lt;br /&gt;
&lt;br /&gt;
* Unescaped parameter value&lt;br /&gt;
&lt;br /&gt;
* Useless assignment to variable - a.&lt;br /&gt;
&lt;br /&gt;
* Cyclomatic complexity for get_questions_from_csv is too high. [18/6]&lt;br /&gt;
&lt;br /&gt;
* Assignment Branch Condition size for get_questions_from_csv is too high. [43.3/15]&lt;br /&gt;
&lt;br /&gt;
* Block has too many lines. [36/25]&lt;br /&gt;
&lt;br /&gt;
* Avoid more than 3 levels of block nesting. (3)&lt;br /&gt;
&lt;br /&gt;
* Perceived complexity for get_questions_from_csv is too high. [16/7]&lt;br /&gt;
&lt;br /&gt;
* Align elsif with if.&lt;br /&gt;
&lt;br /&gt;
* Space missing after comma. (13)&lt;br /&gt;
&lt;br /&gt;
* Line is too long. [172/160]&lt;br /&gt;
&lt;br /&gt;
* Method has too many lines. [108/60]&lt;br /&gt;
&lt;br /&gt;
* Use the return of the conditional for variable assignment and comparison.&lt;br /&gt;
&lt;br /&gt;
* Move @optional_count = 0 out of the conditional. (2)&lt;br /&gt;
&lt;br /&gt;
* Move contents_hash = eval(params[:contents_hash]) out of the conditional. (6)&lt;br /&gt;
&lt;br /&gt;
* Convert if nested inside else to elsif.&lt;br /&gt;
&lt;br /&gt;
* Don't use parentheses around the condition of an if. (3)&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;br /&gt;
The import method has been implemented in a bunch of models which have been listed above. After preliminary analysis, we assume that the import functionality in a few of the models might have to be amended. These models are:&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
We will update the test plan for those models in which the import code is amended to fit into the new framework. If we amend the import code in any other model, we will also update the tests in their respective .spec files to ensure 100% coverage. We also plan to create a spec file for the '''import_file_controller'''.&lt;br /&gt;
&lt;br /&gt;
Scenarios:&lt;br /&gt;
   1. when assignment found and assignment participant does not exist, creates a new user and participant&lt;br /&gt;
   2. when assignment cannot be found, creates a new user then raises an ImportError&lt;br /&gt;
   3. when the assignment team does not have the required fields, raises ArgumentError&lt;br /&gt;
   4. check what import/export actions are allowed for admin, instructor, TA, student&lt;br /&gt;
   5. when course found and course participant does not exist, creates a new user and participant&lt;br /&gt;
   6. when the course team does not have the required fields, raises ArgumentError&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2162._Further_refactoring_and_improvement_of_review_mapping_helper&amp;diff=140619</id>
		<title>CSC/ECE 517 Fall 2021 - E2162. Further refactoring and improvement of review mapping helper</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2162._Further_refactoring_and_improvement_of_review_mapping_helper&amp;diff=140619"/>
		<updated>2021-11-03T16:03:55Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
This page gives a description of the changes made for the ''review_mapping_helper.rb'' of Expertiza based OSS project.&lt;br /&gt;
&lt;br /&gt;
===Expertiza Background ===&lt;br /&gt;
Expertiza is a web application where students can submit and peer-review learning objects (articles, codes, websites, etc). Instructors add and grade the assignments submitted by students to Expertiza. Students can be assigned in teams based on their selection of the topics. It has functionalities such as peer reviews in which students can provide feedback on other's work which helps peer in better developing the project. It is supported by the National Science Foundation.&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
&lt;br /&gt;
The review_mapping_helper.rb  has multiple functions with a wrong naming convention, they are classic examples of bad function names. These functions are to be refactored accordingly. Few of the variables names used in the method must also be refactored to make it more relevant and understandable. In addition, some function's code is to be optimized to ensure that it follows DRY principle. Tests must be written for the functions which are modified. Also, comments must be added to all functions of the file. The create_report_table_header function contains HTML code which should be ideally placed in the view as partials, which will allow to easily reuse the code in Rails application.&lt;br /&gt;
&lt;br /&gt;
==Implementation==&lt;br /&gt;
&lt;br /&gt;
===Flowchart===&lt;br /&gt;
&lt;br /&gt;
The following process is carried out to complete the project-&lt;br /&gt;
&lt;br /&gt;
                                                                           [[File:Steps for project.png]]&lt;br /&gt;
&lt;br /&gt;
===Refactoring===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Refactoring method names====&lt;br /&gt;
All the method names that are refactored are mentioned below-&lt;br /&gt;
&lt;br /&gt;
'''Before:''' &lt;br /&gt;
&lt;br /&gt;
  get_each_round_review_and_feedback_response_map_for_feedback_report&lt;br /&gt;
&lt;br /&gt;
'''After:'''&lt;br /&gt;
&lt;br /&gt;
  get_each_review_and_feedback_response_map     ''[Since the method and file is already about feedback report, we do not need to put feedback_report in the method name]''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Before:'''  &lt;br /&gt;
&lt;br /&gt;
  get_certain_round_review_and_feedback_response_map_for_feedback_report&lt;br /&gt;
&lt;br /&gt;
'''After:'''&lt;br /&gt;
&lt;br /&gt;
  get_certain_review_and_feedback_response_map   ''[Since the method and file is already about feedback report, we do not need to put feedback_report in the method name]''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Before:''' &lt;br /&gt;
&lt;br /&gt;
  get_team_name_color_in_review_report&lt;br /&gt;
&lt;br /&gt;
'''After:'''&lt;br /&gt;
&lt;br /&gt;
  get_team_colour   ''[The method suggests that team color is defined in the review report, so we do not need to put in_review_report in the method name]''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Before:''' &lt;br /&gt;
&lt;br /&gt;
  get_each_round_score_awarded_for_review_report&lt;br /&gt;
&lt;br /&gt;
'''After:'''&lt;br /&gt;
&lt;br /&gt;
  get_awarded_review_score   ''[The method gets the awarded score for review, so we do not need to put for_review_report in the method name]''&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Before:''' &lt;br /&gt;
&lt;br /&gt;
  get_min_max_avg_value_for_review_report&lt;br /&gt;
&lt;br /&gt;
'''After:'''&lt;br /&gt;
&lt;br /&gt;
  get_review_metrics   ''[Since minimum, maximum and average value is the part of metrics in review report, we can replace it all by metrics]'' &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====Refactoring method====&lt;br /&gt;
&lt;br /&gt;
The following method was not used or called in any of the files in the project. Due to that reason, the entire function was commented out.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
def get_current_round(reviewer_id)&lt;br /&gt;
    user_id = Participant.find(reviewer_id).user.id&lt;br /&gt;
    topic_id = SignedUpTeam.topic_id(@assignment.id, user_id)&lt;br /&gt;
    @assignment.number_of_current_round(topic_id)&lt;br /&gt;
    @assignment.num_review_rounds if @assignment.get_current_stage(topic_id) == &amp;quot;Finished&amp;quot; || @assignment.get_current_stage(topic_id) == &amp;quot;metareview&amp;quot;&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Comments added to various functions===&lt;br /&gt;
There were several methods which didn't have any comments or the comments weren't meaningful. In all those cases, comments have been added or changed which are mentioned as follows:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # gets the review score awarded based on each round of the review&lt;br /&gt;
  def get_each_round_score_awarded_for_review_report(reviewer_id, team_id)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # gets minimum, maximum and average value for all the reviews&lt;br /&gt;
  def get_min_max_avg_value_for_review_report(round, team_id)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # sorts the reviewers by the average volume of reviews in each round, in descending order&lt;br /&gt;
  def sort_reviewer_by_review_volume_desc&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # displays all the average scores in round 1, 2 and 3&lt;br /&gt;
  def display_volume_metric(overall_avg_vol, avg_vol_in_round_1, avg_vol_in_round_2, avg_vol_in_round_3)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # moves data of reviews in each round from a current round&lt;br /&gt;
  def initialize_chart_elements(reviewer)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # The data of all the reviews is displayed in the form of a bar chart&lt;br /&gt;
  def display_volume_metric_chart(reviewer)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # For assignments with 1 team member, the following method returns user's fullname else it returns &amp;quot;team name&amp;quot; that a particular reviewee belongs to.&lt;br /&gt;
  def get_team_reviewed_link_name(max_team_size, response, reviewee_id) &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # if the current stage is &amp;quot;submission&amp;quot; or &amp;quot;review&amp;quot;, function returns the current round number otherwise &lt;br /&gt;
  # if the current stage is &amp;quot;Finished&amp;quot; or &amp;quot;metareview&amp;quot;, function returns the number of rounds of review completed&lt;br /&gt;
  def get_current_round_for_review_report(reviewer_id)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # gets the response map data such as reviewer id, reviewd object id and type for the review report&lt;br /&gt;
  def get_data_for_review_report(reviewed_object_id, reviewer_id, type)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # gets the team name's color according to review and assignment submission status&lt;br /&gt;
  def get_team_name_color_in_review_report(response_map)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # checks if a review was submitted in every round and gives the total responses count&lt;br /&gt;
  def response_for_each_round?(response_map)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  # returns hyperlink of the assignment that has been submitted on the due date&lt;br /&gt;
  def submitted_hyperlink(round, response_map, assignment_created, assignment_due_dates)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Function placed in the view as partials=== &lt;br /&gt;
&lt;br /&gt;
The following method has been removed from the '''review_mapping_helper.rb''' and added to the views as partials:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  def create_report_table_header(headers = {})&lt;br /&gt;
    table_header = &amp;quot;&amp;lt;div class = 'reviewreport'&amp;gt;\&lt;br /&gt;
                    &amp;lt;table width='100% cellspacing='0' cellpadding='2' border='0' class='table table-striped'&amp;gt;\&lt;br /&gt;
                    &amp;lt;tr bgcolor='#CCCCCC'&amp;gt;&amp;quot;&lt;br /&gt;
    headers.each do |header, percentage|&lt;br /&gt;
      table_header += if percentage&lt;br /&gt;
                        &amp;quot;&amp;lt;th width = #{percentage}&amp;gt;\&lt;br /&gt;
                        #{header.humanize}\&lt;br /&gt;
                                        &amp;lt;/th&amp;gt;&amp;quot;&lt;br /&gt;
                      else&lt;br /&gt;
                        &amp;quot;&amp;lt;th&amp;gt;\&lt;br /&gt;
                        #{header.humanize}\&lt;br /&gt;
                                        &amp;lt;/th&amp;gt;&amp;quot;&lt;br /&gt;
                      end&lt;br /&gt;
    end&lt;br /&gt;
    table_header += &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;&lt;br /&gt;
    table_header.html_safe&lt;br /&gt;
  end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The following is the code available in views in the file '''_report_table_header.html.erb''':&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;% table_header = &amp;quot;&amp;lt;div class = 'reviewreport'&amp;gt;&amp;lt;table width='100% cellspacing='0' cellpadding='2' border='0' class='table table-striped'&amp;gt;&amp;lt;tr bgcolor='#CCCCCC'&amp;gt;&amp;quot; %&amp;gt;&lt;br /&gt;
 &amp;lt;%  headers.each do |header, percentage| %&amp;gt;&lt;br /&gt;
 &amp;lt;% table_header += if percentage %&amp;gt;&lt;br /&gt;
   &amp;lt;% &amp;quot;&amp;lt;th width = #{percentage}&amp;gt; #{header.humanize} &amp;lt;/th&amp;gt;&amp;quot; %&amp;gt;&lt;br /&gt;
 &amp;lt;% else %&amp;gt;&lt;br /&gt;
   &amp;lt;%&amp;quot;&amp;lt;th&amp;gt; #{header.humanize} &amp;lt;/th&amp;gt;&amp;quot; %&amp;gt;&lt;br /&gt;
 &amp;lt;% end %&amp;gt;&lt;br /&gt;
&amp;lt;% end %&amp;gt;&lt;br /&gt;
&amp;lt;%  table_header += &amp;quot;&amp;lt;/tr&amp;gt;&amp;quot;%&amp;gt;&lt;br /&gt;
&amp;lt;% return table_header.html_safe %&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Testing==&lt;br /&gt;
&lt;br /&gt;
===Manual Testing===&lt;br /&gt;
&lt;br /&gt;
''To be carried out''&lt;br /&gt;
&lt;br /&gt;
===RSpec Testing===&lt;br /&gt;
&lt;br /&gt;
RSpec testing was carried out for the methods that had been refactored. Given below are the tests which are written for the functions that are modified:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
describe 'My Test Cases' do&lt;br /&gt;
  before(:each) do&lt;br /&gt;
    create(:instructor)&lt;br /&gt;
    create(:role_of_student)&lt;br /&gt;
    login_as(&amp;quot;instructor6&amp;quot;)&lt;br /&gt;
    visit '/tree_display/list'&lt;br /&gt;
    click_link 'View reports'&lt;br /&gt;
    expect(page).to have_current_path('/reports/response_report')&lt;br /&gt;
    click_link 'View'&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;can display review metrics&amp;quot;, js: true do&lt;br /&gt;
    expect(page).to have_content('Metrics')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;can display review grades of each round&amp;quot;, js: true do&lt;br /&gt;
    expect(page).to have_content('Score awarded')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;can display review summary&amp;quot;, js: true do&lt;br /&gt;
    expect(page).to have_content('Reviews done')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;can display review summary&amp;quot;, js: true do&lt;br /&gt;
    expect(page).to have_content('Reviewer')&lt;br /&gt;
  end&lt;br /&gt;
&lt;br /&gt;
  it &amp;quot;can display review summary&amp;quot;, js: true do&lt;br /&gt;
    expect(page).to have_content('Team reviewed')&lt;br /&gt;
  end&lt;br /&gt;
end&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Design Pattern==&lt;br /&gt;
&lt;br /&gt;
A design pattern is a general repeatable solution to a commonly occurring problem in software design. It is a description or template for how to solve a problem that can be used in many different situations.&lt;br /&gt;
&lt;br /&gt;
During the process of refactoring methods as well as method names,'''Strategy Pattern''' was used in the implementation. The Strategy pattern is most useful when you want to provide multiple ways of processing a request, without hard-coding knowledge about those different methods into the object that handles the request.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
1) https://www.rubyguides.com/2015/12/ruby-refactoring/&lt;br /&gt;
&lt;br /&gt;
2) http://rspec.info/documentation/3.8/rspec-core/&lt;br /&gt;
&lt;br /&gt;
3) http://wiki.expertiza.ncsu.edu/index.php/Main_Page&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Team Members==&lt;br /&gt;
&lt;br /&gt;
Ashish Kumar Jayantilal Jain&lt;br /&gt;
&lt;br /&gt;
Aishwarya Tirumala&lt;br /&gt;
&lt;br /&gt;
Devang Upadhyay&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2162._Further_refactoring_and_improvement_of_review_mapping_helper&amp;diff=140618</id>
		<title>CSC/ECE 517 Fall 2021 - E2162. Further refactoring and improvement of review mapping helper</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2162._Further_refactoring_and_improvement_of_review_mapping_helper&amp;diff=140618"/>
		<updated>2021-11-03T16:02:30Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
This page gives a description of the changes made for the ''review_mapping_helper.rb'' of Expertiza based OSS project.&lt;br /&gt;
&lt;br /&gt;
===Expertiza Background ===&lt;br /&gt;
Expertiza is a web application where students can submit and peer-review learning objects (articles, codes, websites, etc). Instructors add and grade the assignments submitted by students to Expertiza. Students can be assigned in teams based on their selection of the topics. It has functionalities such as peer reviews in which students can provide feedback on other's work which helps peer in better developing the project. It is supported by the National Science Foundation.&lt;br /&gt;
&lt;br /&gt;
=== Problem Statement ===&lt;br /&gt;
&lt;br /&gt;
The review_mapping_helper.rb  has multiple functions with a wrong naming convention, they are classic examples of bad function names. These functions are to be refactored accordingly. Few of the variables names used in the method must also be refactored to make it more relevant and understandable. In addition, some function's code is to be optimized to ensure that it follows DRY principle. Tests must be written for the functions which are modified. Also, comments must be added to all functions of the file. The create_report_table_header function contains HTML code which should be ideally placed in the view as partials, which will allow to easily reuse the code in Rails application.&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140588</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140588"/>
		<updated>2021-11-03T04:48:16Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;=== Team ===&lt;br /&gt;
*Akash Sarda, aksarda&lt;br /&gt;
*Sairam Sakhamuri, svsakham&lt;br /&gt;
*Sidhant Arora, sarora22&lt;br /&gt;
*Sai Harsha Nadendla, snadend2&lt;br /&gt;
&lt;br /&gt;
== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140587</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140587"/>
		<updated>2021-11-03T04:44:30Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* Design Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140586</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140586"/>
		<updated>2021-11-03T04:43:47Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* Design Plan */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
Visually this is what is happening right now (with some files missing for brevity's sake):&lt;br /&gt;
&lt;br /&gt;
[[Image: badUML.png]]&lt;br /&gt;
&lt;br /&gt;
What we want it to look like:&lt;br /&gt;
&lt;br /&gt;
[[Image: DesignImport.PNG]]&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=File:DesignImport.PNG&amp;diff=140585</id>
		<title>File:DesignImport.PNG</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=File:DesignImport.PNG&amp;diff=140585"/>
		<updated>2021-11-03T04:43:14Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140579</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140579"/>
		<updated>2021-11-03T04:17:34Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
Visually this is what is happening right now (with some files missing for brevity's sake):&lt;br /&gt;
&lt;br /&gt;
[[Image: badUML.png]]&lt;br /&gt;
&lt;br /&gt;
What we want it to look like:&lt;br /&gt;
&lt;br /&gt;
[[Image: goodUML.png]]&lt;br /&gt;
&lt;br /&gt;
== Test Plan ==&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140578</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140578"/>
		<updated>2021-11-03T04:16:14Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* Proposed Import Changes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Design Plan ==&lt;br /&gt;
&lt;br /&gt;
The design plan is to use Strategy patter to implement import functionality, so that all the import requests present in different models are routed through the ImportFileController. Right now since the import functionality is implemented in various model methods this leads to many if and else statements to check the type of model. So, we intent to generalize the import functionality by placing common code in the ImportFileController. But each model will still have its own import method. With this approach the redundancy is reduced by moving common code to ImportFileController and code will become DRY.&lt;br /&gt;
&lt;br /&gt;
Other helper methods such as ImportFileHelper and ImportTopicHelper that are used to perform import functionality will also be removed, which keeps import functionality consistent. We will be using method overloading and overriding for the methods in ImportFileController to eliminate unnecessary if and else blocks.&lt;br /&gt;
&lt;br /&gt;
To summarize our plan of changes:&lt;br /&gt;
&lt;br /&gt;
* Redirect all import calls through ImportFileController&lt;br /&gt;
* Refactor ImportFileController by removing the redundant code and making code generic.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
&lt;br /&gt;
Visually this is what is happening right now (with some files missing for brevity's sake):&lt;br /&gt;
&lt;br /&gt;
[[Image: badUML.png]]&lt;br /&gt;
&lt;br /&gt;
What we want it to look like:&lt;br /&gt;
&lt;br /&gt;
[[Image: goodUML.png]]&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140572</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140572"/>
		<updated>2021-11-03T03:44:29Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;br /&gt;
&lt;br /&gt;
== Proposed Import Changes ==&lt;br /&gt;
&lt;br /&gt;
The import functionality should be routed through the ImportFileController. Everything should still work as it does now after our changes; we are cleaning up code and may move functionality to a different location but nothing will get removed permanently. Luckily, most import functionality is routed through the ImportFileController and import class methods on models that are being imported. It makes sense for each model to have its own import method because each model knows what to expect for itself. Making things overly generic will end up contradicting the DRY principle and lead to many nested if statements. &lt;br /&gt;
&lt;br /&gt;
We intend to remove other helpers and files, such as the ImportFileHelper, the ImportTopicHelper, and the QuestionnaireHelper to keep import routing consistent. We will also remove the QuestionnaireController and route that import through the ImportFileController. In addition, we will update ImportFileController methods (in particular, #hash_rows_with_headers and #import_from_hash) to make better use of polymorphism and eliminate large and unnecessary if/else blocks.&lt;br /&gt;
&lt;br /&gt;
We will also insert more specific object creation specifications. Consider if a user's import contains objects that do not exist in the database. We will provide an option for the user to specify whether or not new/dependent objects should be created and account for this boolean in relevant ::import functions for the models.&lt;br /&gt;
&lt;br /&gt;
To summarize our changes:&lt;br /&gt;
&lt;br /&gt;
* Route all import traffic through ImportFileController and ::import calls on models.&lt;br /&gt;
* Refactor ImportFileController.&lt;br /&gt;
* Insert object creation conditions into all relevant ::import functions and into the ImportFileController form.&lt;br /&gt;
* Add ability for ImportFileController to import Question objects for Questionnaires. (Add question view to views/import_file, introduce Question logic into the controller.)&lt;br /&gt;
&lt;br /&gt;
Visually this is what is happening right now (with some files missing for brevity's sake):&lt;br /&gt;
&lt;br /&gt;
[[Image: badUML.png]]&lt;br /&gt;
&lt;br /&gt;
What we want it to look like:&lt;br /&gt;
&lt;br /&gt;
[[Image: goodUML.png]]&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140571</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140571"/>
		<updated>2021-11-03T03:43:23Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: /* Existing Import Functionality */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
The current import functionality is done through the ImportFileController and there are different import class methods implemented in different models that are performing import function.&lt;br /&gt;
&lt;br /&gt;
In the SignUpTopic and User models use helper classes and the attributes are taken from a hash and new ActiveRecord object is created.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', methods:&lt;br /&gt;
** Methods used to process files:&lt;br /&gt;
*** #get_delimiter - Sets delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line of the file&lt;br /&gt;
*** #parse_to_grid - parses the file into 2D array&lt;br /&gt;
*** #parse_to_hash - parses file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
We have found that the below mentioned models are using the import functionality defined in ImportFileController. SignUpTopic and User are dependent on the helper methods to use import functionality.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140563</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140563"/>
		<updated>2021-11-03T03:23:15Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;br /&gt;
&lt;br /&gt;
== Existing Import Functionality ==&lt;br /&gt;
&lt;br /&gt;
Existing import functionality is primarily routed through the ImportFileController and an import class method for various models.&lt;br /&gt;
&lt;br /&gt;
SignUpTopic and User, rely on helper classes that extract attributes from a hash and create an ActiveRecord object.&lt;br /&gt;
&lt;br /&gt;
Questionnaire relies on a helper method that can import Question objects (objects that make up a Questionnaire) from a CSV and adjust the size of the associated QuestionAdvice (the words that pop up after you pick a certain number of stars). However, these functions might be deprecated, as it appears that Question importing is now routed through the ImportFileController unsuccessfully. More detail about specific functions is provided below.&lt;br /&gt;
&lt;br /&gt;
=== Controllers ===&lt;br /&gt;
&lt;br /&gt;
*'''import_file_controller''', the list of methods in the controller are the following:&lt;br /&gt;
** File processing methods:&lt;br /&gt;
*** #get_delimiter - Sets proper delimiter for filetype&lt;br /&gt;
*** #parse_line - Processes line (row) of the file&lt;br /&gt;
*** #parse_to_grid - Turns file into 2D array&lt;br /&gt;
*** #parse_to_hash - Turns file into hash where 'header' stores header row and 'body' stores all contents.&lt;br /&gt;
*** #hash_rows_with_headers - Creates hash for each row of file. Keys are headers, values are row values.&lt;br /&gt;
** Import methods:&lt;br /&gt;
*** #import_from_hash - Primary import functionality. Creates objects for hashed rows (from #hash_rows_with_headers).&lt;br /&gt;
*** #import - Larger controller of import, sets error messages and displays.&lt;br /&gt;
&lt;br /&gt;
*'''questionnaires_controller''', the list of methods in the controller are the following: &lt;br /&gt;
** ::import - Allows import from CSV using QuestionnaireHelper (Appears to be deprecated/unused)&lt;br /&gt;
&lt;br /&gt;
=== Helpers ===&lt;br /&gt;
*'''import_file_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for User object from hash.&lt;br /&gt;
** ::create_new_user - Makes a user object in the database.&lt;br /&gt;
&lt;br /&gt;
*'''import_topics_helper''', the list of methods in the file are the following: &lt;br /&gt;
** ::define_attributes - Sets and returns attributes for a SignUpTopic from hash.&lt;br /&gt;
** ::create_new_sign_up_topic - Makes SignUpTopic objects in the database.&lt;br /&gt;
&lt;br /&gt;
*'''questionnaire_helper''' (Appears to be deprecated/unused), the list of methods in the file are the following: &lt;br /&gt;
** ::get_questions_from_csv - Allows Question and QuestionAdvice import/creation with CSV file.&lt;br /&gt;
&lt;br /&gt;
=== Models ===&lt;br /&gt;
&lt;br /&gt;
All these models have an ::import method called by the ImportFileController. In addition, SignUpTopic and User objects rely on their helpers, which are mentioned above.&lt;br /&gt;
&lt;br /&gt;
*'''assignment_participant'''&lt;br /&gt;
*'''assignment_team'''&lt;br /&gt;
*'''course_participant'''&lt;br /&gt;
*'''course_team'''&lt;br /&gt;
*'''team'''&lt;br /&gt;
*'''metareview_response_map'''&lt;br /&gt;
*'''question'''&lt;br /&gt;
*'''review_response_map'''&lt;br /&gt;
*'''sign_up_sheet'''&lt;br /&gt;
*'''sign_up_topic'''&lt;br /&gt;
*'''user'''&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
	<entry>
		<id>https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140559</id>
		<title>CSC/ECE 517 Fall 2021 - E2160. Implementing and testing import export controllers</title>
		<link rel="alternate" type="text/html" href="https://wiki.expertiza.ncsu.edu/index.php?title=CSC/ECE_517_Fall_2021_-_E2160._Implementing_and_testing_import_export_controllers&amp;diff=140559"/>
		<updated>2021-11-03T02:52:17Z</updated>

		<summary type="html">&lt;p&gt;Svsakham: Created page with &amp;quot;== Import and Export Functionality == In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that ha...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Import and Export Functionality ==&lt;br /&gt;
In Expertiza, many kinds of files can be imported or exported: lists of users for whom accounts are to be created, lists of teams that have been created or need to be created, lists of topics that users are to be able to sign up for, or instructor or peer scores that have been given for a particular assignment.  Historically, all of these import and export methods were written individually, using similar code patterns.&lt;br /&gt;
The instructor might end up adding those in excel or having that data in excel - this functionality allows the expertiza to accept that csv file and tabulate it infront of the user. The user can then select to assign columns to that data and then import it into the database.&lt;br /&gt;
Eg. list of topics or feedback comments from the students can be tabular if it is say a MCQ questionnaire. The instructor will then have to either enter each entry manually through the UI or can upload this file to automate it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Problem Description ==&lt;br /&gt;
Initially the different classes (Topics, Assignments, Course Participants) had different implementations of taking in the csv file which was tightly coupled with the feature / model class. However, it was discovered that instead of defining the new method again and again, this process can be automated, by pushing common code of reading the headers and writing to the database into a single generic function. This would basically make it really easy to add this functionality to other model classes and to the new features yet to come to Expertiza.&lt;/div&gt;</summary>
		<author><name>Svsakham</name></author>
	</entry>
</feed>