CSC/ECE 517 Fall 2016/E1653. Fix and improve rubric criteria

From Expertiza_Wiki
Jump to navigation Jump to search

E1653. Fix and Improve Rubric Criteria


Expertiza background

Expertiza is an open source web application based on Ruby on Rails framework. This application facilitates submission and peer review of assignments and team projects. Students can upload their assignments and URLs linked to their work on expertiza which can be reviewed by the instructor and peer reviewed by other students. Expertiza also expedites the process of selecting/assigning topics and choosing team mates for team projects. Instructors can also create and customize assignments for students and create review rubrics which are used by students for peer reviewing others' work.

Tasks identified

  • Change allow_action? method of questionnaires controller to restrict unauthorized access to edit review rubrics. Only those Instructors who own the rubric or their Teaching Assistants should be allowed edit them.
  • Display an error message when a user who is not the owner of a questionnaire attempts to edit it and redirect him back to the page he came from.
  • Fix the working of import and export methods(dumping and loading criterion) in the Questionnaire controller.
  • Perform feature testing for the import and export methods of questionnaire controller.
  • Remove old and unused code related to rubric import and export.
  • Write feature tests for criterion advice.

Link to deployed application

Modified/Created files

  • questionnaires_controller.rb
  • QuestionnaireHelper.rb
  • Questionnaire.rb
  • questionnaire_spec.rb
  • spec/features/import_export_csv_oss/ (New folder created containing 3 test files)

Summary of implementation

New functionality

  • An instructor can no longer change others' review rubrics but can only view them. If he attempts to do so, an error message will be displayed and he will be redirected back.
  • Only those review rubrics can be modified by an instructor which are owned by him.
  • A Teaching Assistant can modify only those review rubrics which are owned by the instructor under whom he works.
  • A CSV file containing the questions with the correct column names can now be imported into the rubric.
  • A rubric can also be exported in CSV format to a destination path on our local system.

Changes in source code

1. Changes in allow_action? method of the Questionnaires controller.

The action_allow? method earlier provided access to all users to modify or view any review rubric.

# Source code before implementation.
   def action_allowed?

    ['Super-Administrator',
     'Administrator',
     'Instructor',
     'Teaching Assistant', 'Student'].include? current_role_name

  end

The action_allow? method now provides access to modify a rubric to those instructors who own the rubric or the Teaching Assistants who work under them. However, any user can view the contents of the review rubric.

# Source code after implementation.
def action_allowed?
    case params[:action]
      when 'edit', 'update', 'delete', 'toggle_access'
        #Modifications can only be done by papertrail
        q= Questionnaire.find_by(id:params[:id])
        owner_inst_id = q.instructor_id
        if(current_user.role_id==6)     #To identify teaching assistant
          current_ta = current_user;
        end
        owner_flag= (current_user.id == owner_inst_id)

        if(!current_ta.nil?)
          owner_flag = (owner_flag or (current_ta.parent_id == owner_inst_id))
        end
        return owner_flag

      else
        #Allow all others
        ['Super-Administrator',
         'Administrator',
         'Instructor',
         'Teaching Assistant',
         'Student'].include? current_role_name
    end

  end

2. a. Modification of import method declared in the questionnaires controller.

The code initially was not using the read method to read the file data and was unable to import a file.

#Source code before implementation
def import

    @questionnaire = Questionnaire.find(params[:id])
    file = params['csv']
    @questionnaire.questions << QuestionnaireHelper.get_questions_from_csv(@questionnaire, file)
  end

The modified code now reads data from the file and calls the method get_questions_from_csv and passes the data to it. The method reads data in rows and saves each row as a question. It then saves each question.

#Source code after implementation
  def import
    questionnaire_id = (params[:id])
    begin
      uploaded_io = params[:csv]
      File.open(Rails.root.join('spec', 'features/import_export_csv_oss/', uploaded_io.original_filename), 'wb') do |file|
        file.write(uploaded_io.read)
      end
        file_data = File.read(Rails.root.join('spec/features/import_export_csv_oss/'+uploaded_io.original_filename))
        a = QuestionnaireHelper.get_questions_from_csv(file_data,params[:id])
        redirect_to edit_questionnaire_path(questionnaire_id.to_sym), notice: "All questions have been successfully imported!"
       rescue
      redirect_to edit_questionnaire_path(questionnaire_id.to_sym), notice: $ERROR_INFO
    end
  end

b. Removing unwanted code and modifying the get_questions_from_csv method declared in the questionnaire controller.

The method get_questions_from_csv defined in the questionnaire helper was unable to read data from the file.

