I think I am going to open source my Assessments work as a plugin

I guess it has been almost three years since I came up with my first version of assessments for Active4D using XML. I guess it was for the ThyssenKrupp project. They wanted to do on-line applications and career builder sucked - so I rolled my own.

After I retired I wrote a version that used three tables - Assessments, Questions, and Answers. I think I first wrote it in Rails, then in Active4d. I made a lot of enhancements to the Active4D version and then moved them back to Rails. I then upgraded the Rails version to work in Rails 3. I think it is time to clean it up and see anyone else needs such an animal.

What are assessments?

I wrote assessments to take care on on-line applications for AIDT. On-line applications had 6 sections.

  • Contact Information
  • General Questions ( Are you willing to work overtime, weekends, etc)
  • Education Questions - gather all education and training
  • Work skills Questions - How many years of experience do you have welding.
  • Work History Questions - Information on last three jobs.
  • Custom Questions. Questions that looked more closely at skills and history.

On the original (and still used) XML version, everything except Contact and work history were scored as Assessments. The XML basically defined the questions and answers table attributes and values in XML.

The current version can do all 6 sections, although contact information is better gathered through a registration module. After thinking about what I came up with, I worked at trying to make it as generic as possible. It can be used for surveys, tests and probably a number of other things.

How it works

class Assessment < ActiveRecord::Base
  has_many :questions, :order => "sequence", :dependent => :destroy

class Question < ActiveRecord::Base
  belongs_to :assessment
  has_many :answers, :order => "sequence", :dependent => :destroy

class Answer < ActiveRecord::Base
  belongs_to :question

Assessments are basically just information about the assessment and the parent of the questions. Being a plugin, assessments must be tied to something, an applications, survey, evaluation. There are fields in the table that allow that connection to be made it different ways.

create_table "assessments", :force => true do |t|
  t.integer  "master_id"
  t.integer  "assessible_id"
  t.string   "assessible_type", :limit => 20
  t.string   "name"
  t.text     "description"
  t.string   "status"
  t.string   "category"
  t.string   "answer_type",     :limit => 20
  t.string   "display_type",    :limit => 20
  t.float    "max_raw"
  t.float    "max_weighted"
  t.datetime "created_at"
  t.datetime "updated_at"

They can be tied to any other model through a Polymorphic association “assessible”. They can also be tied to another model using the assessment id. In the case of online applications, there was a many to many table setup between the job application stage and the assessments.

Most of the information is optional. The assessment record is mainly there to get the questions on the assessments and the possible answers to those question. There are two important actions in the Assessments controller that are the heart of the plugin. Display and Post. Display generates the web form to take the assessments. Display is actually optional, you can always roll your own layout - you just must conform to the naming convention for input elements. Post then scores the submitted assessment and returns a scored assessment object (hash) that the user can do what then want with (store just the score, serialize the hash and store it, etc). I’ll delay talking about what some fields do/mean until we discuss questions and answers.

Questions and Answers

create_table "questions", :force => true do |t|
  t.integer  "master_id" 
  t.integer  "assessment_id"
  t.integer  "sequence"
  t.string   "question"
  t.string   "shortname",     :limit => 20
  t.string   "answer_type",   :limit => 20
  t.string   "display_type",  :limit => 20
  t.string   "score_method",  :limit => 20
  t.float    "weight"
  t.boolean  "critical"
  t.float    "minimum_value"
  t.text     "note"
  t.datetime "created_at"
  t.datetime "updated_at"

create_table "answers", :force => true do |t|
  t.integer  "master_id"
  t.integer  "question_id"
  t.integer  "sequence"
  t.string   "answer"
  t.string   "shortname",        :limit => 20
  t.float    "value"
  t.boolean  "requires_other"
  t.string   "other_question"
  t.string   "answer_eval"
  t.float    "eval_false_value"
  t.datetime "created_at"
  t.datetime "updated_at"

  • master_id is an optional feature. For the online application use, assessments are usually “cloned” from a “Master” assessment. Different companies might want to score questions differently. The easiest way to do this is just to make a copy of the assessment and modify the scoring. The master id can be considered a parent id and allows you to get all cloned assessments, questions or answers for that parent. This would allow you to see how applicants answered certain questions across all companies.
  • sequence is used to sort questions and answers for the display action
  • answer_type controls what input elements are used - basically radio, checkbox, select, text and textarea.
    • radio - radio buttons are used for answers (multiple choice)
    • checkbox - checkboxes are used for answers to all “Check all that apply”
    • select - basically the same as a radio button, just using a select-options pulldown
    • select-multiple - basically the same as a checkbox
    • text - a single text field answer. This answer can optionally be scored using a regex scheme
    • text_numeric - A scored text field that contains a numeric answer.
    • textarea - Displays a textarea instead of a textfield to gather answers for composition type questions.
    • textform - allows multiple text answers for one question (e.g, address, city, state, zip, etc)
  • display_type is used by the display action to generate radio buttons or checkboxes.
    • Inline - take less space for the “rate something on a 1 to 5 scale” type questions. Buttons and answers are painted horizontally.
    • List (default) - Buttons and answers are painted vertically
  • score_method defines how certain answer types are scored.
    • value (default or blank). The value of the the answer is used
    • sum - used only for checkboxes or multiple-select to generate a score based on the sum of the selected values.
    • max - the default method for checkboxes and multiple-selects where the score is based on the highest answer value checked.
    • none - used to just gather answers but not score them.
  • requires_other - Each answer (radio, checkbox only) can generate a text box to gather additional information. For instance selecting a “Completed high school with a diploma” answer could bring up an addition question to enter the name and location of the school.
  • answer_eval is used to score text answers and uses a regex scheme to score the text field.
  • shortname - questions and answers have optional shortnames. While the answer.answer and question.question are used in the display action. A summary dump of the scored objects can be hard to read with all those long questions. In the summary dump, the short name will be used if it is not blank.
  • critical - A question can be considered critical if the answer fails to meet a minimum value on a question. In that case you failed the assessment - even if you score was high. This is used to screen out people if they do not conform to a job requirement (e.g., willing to work rotating shifts)
  • weight - the raw answer values can we weighted, meaning that some questions are more important than others. This is also optional and there are other ways of achieving the same outcome. (answer values are floats)

All three tables are have CRUD controllers using nested routes. Only Assessment has none-CRUD actions: Post, Display and Clone.

A short example

I have a test assessment that displays most of the options. The question list follows:

While you don’t see how the answers are set up, you can pretty much tell from the display page - filled in:

And the post dump.

I only scored 19 points out a possible 42. I also failed the assessment because I answered “golf” as a hobby. This employer had the opinion that anyone who hunts, fishes, or golfs would not be a good employee. The answer eval field was set to !=1:(golf|fish|hunt) which is parsed and breaks down to that they will get 1 point if the answer does not contain golf, fish or hunt!

The post.inspect is the scored object. Again, use out of it what you need.