live coder seeding - completes #16
This commit is contained in:
		@@ -10,7 +10,9 @@ $(function(){
 | 
				
			|||||||
  $("#question_input_type").on('change', function(){
 | 
					  $("#question_input_type").on('change', function(){
 | 
				
			||||||
    var qid = $(this).attr('data-qid') === undefined ? '' : "/" + $(this).attr('data-qid');
 | 
					    var qid = $(this).attr('data-qid') === undefined ? '' : "/" + $(this).attr('data-qid');
 | 
				
			||||||
                                            // /admin/question(/:question_id)/options/:input_type
 | 
					                                            // /admin/question(/:question_id)/options/:input_type
 | 
				
			||||||
    $("[data-id=input-options-wrapper]").load("/admin/question" + qid + "/options/" + $(this).val());
 | 
					    $("[data-id=input-options-wrapper]").load("/admin/question" + qid + "/options/" + $(this).val(), function(){
 | 
				
			||||||
 | 
					      $(".code-input textarea").linedtextarea();
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,9 +60,9 @@ module Admin
 | 
				
			|||||||
      def process_question_params
 | 
					      def process_question_params
 | 
				
			||||||
        question = question_params
 | 
					        question = question_params
 | 
				
			||||||
        question[:input_options] = question_params[:multi_choice] unless question_params[:multi_choice].nil?
 | 
					        question[:input_options] = question_params[:multi_choice] unless question_params[:multi_choice].nil?
 | 
				
			||||||
        question[:input_options] = question_params[:live_coder] unless question_params[:live_coder].nil?
 | 
					        question[:input_options] = question_params[:live_code] unless question_params[:live_code].nil?
 | 
				
			||||||
        question.delete(:multi_choice)
 | 
					        question.delete(:multi_choice)
 | 
				
			||||||
        question.delete(:live_coder)
 | 
					        question.delete(:live_code)
 | 
				
			||||||
        question
 | 
					        question
 | 
				
			||||||
      end
 | 
					      end
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,10 @@
 | 
				
			|||||||
class Question < ApplicationRecord
 | 
					class Question < ApplicationRecord
 | 
				
			||||||
  serialize :input_options, Array
 | 
					  serialize :input_options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  has_many :answers
 | 
					  has_many :answers
 | 
				
			||||||
  belongs_to :quiz
 | 
					  belongs_to :quiz
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  after_initialize :prepare_input_options
 | 
				
			||||||
  before_validation :compact_input_options
 | 
					  before_validation :compact_input_options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  validates :quiz_id, presence: true
 | 
					  validates :quiz_id, presence: true
 | 
				
			||||||
@@ -15,6 +16,11 @@ class Question < ApplicationRecord
 | 
				
			|||||||
  private
 | 
					  private
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def compact_input_options
 | 
					    def compact_input_options
 | 
				
			||||||
      self.input_options = input_options.reject(&:blank?)
 | 
					      self.input_options = input_options.reject { |_k, v| v.blank? } if input_options.class == Hash
 | 
				
			||||||
 | 
					      self.input_options = input_options.reject(&:blank?) if input_options.class == Array
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def prepare_input_options
 | 
				
			||||||
 | 
					      self.input_options = input_options || {}
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,7 +31,9 @@ class AnswerFormatValidator < ActiveModel::EachValidator
 | 
				
			|||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def live_code record, attribute, value
 | 
					    def live_code record, attribute, value
 | 
				
			||||||
      return unless value.nil? || value.values.join.blank?
 | 
					      seed = (Question.find_by(id: record.question_id) || Question.new).input_options
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return unless value.nil? || value.values.join.blank? || !match_seed?(value, seed)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
      record.errors[attribute] << (options[:message] || live_code_error_message(value))
 | 
					      record.errors[attribute] << (options[:message] || live_code_error_message(value))
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
@@ -44,4 +46,16 @@ class AnswerFormatValidator < ActiveModel::EachValidator
 | 
				
			|||||||
      end
 | 
					      end
 | 
				
			||||||
      "You must write comments or code in one of the textareas to progress."
 | 
					      "You must write comments or code in one of the textareas to progress."
 | 
				
			||||||
    end
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def match_seed? value, seed
 | 
				
			||||||
 | 
					      s_value = value.stringify_keys
 | 
				
			||||||
 | 
					      s_seed = seed.stringify_keys
 | 
				
			||||||
 | 
					      keys = s_value.merge(s_seed).keys.uniq
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      matches = keys.inject([]) do |memo, k|
 | 
				
			||||||
 | 
					        memo << (s_value[k].to_s.strip == s_seed[k].to_s.strip)
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      matches.include? false
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,29 +1,18 @@
 | 
				
			|||||||
<%
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return nil
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  # don't load this partial yet. Will finish in issue #16
 | 
					 | 
				
			||||||
  # https://gitlab.perficientxd.com/pdr/skill-assessment-app/issues/16
 | 
					 | 
				
			||||||
  options = { 'text' => '', 'html' => '', 'css' => '', 'js' => '' }
 | 
					 | 
				
			||||||
%>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
<div data-id="live-coder-answer">
 | 
					<div data-id="live-coder-answer">
 | 
				
			||||||
  <div class="code-input">
 | 
					  <div class="code-input">
 | 
				
			||||||
    <label for="question_input_options_html">HTML</label>
 | 
					    <label for="question_input_options_html">HTML</label>
 | 
				
			||||||
    <%= text_area_tag 'question[live_code][html]', options['html'], { data: {id: 'code-html', last: options['html']}, class: 'code-answer code-html' } %>
 | 
					    <%= text_area_tag 'question[live_code][html]', question.input_options['html'], { data: {id: 'code-html', last: question.input_options['html']}, class: 'code-answer code-html' } %>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="code-input">
 | 
					  <div class="code-input">
 | 
				
			||||||
    <label for="question_input_options_css">CSS</label>
 | 
					    <label for="question_input_options_css">CSS</label>
 | 
				
			||||||
    <%= text_area_tag 'question[live_code][css]', options['css'], { data: {id: 'code-css', last: options['css']}, class: 'code-answer code-css' } %>
 | 
					    <%= text_area_tag 'question[live_code][css]', question.input_options['css'], { data: {id: 'code-css', last: question.input_options['css']}, class: 'code-answer code-css' } %>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="code-input">
 | 
					  <div class="code-input">
 | 
				
			||||||
    <label for="question_input_options_js">JS</label>
 | 
					    <label for="question_input_options_js">JS</label>
 | 
				
			||||||
    <%= text_area_tag 'question[live_code][js]', options['js'], { data: {id: 'code-js', last: options['js']}, class: 'code-answer code-js' } %>
 | 
					    <%= text_area_tag 'question[live_code][js]', question.input_options['js'], { data: {id: 'code-js', last: question.input_options['js']}, class: 'code-answer code-js' } %>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="results" data-id="results"></div>
 | 
					  <div class="results" data-id="results"></div>
 | 
				
			||||||
</div>
 | 
					</div>
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,7 +5,12 @@
 | 
				
			|||||||
                    name: "answer[#{question.input_type}][later]",
 | 
					                    name: "answer[#{question.input_type}][later]",
 | 
				
			||||||
                 checked: Array(question.answer).include?('finish-later')
 | 
					                 checked: Array(question.answer).include?('finish-later')
 | 
				
			||||||
                  }
 | 
					                  }
 | 
				
			||||||
  answers = answer.try(:answer) || answer
 | 
					
 | 
				
			||||||
 | 
					  answers    = answer.try(:answer) || answer
 | 
				
			||||||
 | 
					  value_text = answers['text']     || question.input_options['text']
 | 
				
			||||||
 | 
					  value_html = answers['html']     || question.input_options['html']
 | 
				
			||||||
 | 
					  value_css  = answers['css']      || question.input_options['css']
 | 
				
			||||||
 | 
					  value_js   = answers['js']       || question.input_options['js']
 | 
				
			||||||
%>
 | 
					%>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<%= render partial: "quiz/answer_errors", locals: {question: question, answer: answer} %>
 | 
					<%= render partial: "quiz/answer_errors", locals: {question: question, answer: answer} %>
 | 
				
			||||||
@@ -38,21 +43,21 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
<div id="answer<%= question.question_id %>" data-id="live-coder-answer" style="display: none;">
 | 
					<div id="answer<%= question.question_id %>" data-id="live-coder-answer" style="display: none;">
 | 
				
			||||||
  <label for="answer_live_code_text">Enter answer here</label>
 | 
					  <label for="answer_live_code_text">Enter answer here</label>
 | 
				
			||||||
  <%= text_area_tag 'answer[live_code][text]', (answers['text']), { disabled: true, data: {last: answers['text']}} %>
 | 
					  <%= text_area_tag 'answer[live_code][text]', value_text, { disabled: true, data: {last: answers['text']}} %>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="code-input">
 | 
					  <div class="code-input">
 | 
				
			||||||
    <label for="answer_live_code_html">HTML</label>
 | 
					    <label for="answer_live_code_html">HTML</label>
 | 
				
			||||||
    <%= text_area_tag 'answer[live_code][html]', (answers['html']), { disabled: true, data: {id: 'code-html', last: answers['html']}, class: 'code-answer code-html' } %>
 | 
					    <%= text_area_tag 'answer[live_code][html]', value_html, { disabled: true, data: {id: 'code-html', last: answers['html']}, class: 'code-answer code-html' } %>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="code-input">
 | 
					  <div class="code-input">
 | 
				
			||||||
    <label for="answer_live_code_css">CSS</label>
 | 
					    <label for="answer_live_code_css">CSS</label>
 | 
				
			||||||
    <%= text_area_tag 'answer[live_code][css]', (answers['css']), { disabled: true, data: {id: 'code-css', last: answers['css']}, class: 'code-answer code-css' } %>
 | 
					    <%= text_area_tag 'answer[live_code][css]', value_css, { disabled: true, data: {id: 'code-css', last: answers['css']}, class: 'code-answer code-css' } %>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="code-input">
 | 
					  <div class="code-input">
 | 
				
			||||||
    <label for="answer_live_code_js">JS</label>
 | 
					    <label for="answer_live_code_js">JS</label>
 | 
				
			||||||
    <%= text_area_tag 'answer[live_code][js]', (answers['js']), { disabled: true, data: {id: 'code-js', last: answers['js']}, class: 'code-answer code-js' } %>
 | 
					    <%= text_area_tag 'answer[live_code][js]', value_js, { disabled: true, data: {id: 'code-js', last: answers['js']}, class: 'code-answer code-js' } %>
 | 
				
			||||||
  </div>
 | 
					  </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  <div class="results" data-id="results"></div>
 | 
					  <div class="results" data-id="results"></div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,7 +39,7 @@ class CandidateQuizQuestion
 | 
				
			|||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def input_options
 | 
					  def input_options
 | 
				
			||||||
    YAML.load(row["input_options"].to_s)
 | 
					    YAML.load(row["input_options"].to_s) || {}
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def answer
 | 
					  def answer
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								test/fixtures/questions.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								test/fixtures/questions.yml
									
									
									
									
										vendored
									
									
								
							@@ -68,6 +68,8 @@ fed7:
 | 
				
			|||||||
  category: Javascript
 | 
					  category: Javascript
 | 
				
			||||||
  input_type: live_code
 | 
					  input_type: live_code
 | 
				
			||||||
  input_options:
 | 
					  input_options:
 | 
				
			||||||
 | 
					    :html: "<p>sample seed html</p>"
 | 
				
			||||||
 | 
					    :css: "body { color: #644; }"
 | 
				
			||||||
  sort: 6
 | 
					  sort: 6
 | 
				
			||||||
  active: true
 | 
					  active: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -15,4 +15,13 @@ class QuestionLiveCoderTest < ActionDispatch::IntegrationTest
 | 
				
			|||||||
    # TODO: add in capybara and test form post
 | 
					    # TODO: add in capybara and test form post
 | 
				
			||||||
    # assert_redirected summary_path
 | 
					    # assert_redirected summary_path
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test "should load seed data into live coder" do
 | 
				
			||||||
 | 
					    setup_auth candidates(:juan)
 | 
				
			||||||
 | 
					    question = questions(:fed7)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    get question_path(question.id)
 | 
				
			||||||
 | 
					    assert_response :success
 | 
				
			||||||
 | 
					    assert_select '#answer_live_code_html', question.input_options['html']
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -11,4 +11,11 @@ class QuestionTest < ActiveSupport::TestCase
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    assert_equal 2, question.input_options.count
 | 
					    assert_equal 2, question.input_options.count
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test 'should have seed input for live_coder' do
 | 
				
			||||||
 | 
					    question = Question.find questions(:fed7).to_i
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert_kind_of Hash, question.input_options
 | 
				
			||||||
 | 
					    assert question.input_options.keys.include? :html
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,13 +3,15 @@ class AnswerValidatable
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  attr_accessor :answer
 | 
					  attr_accessor :answer
 | 
				
			||||||
  attr_accessor :question
 | 
					  attr_accessor :question
 | 
				
			||||||
 | 
					  attr_accessor :question_id
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  validates :answer, answer_format: true
 | 
					  validates :answer, answer_format: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  MockQuestion = Struct.new(:input_type)
 | 
					  MockQuestion = Struct.new(:input_type)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def initialize input_type
 | 
					  def initialize input_type, qid = nil
 | 
				
			||||||
    @input_type = input_type
 | 
					    @input_type = input_type
 | 
				
			||||||
 | 
					    @question_id = qid
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  def question
 | 
					  def question
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,4 +48,20 @@ class AnswerFormatValidatorTest < ActiveSupport::TestCase
 | 
				
			|||||||
    assert obj.valid?
 | 
					    assert obj.valid?
 | 
				
			||||||
    assert obj.errors.messages.empty?
 | 
					    assert obj.errors.messages.empty?
 | 
				
			||||||
  end
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test "live_code should PASS using seed data" do
 | 
				
			||||||
 | 
					    obj = AnswerValidatable.new('live_code', questions(:fed7).id)
 | 
				
			||||||
 | 
					    obj.answer = { text: "no thanks", html: "<p>sample seed html</p>", css: "body { color: #644; }", js: "" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    assert obj.valid?
 | 
				
			||||||
 | 
					    assert obj.errors.messages.empty?
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  test "live_code should FAIL with seed data only" do
 | 
				
			||||||
 | 
					    obj = AnswerValidatable.new('live_code', questions(:fed7).id)
 | 
				
			||||||
 | 
					    obj.answer = { text: "", html: "<p>sample seed html</p>", css: "body { color: #644; }", js: "" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    refute obj.valid?
 | 
				
			||||||
 | 
					    assert_match(/write.*code/, obj.errors.messages[:answer][0])
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
end
 | 
					end
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user