#Source code before implementation
def self.get_questions_from_csv(questionnaire, file)
    questions = []
    custom_rubric = questionnaire.section == "Custom"
    CSV::Reader.parse(file) do |row|
      unless row.empty?
        i = 0
        score = questionnaire.max_question_score
        q = Question.new
        q_type = QuestionType.new if custom_rubric
        q.true_false = false
        row.each do |cell|
          case i
          when CSV_QUESTION
            q.txt = cell.strip unless cell.nil?
          when CSV_TYPE
            unless cell.nil?
              q.true_false = cell.downcase.strip == Question::TRUE_FALSE.downcase
              q_type.q_type = cell.strip if custom_rubric
            end
          when CSV_PARAM
            if custom_rubric
              q_type.parameters = cell.strip if cell
            end
          when CSV_WEIGHT
            q.weight = cell.strip.to_i if cell
          else
            if score >= questionnaire.min_question_score and !cell.nil?
              a = QuestionAdvice.new(score: score, advice: cell.strip) if custom_rubric
              a = QuestionAdvice.new(score: questionnaire.min_question_score + i - 4, advice: cell.strip)
              score -= 1
              q.question_advices << a
            end
          end
          i += 1
        end
        q.save
        q_type.question = q if custom_rubric
        q_type.save if custom_rubric
        questions << q
      end
    end
    questions
  end

The unwanted code from the get_questions_from_csv method is now removed and the modified code successfully reads data from the csv file and saves questions present in the from of rows in the rubric.

#Source code after implementation
 def self.get_questions_from_csv(file_data,id)
    CSV.parse(file_data, headers: true) do |row|
      questions_hash = row.to_hash
      ques = Question.new(questions_hash)
      ques.questionnaire_id=id
      ques.save

    end # end CSV.parse
  end

3. Removing dysfunctional code from the QuestionnaireHelper and adding a new method in questionnaire.rb model

The create_questionnaire_csv method was not functional and was removed from the QuestionnaireHelper

def self.create_questionnaire_csv(questionnaire, _user_name)
    csv_data = CSV.generate do |csv|
      for question in questionnaire.questions
        # Each row is formatted as follows
        # Question, question advice (from high score to low), type, weight
        row = []
        row << question.txt
        row << question.type


        row << question.alternatives || ''
        row << question.size || ''

        row << question.weight

        # if questionnaire.section == "Custom"
        #  row << QuestionType.find_by_question_id(question.id).parameters
        # else
        #  row << ""
        # end

        # loop through all the question advice from highest score to lowest score
        adjust_advice_size(questionnaire, question)
        for advice in question.question_advices.sort {|x, y| y.score <=> x.score }
          row << advice.advice
        end

        csv << row
    end
    end

    csv_data
end

The to_csv function was added in the questionnaire.rb model class which generates a CSV out of the data in the question table which can then be exported to a local machine.

 def to_csv(ques)
        questions = ques
        csv_data = CSV.generate do |csv|
          row = ['seq','txt','type','weight','size','max_label','min_label','alternatives']
          csv << row
          for question in questions
            row = []
            row << question.seq
            row << question.txt
            row << question.type
            row << question.weight
            row << question.size || ''
            row << question.max_label
            row << question.min_label
            row << question.alternatives

            csv << row

      end
    end
    end

Testing using RSpec

A set of feature tests using the RSpec framework have been added to test the implemented functionalities. Test data from spec/features/import_export_csv_oss/ was used to run the test cases.

1. The file to be uploaded should not be an empty file. The following test case checks if the imported file is empty.

describe 'Import questions from CSV' do

    it 'should not be an empty file', js: true do
      login_as("instructor6")
      visit '/questionnaires/1/edit'
      file_path=Rails.root+"spec/features/import_export_csv_oss/navjot.csv"
      attach_file('csv',file_path)
      click_button "Import from CSV"
      expect(page).not_to have_content('No such file')
end

2.The following test case validates an imported CSV file. The test case passes if all the questions get successfully imported.

it 'should be a valid CSV file', js: true do
    login_as("instructor6")
    visit '/questionnaires/1/edit'
    file_path=Rails.root+"spec/features/import_export_csv_oss/navjot.csv"
    attach_file('csv',file_path)
    click_button "Import from CSV"
    expect(page).to have_content('All questions have been successfully imported!')

end

3. The following test case validates the names of all columns in the imported CSV file. The following example shows that this test case passes when there's an error in the column name of the imported file. If we change this test file with the one in the above two cases, and change expect().to to expect().not_to, we can see that it does exactly what we require it to.

 it 'should have valid column names', js: true do
      login_as("instructor6")
      visit '/questionnaires/1/edit'
      file_path=Rails.root+"spec/features/import_export_csv_oss/navjot (propername).csv"
      attach_file('csv',file_path)
      click_button "Import from CSV"
      expect(page).to have_content('unknown attribute')
    end

4. The following test case validates that no error message appears on the page while exporting.

it 'should not display an error flash message while exporting', js: true  do
    login_as("instructor6")
    visit '/questionnaires/1/edit'
    click_button "Export questions to CSV"
    expect(page).not_to have_content('error')
  end

  end

5.The following test case validates if a user is able to edit an advice.

scenario 'clicks on edit advice', js: true do

    #login_as("instructor6")
    visit '/advice/edit_advice/1'

    expect(page).to have_content("Edit an existing questionnaire's advice")
  end

6. The following test case validates that the changes made in the advice page are re-displayed.

 scenario 'reloads page successfully', js: true do

    #login_as("instructor6")
    visit '/questionnaires/1/edit'
    click_on 'Add'
    visit '/advice/edit_advice/1'
    click_on 'Save and redisplay advice'
    expect(page).to have_content("The advice was successfully saved!")
    expect(page).to have_content("Edit an existing questionnaire's advice")

    #expect(flash[:notice]).to match "The advice was successfully saved"
  end

7.The following test case validates if a user is redirected to the edit advice page after clicking on the 'Edit/View advice' button

scenario 'redirects from questionnaire to advice', js: true do
    visit '/questionnaires/1/edit'
    click_on 'Edit/View advice'
    expect(page).to have_content("Edit an existing questionnaire's advice")
    #expect(AdviceController.save_advice).to be(true)
  end

8.The following test case validates if the changes made in the edit_advice page are saved to the database after the 'Save and redisplay advice' button is clicked.

scenario 'saves content', js: true do
  visit '/questionnaires/1/edit'
  click_on 'Add'
  visit '/advice/edit_advice/1'
    fill_in "horizontal_1_advice" , with: "Example"
    click_on 'Save and redisplay advice'
    expect(QuestionAdvice.where(id: 1, advice:"Example")).not_to be_empty
  end


end

Testing on UI

To Test only if an Instructor who owns the rubric or his TA can edit the rubric

  1. Login to expertiza as 'instructor6' (Username: instructor6 Password:password)
  2. Select 'Questionnaire' Tab under Manage Content
  3. Click on any one of the Review tab (For eg: Review, Metareview, Author Feedback)
  4. Click on edit button of a questionnaire which is owned by instructor6 (This can be found out from the expertiza_development database using query: select name from questionnaires where instructor_id=6;)
  5. User should be redirected to a page where he can edit the questionnaire
  6. Impersonate 'teaching_assistant520' (is a TA under instructor6) and repeat steps 2-5
  7. User should be redirected to a page where he can edit the questionnaire
  8. Repeat the above steps for a questionnaire which is not owned by the instructor
  9. An error message is displayed above the 'Manage Content' title

To Test if a user is able to Import/Export CSV files

  1. Login to expertiza as 'instructor6' (Username: instructor6 Password:password)
  2. Select 'Questionnaire' Tab under Manage Content
  3. Click on any one of the Review tab (For eg: Review, Metareview, Author Feedback)
  4. Click on edit button of a questionnaire which is owned by instructor6
  5. Click on 'Browse' button under 'Import/Export (from/to CSV format)' and select a file of CSV format which contains the questions
  6. Click on 'Import from CSV' button. The questions in the files should be saved into the questionnaire
    1. This can invite future research into the area, since currently, it allows the duplicated sequence number code to be added
  7. Click on 'Export questions to CSV'. The questions should be saved to the local system in a file of CSV format

To test if a user is able to edit advice for a questionnaire

  1. Login to expertiza as 'instructor6' (Username: instructor6 Password:password)
  2. Select 'Questionnaire' Tab under Manage Content.
  3. Click on any one of the Review tab (For eg: Review, Metareview, Author Feedback)
  4. Click on edit button of a questionnaire which is owned by instructor6
  5. Click on edit advice button. This should redirect to a page containing the advice for that questionnaire


To test if a user is able to save edited advice for a questionnaire

  1. Login to expertiza as 'instructor6' (Username: instructor6 Password:password)
  2. Select 'Questionnaire' Tab under Manage Content
  3. Click on any one of the Review tab (For eg: Review, Metareview, Author Feedback)
  4. Click on edit button of a questionnaire which is owned by instructor6
  5. Click on 'save and redisplay' button. This should reload the same page with the edited advice