breakpointer-surveyor 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +6 -0
- data/MIT-LICENSE +20 -0
- data/README.md +20 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/app/controllers/answers_controller.rb +87 -0
- data/app/controllers/dependencies_controller.rb +87 -0
- data/app/controllers/dependency_conditions_controller.rb +87 -0
- data/app/controllers/questions_controller.rb +85 -0
- data/app/controllers/sections_controller.rb +87 -0
- data/app/controllers/surveying_controller.rb +139 -0
- data/app/controllers/surveys_controller.rb +87 -0
- data/app/helpers/answers_helper.rb +2 -0
- data/app/helpers/application_helper.rb +3 -0
- data/app/helpers/questions_helper.rb +2 -0
- data/app/helpers/sections_helper.rb +2 -0
- data/app/helpers/survey_form_builder.rb +38 -0
- data/app/helpers/survey_importer_helper.rb +2 -0
- data/app/helpers/surveying_helper.rb +91 -0
- data/app/helpers/surveys_helper.rb +2 -0
- data/app/models/answer.rb +18 -0
- data/app/models/dependency.rb +43 -0
- data/app/models/dependency_condition.rb +76 -0
- data/app/models/question.rb +44 -0
- data/app/models/question_group.rb +5 -0
- data/app/models/response.rb +58 -0
- data/app/models/response_set.rb +174 -0
- data/app/models/survey.rb +54 -0
- data/app/models/survey_section.rb +19 -0
- data/app/models/user.rb +5 -0
- data/app/views/answer_display_types/_any_answer.html.haml +3 -0
- data/app/views/answer_display_types/_any_other_and_string.html.haml +5 -0
- data/app/views/answer_display_types/_any_string.html.haml +4 -0
- data/app/views/answer_display_types/_date.html.haml +3 -0
- data/app/views/answer_display_types/_datetime.html.haml +2 -0
- data/app/views/answer_display_types/_default.html.haml +1 -0
- data/app/views/answer_display_types/_float.html.haml +5 -0
- data/app/views/answer_display_types/_grid_any_answer.html.haml +2 -0
- data/app/views/answer_display_types/_grid_default.html.haml +2 -0
- data/app/views/answer_display_types/_grid_float.html.haml +2 -0
- data/app/views/answer_display_types/_grid_integer.html.haml +2 -0
- data/app/views/answer_display_types/_grid_one_answer.html.haml +2 -0
- data/app/views/answer_display_types/_grid_string.html.haml +2 -0
- data/app/views/answer_display_types/_integer.html.haml +6 -0
- data/app/views/answer_display_types/_one_answer.html.haml +3 -0
- data/app/views/answer_display_types/_one_string.html.haml +6 -0
- data/app/views/answer_display_types/_repeater_integer.html.haml +6 -0
- data/app/views/answer_display_types/_repeater_string.html.haml +7 -0
- data/app/views/answer_display_types/_string.html.haml +7 -0
- data/app/views/answer_display_types/_text.html.haml +6 -0
- data/app/views/answer_display_types/_time.html.haml +2 -0
- data/app/views/layouts/surveys.html.erb +17 -0
- data/app/views/question_display_types/_default.html.haml +12 -0
- data/app/views/question_display_types/_dropdown.html.haml +12 -0
- data/app/views/question_display_types/_grid_default.html.haml +14 -0
- data/app/views/question_display_types/_grid_dropdown.html.haml +15 -0
- data/app/views/question_display_types/_group_default.html.haml +8 -0
- data/app/views/question_display_types/_group_dropdown.html.haml +6 -0
- data/app/views/question_display_types/_image.html.haml +2 -0
- data/app/views/question_display_types/_inline.html.haml +12 -0
- data/app/views/question_display_types/_label.html.haml +4 -0
- data/app/views/question_display_types/_repeater_default.html.haml +10 -0
- data/app/views/question_display_types/_repeater_dropdown.html.haml +8 -0
- data/app/views/question_display_types/_slider.html.haml +17 -0
- data/app/views/question_group_display_types/_default.html.haml +13 -0
- data/app/views/question_group_display_types/_grid.html.haml +24 -0
- data/app/views/question_group_display_types/_repeater.html.haml +20 -0
- data/app/views/surveying/edit.html.haml +38 -0
- data/app/views/surveying/finish.html.haml +1 -0
- data/app/views/surveying/index.html.erb +19 -0
- data/app/views/surveying/new.html.haml +17 -0
- data/app/views/surveying/show.html.haml +8 -0
- data/config/routes.rb +10 -0
- data/generators/surveyor/surveyor_generator.rb +38 -0
- data/generators/surveyor/templates/README +9 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_arrows_leftright.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_arrows_updown.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_close.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_doc.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_folder_closed.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_folder_open.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_minus.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_plus.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_11x11_icon_resize_se.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_35x9_colorpicker_indicator.gif.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_7x7_arrow_down.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_7x7_arrow_left.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_7x7_arrow_right.gif +0 -0
- data/generators/surveyor/templates/assets/images/222222_7x7_arrow_up.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_arrows_leftright.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_arrows_updown.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_close.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_doc.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_folder_closed.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_folder_open.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_minus.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_11x11_icon_plus.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_7x7_arrow_down.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_7x7_arrow_left.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_7x7_arrow_right.gif +0 -0
- data/generators/surveyor/templates/assets/images/454545_7x7_arrow_up.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_arrows_leftright.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_arrows_updown.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_close.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_doc.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_folder_closed.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_folder_open.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_minus.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_11x11_icon_plus.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_7x7_arrow_down.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_7x7_arrow_left.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_7x7_arrow_right.gif +0 -0
- data/generators/surveyor/templates/assets/images/888888_7x7_arrow_up.gif +0 -0
- data/generators/surveyor/templates/assets/images/dadada_40x100_textures_02_glass_75.png +0 -0
- data/generators/surveyor/templates/assets/images/e6e6e6_40x100_textures_02_glass_75.png +0 -0
- data/generators/surveyor/templates/assets/images/ffffff_40x100_textures_01_flat_0.png +0 -0
- data/generators/surveyor/templates/assets/images/ffffff_40x100_textures_02_glass_65.png +0 -0
- data/generators/surveyor/templates/assets/javascripts/accessibleUISlider.jQuery.js +201 -0
- data/generators/surveyor/templates/assets/javascripts/jquery-1.2.6.js +3549 -0
- data/generators/surveyor/templates/assets/javascripts/jquery-ui-personalized-1.5.3.js +7616 -0
- data/generators/surveyor/templates/assets/javascripts/jquery.form.js +637 -0
- data/generators/surveyor/templates/assets/javascripts/surveyor.js +35 -0
- data/generators/surveyor/templates/assets/stylesheets/jquery-ui-slider-additions.css +71 -0
- data/generators/surveyor/templates/assets/stylesheets/reset.css +46 -0
- data/generators/surveyor/templates/assets/stylesheets/sass/surveyor.sass +321 -0
- data/generators/surveyor/templates/assets/stylesheets/surveyor.css +245 -0
- data/generators/surveyor/templates/assets/stylesheets/ui.theme.css +851 -0
- data/generators/surveyor/templates/migrate/create_answers.rb +46 -0
- data/generators/surveyor/templates/migrate/create_dependencies.rb +21 -0
- data/generators/surveyor/templates/migrate/create_dependency_conditions.rb +29 -0
- data/generators/surveyor/templates/migrate/create_question_groups.rb +18 -0
- data/generators/surveyor/templates/migrate/create_questions.rb +33 -0
- data/generators/surveyor/templates/migrate/create_response_sets.rb +22 -0
- data/generators/surveyor/templates/migrate/create_responses.rb +33 -0
- data/generators/surveyor/templates/migrate/create_survey_sections.rb +25 -0
- data/generators/surveyor/templates/migrate/create_surveys.rb +25 -0
- data/generators/surveyor/templates/surveys/kitchen_sink_survey.rb +201 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/lib/tasks/surveyor_tasks.rake +29 -0
- data/lib/tiny_code.rb +58 -0
- data/lib/user_manager.rb +9 -0
- data/lib/xml_formatter.rb +12 -0
- data/script/surveyor/answer.rb +84 -0
- data/script/surveyor/columnizer.rb +36 -0
- data/script/surveyor/dependency.rb +43 -0
- data/script/surveyor/dependency_condition.rb +74 -0
- data/script/surveyor/dslparse.rb +66 -0
- data/script/surveyor/question.rb +76 -0
- data/script/surveyor/question_group.rb +33 -0
- data/script/surveyor/specs/answer_spec.rb +66 -0
- data/script/surveyor/specs/question_dependency_spec.rb +46 -0
- data/script/surveyor/specs/question_group_spec.rb +9 -0
- data/script/surveyor/specs/question_spec.rb +111 -0
- data/script/surveyor/specs/section_spec.rb +58 -0
- data/script/surveyor/survey.rb +108 -0
- data/script/surveyor/survey_section.rb +153 -0
- data/script/surveyor/whr_dsl.tmproj +244 -0
- data/spec/controllers/answers_controller_spec.rb +64 -0
- data/spec/controllers/dependencies_controller_spec.rb +63 -0
- data/spec/controllers/dependency_conditions_controller_spec.rb +64 -0
- data/spec/controllers/questions_controller_spec.rb +64 -0
- data/spec/controllers/sections_controller_spec.rb +64 -0
- data/spec/controllers/surveying_controller_spec.rb +328 -0
- data/spec/controllers/surveying_routing_spec.rb +45 -0
- data/spec/controllers/surveys_controller_spec.rb +64 -0
- data/spec/fixtures/answers.yml +9 -0
- data/spec/fixtures/dependencies.yml +7 -0
- data/spec/fixtures/dependency_conditions.yml +27 -0
- data/spec/fixtures/question_groups.yml +7 -0
- data/spec/fixtures/questions.yml +15 -0
- data/spec/fixtures/response_sets.yml +13 -0
- data/spec/fixtures/responses.yml +9 -0
- data/spec/fixtures/survey_sections.yml +11 -0
- data/spec/fixtures/surveys.yml +9 -0
- data/spec/fixtures/users.yml +7 -0
- data/spec/helpers/survey_importer_helper_spec.rb +11 -0
- data/spec/helpers/surveying_helper_spec.rb +11 -0
- data/spec/models/answer_spec.rb +62 -0
- data/spec/models/dependency_condition_spec.rb +347 -0
- data/spec/models/dependency_spec.rb +82 -0
- data/spec/models/question_group_spec.rb +11 -0
- data/spec/models/question_spec.rb +70 -0
- data/spec/models/response_set_spec.rb +172 -0
- data/spec/models/response_spec.rb +94 -0
- data/spec/models/survey_section_spec.rb +34 -0
- data/spec/models/survey_spec.rb +72 -0
- data/spec/models/user_spec.rb +11 -0
- data/spec/rcov.opts +2 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +72 -0
- data/spec/views/app/edit.html.erb_spec.rb +20 -0
- data/spec/views/app/index.html.erb_spec.rb +19 -0
- data/spec/views/app/new.html.erb_spec.rb +21 -0
- data/spec/views/app/show.html.erb_spec.rb +17 -0
- data/surveyor.gemspec +258 -0
- data/uninstall.rb +1 -0
- metadata +286 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
class QuestionGroup
|
|
2
|
+
|
|
3
|
+
# Context, Content, Display
|
|
4
|
+
attr_accessor :id, :section_id, :section, :parser
|
|
5
|
+
attr_accessor :text, :help_text
|
|
6
|
+
attr_accessor :display_type
|
|
7
|
+
|
|
8
|
+
# id, section and text required
|
|
9
|
+
def initialize(section, args, options)
|
|
10
|
+
self.parser = section.parser
|
|
11
|
+
self.id = parser.new_question_group_id
|
|
12
|
+
self.text = args[0]
|
|
13
|
+
self.default_options().merge(options).merge(args[1] || {}).each{|key,value| self.instance_variable_set("@#{key}", value)}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def default_options()
|
|
17
|
+
{:display_type => "default"}
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def to_yml
|
|
21
|
+
out =[ %(#{@id}:) ]
|
|
22
|
+
out << %( id: #{@id})
|
|
23
|
+
out << %( text: "#{@text}")
|
|
24
|
+
out << %( help_text: "#{@help_text}")
|
|
25
|
+
out << %( display_type: "#{@display_type}")
|
|
26
|
+
(out << nil ).join("\r\n")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_file
|
|
30
|
+
File.open(self.parser.question_groups_yml, File::CREAT|File::APPEND|File::WRONLY){ |f| f << to_yml }
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../answer'
|
|
2
|
+
|
|
3
|
+
describe Answer, " when first created" do
|
|
4
|
+
|
|
5
|
+
TEST_ID = 1
|
|
6
|
+
TEST_CONTEXT_ID = "b3a_1"
|
|
7
|
+
TEST_QUESTION_ID = "2"
|
|
8
|
+
TEST_TEXT = "No / Rarely"
|
|
9
|
+
TEST_OPTIONS = {:help_text => "Never or rarely ever"}
|
|
10
|
+
|
|
11
|
+
before do
|
|
12
|
+
@ans = Answer.new(TEST_ID, TEST_QUESTION_ID, TEST_CONTEXT_ID, TEST_TEXT, TEST_OPTIONS)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
it "should set inititalized variables to those passed in" do
|
|
16
|
+
@ans.id.should == TEST_ID
|
|
17
|
+
@ans.question_id.should == TEST_QUESTION_ID
|
|
18
|
+
@ans.context_id.should == TEST_CONTEXT_ID
|
|
19
|
+
@ans.text.should == TEST_TEXT
|
|
20
|
+
@ans.help_text.should == TEST_OPTIONS[:help_text]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "should output current state to yml" do
|
|
24
|
+
@ans.should.respond_to?(:to_yml)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
it "should create a normalized code from the answer text" do
|
|
28
|
+
# The answer object should take the title of the text and convert
|
|
29
|
+
# it to a code that is more appropirate for a database entry
|
|
30
|
+
|
|
31
|
+
# Taking a few answers from the survey for testing
|
|
32
|
+
str = []
|
|
33
|
+
str[0] = "This? is a in - t3rrible-@nswer of! (question) on"
|
|
34
|
+
str[1] = "Private insurance/ HMO/ PPO"
|
|
35
|
+
str[2] = "VA"
|
|
36
|
+
str[3] = "PMS (Premenstrual syndrome)/ PMDD (Premenstrual Dysphoric Disorder)"
|
|
37
|
+
str[4] = "Have never been employed outside the home"
|
|
38
|
+
str[5] = "Professional"
|
|
39
|
+
str[6] = "Not working because of temporary disability, but expect to return to a job"
|
|
40
|
+
|
|
41
|
+
# What the results should look like
|
|
42
|
+
r_str = []
|
|
43
|
+
r_str[0] = "this_t3rrible_nswer"
|
|
44
|
+
r_str[1] = "private_insurance_hmo_ppo"
|
|
45
|
+
r_str[2] = "va"
|
|
46
|
+
r_str[3] = "pms_pmdd"
|
|
47
|
+
r_str[4] = "never_been_employed_outside_home"
|
|
48
|
+
r_str[5] = "professional"
|
|
49
|
+
r_str[6] = "temporary_disability_expect_return_job"
|
|
50
|
+
|
|
51
|
+
count = 0
|
|
52
|
+
str.each do |s|
|
|
53
|
+
|
|
54
|
+
code = Answer.to_normalized_code(s)
|
|
55
|
+
code.should eql(r_str[count])
|
|
56
|
+
count += 1
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
it "should create a normalized code automatically when initalized" do
|
|
63
|
+
@ans.code.should eql("no_rarely")
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../question_dependency'
|
|
2
|
+
|
|
3
|
+
describe QuestionDependency, " when first created" do
|
|
4
|
+
|
|
5
|
+
ID = 1
|
|
6
|
+
QUESTION_ID = 2
|
|
7
|
+
DEPENDENT_QUESTION_ID = 3
|
|
8
|
+
DEPENDENT_GROUP_ID = nil
|
|
9
|
+
DEPENDENCY_TYPE_1 = {:dependency_type => :response_count, :conditional => :one_or_more }
|
|
10
|
+
DEPENDENCY_TYPE_2 = {:dependency_type => :response_answer, :conditional => :selected, :answer_id => 1}
|
|
11
|
+
DEPENDENCY_TYPE_3 = {:dependency_type => :response_value, :condition => :not_zero}
|
|
12
|
+
|
|
13
|
+
before do
|
|
14
|
+
@dependent_1 = QuestionDependency.new(ID, QUESTION_ID, DEPENDENT_QUESTION_ID, DEPENDENT_GROUP_ID, DEPENDENCY_TYPE_1)
|
|
15
|
+
@dependent_2 = QuestionDependency.new(ID, QUESTION_ID, DEPENDENT_QUESTION_ID, DEPENDENT_GROUP_ID, DEPENDENCY_TYPE_2)
|
|
16
|
+
@dependent_3 = QuestionDependency.new(ID, QUESTION_ID, DEPENDENT_QUESTION_ID, DEPENDENT_GROUP_ID, DEPENDENCY_TYPE_3)
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "should set initialization paramerters properly" do
|
|
21
|
+
# Checking both type1 and type2 dependencies
|
|
22
|
+
@dependent_1.id.should == ID
|
|
23
|
+
@dependent_1.question_id.should == QUESTION_ID
|
|
24
|
+
@dependent_1.dependent_question_id.should == DEPENDENT_QUESTION_ID
|
|
25
|
+
@dependent_1.dependency_type.should == DEPENDENCY_TYPE_1[:dependency_type]
|
|
26
|
+
@dependent_1.conditional.should == DEPENDENCY_TYPE_1[:conditional]
|
|
27
|
+
|
|
28
|
+
@dependent_2.id.should == ID
|
|
29
|
+
@dependent_2.question_id.should == QUESTION_ID
|
|
30
|
+
@dependent_2.dependent_question_id.should == DEPENDENT_QUESTION_ID
|
|
31
|
+
@dependent_2.dependency_type.should == DEPENDENCY_TYPE_2[:dependency_type]
|
|
32
|
+
@dependent_2.conditional.should == DEPENDENCY_TYPE_2[:conditional]
|
|
33
|
+
@dependent_2.answer_id.should == DEPENDENCY_TYPE_2[:answer_id]
|
|
34
|
+
|
|
35
|
+
@dependent_3.id.should == ID
|
|
36
|
+
@dependent_3.question_id.should == QUESTION_ID
|
|
37
|
+
@dependent_3.dependent_question_id.should == DEPENDENT_QUESTION_ID
|
|
38
|
+
@dependent_3.dependency_type.should == DEPENDENCY_TYPE_3[:dependency_type]
|
|
39
|
+
@dependent_3.conditional.should == DEPENDENCY_TYPE_3[:conditional]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "should output current state to yml" do
|
|
43
|
+
@ans.should.respond_to?(:to_yml)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../question'
|
|
2
|
+
|
|
3
|
+
describe Question, " when first created" do
|
|
4
|
+
TEST_ID = 1
|
|
5
|
+
TEST_CONTEXT_ID = "B3"
|
|
6
|
+
TEST_SECTION_ID = 2
|
|
7
|
+
TEST_TEXT = "In the past 12 months how many times have you been to a doctor?"
|
|
8
|
+
TEST_HELP_TEXT = "Please give a rough estimate"
|
|
9
|
+
TEST_OPTIONS = {:help_text => TEST_HELP_TEXT }
|
|
10
|
+
TEST_CODE = "many_times_you_been_doctor"
|
|
11
|
+
|
|
12
|
+
before do
|
|
13
|
+
@ques = Question.new(TEST_ID, TEST_SECTION_ID, TEST_CONTEXT_ID, TEST_TEXT, TEST_OPTIONS)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
it "should set initialization parameters properly" do
|
|
17
|
+
@ques.id.should == TEST_ID
|
|
18
|
+
@ques.context_id.should == TEST_CONTEXT_ID
|
|
19
|
+
@ques.section_id.should == TEST_SECTION_ID
|
|
20
|
+
@ques.code.should == TEST_CODE
|
|
21
|
+
@ques.options[:help_text].should == TEST_HELP_TEXT
|
|
22
|
+
|
|
23
|
+
#Checking the defaults
|
|
24
|
+
@ques.options[:has_omit?].should == true
|
|
25
|
+
@ques.options[:has_other?].should == false
|
|
26
|
+
@ques.options[:answer_format].should == :medium_text
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it "should output current state to yml" do
|
|
30
|
+
@ques.should.respond_to?(:to_yml)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it "should create a normalized code from the answer text" do
|
|
34
|
+
# The question object should take the title of the text and convert
|
|
35
|
+
# it to a code that is more appropirate for a database entry
|
|
36
|
+
|
|
37
|
+
# Taking a few questions from the survey for testing
|
|
38
|
+
str = []
|
|
39
|
+
str[0] = "How long has it been since you last visited a doctor for a routine checkup (routine being not for a particular reason)?"
|
|
40
|
+
str[1] = "Do you take medications as directed?"
|
|
41
|
+
str[2] = "Do you every leak urine (or) water when you didn't want to?" #checking for () and ' removal
|
|
42
|
+
str[3] = "Do your biological family members (not adopted) have a \"history\" of any of the following?"
|
|
43
|
+
str[4] = "Your health:"
|
|
44
|
+
str[5] = "In general, you would say your health is:"
|
|
45
|
+
|
|
46
|
+
# What the results should look like
|
|
47
|
+
r_str = []
|
|
48
|
+
r_str[0] = "visited_doctor_for_routine_checkup"
|
|
49
|
+
r_str[1] = "you_take_medications_as_directed"
|
|
50
|
+
r_str[2] = "urine_water_you_didnt_want"
|
|
51
|
+
r_str[3] = "family_members_history_any_following"
|
|
52
|
+
r_str[4] = "your_health"
|
|
53
|
+
r_str[5] = "you_would_say_your_health"
|
|
54
|
+
|
|
55
|
+
count = 0
|
|
56
|
+
str.each do |s|
|
|
57
|
+
|
|
58
|
+
code = Question.to_normalized_code(s)
|
|
59
|
+
code.should eql(r_str[count])
|
|
60
|
+
count += 1
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
it "should create a normalized code automatically when initalized" do
|
|
67
|
+
@ques.code.should eql(TEST_CODE)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
it "should update the normalized code if the title is changed" do
|
|
71
|
+
@ques.code.should eql(TEST_CODE)
|
|
72
|
+
@ques.text = "Sometimes or All the time?"
|
|
73
|
+
@ques.code.should eql("sometimes_all_time")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
describe Question, " when it contains data" do
|
|
79
|
+
|
|
80
|
+
before do # Mocking up some answers
|
|
81
|
+
@question = Question.new(1,TEST_SECTION_ID,TEST_CONTEXT_ID,TEST_TEXT,TEST_OPTIONS)
|
|
82
|
+
ma1 = mock("answer")
|
|
83
|
+
ma1.stub!(:context_id).and_return("1")
|
|
84
|
+
@question.add_answer(ma1)
|
|
85
|
+
|
|
86
|
+
ma2 = mock("answer")
|
|
87
|
+
ma2.stub!(:context_id).and_return("2")
|
|
88
|
+
@question.add_answer(ma2)
|
|
89
|
+
|
|
90
|
+
ma3 = mock("answer")
|
|
91
|
+
ma3.stub!(:context_id).and_return("3")
|
|
92
|
+
@question.add_answer(ma3)
|
|
93
|
+
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
it "should have added the test answers correctly" do
|
|
97
|
+
@question.answers.length.should eql(3)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
it "should have a question text" do
|
|
101
|
+
@question.text.should eql(TEST_TEXT)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
it "should find an answer by context_id" do
|
|
105
|
+
pending # yoon: commented out during dsl refactoring
|
|
106
|
+
a_to_find = @question.find_answer_by_context_id("2")
|
|
107
|
+
a_to_find.should_not eql(nil)
|
|
108
|
+
a_to_find.context_id.should eql("2")
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
end
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/../section'
|
|
2
|
+
|
|
3
|
+
describe Section, " when first created" do
|
|
4
|
+
|
|
5
|
+
TEST_TITLE = "Demographics"
|
|
6
|
+
TEST_SECTION = :B
|
|
7
|
+
|
|
8
|
+
before do
|
|
9
|
+
@section = Section.new(1,TEST_SECTION,TEST_TITLE)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it "should accept questions" do
|
|
13
|
+
mock_question = mock("question")
|
|
14
|
+
@section.questions.size.should eql(0)
|
|
15
|
+
@section.add_question(mock_question)
|
|
16
|
+
@section.questions.size.should eql(1)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "should output current state to code" do
|
|
20
|
+
@section.should.respond_to?(:to_code)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe Section, " when it contains data" do
|
|
26
|
+
|
|
27
|
+
before do # Mocking up some questions
|
|
28
|
+
@section = Section.new(1,TEST_SECTION,TEST_TITLE)
|
|
29
|
+
mq1 = mock("question")
|
|
30
|
+
mq1.stub!(:context_id).and_return("B1")
|
|
31
|
+
@section.add_question(mq1)
|
|
32
|
+
|
|
33
|
+
mq2 = mock("question")
|
|
34
|
+
mq2.stub!(:context_id).and_return("B2")
|
|
35
|
+
@section.add_question(mq2)
|
|
36
|
+
|
|
37
|
+
mq3 = mock("question")
|
|
38
|
+
mq3.stub!(:context_id).and_return("B3")
|
|
39
|
+
@section.add_question(mq3)
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
it "should have added the test questions correctly" do
|
|
44
|
+
@section.questions.length.should eql(3)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should have a title" do
|
|
48
|
+
@section.title.should eql(TEST_TITLE)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "should find a question by context_id" do
|
|
52
|
+
pending # yoon: commented out during dsl refactoring
|
|
53
|
+
q_to_find = @section.find_question_by_context_id("B2")
|
|
54
|
+
q_to_find.should_not eql(nil)
|
|
55
|
+
q_to_find.context_id.should eql("B2")
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/survey_section'
|
|
2
|
+
|
|
3
|
+
class Survey
|
|
4
|
+
include Columnizer
|
|
5
|
+
# Content, Reference, Expiry, Children
|
|
6
|
+
attr_accessor :id, :parser
|
|
7
|
+
attr_accessor :title, :description
|
|
8
|
+
attr_accessor :access_code
|
|
9
|
+
attr_accessor :active_at, :inactive_at
|
|
10
|
+
attr_accessor :survey_sections
|
|
11
|
+
|
|
12
|
+
@@current_survey = nil
|
|
13
|
+
|
|
14
|
+
def self.current_survey
|
|
15
|
+
@@current_survey
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.current_survey=(value)
|
|
19
|
+
@@current_survey = value
|
|
20
|
+
puts "Assigning current survey #{@@current_survey.title}"
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# id, parser, and title required
|
|
24
|
+
def initialize(id, parser, title, options = {})
|
|
25
|
+
self.id = id
|
|
26
|
+
self.parser = parser
|
|
27
|
+
self.title = title
|
|
28
|
+
self.survey_sections = []
|
|
29
|
+
self.access_code = Columnizer.to_normalized_column(title)
|
|
30
|
+
# self.default_options(title).merge(options).each{|key,value| self.instance_variable_set("@#{key}", value)}
|
|
31
|
+
Survey.current_survey = self
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# def default_options(title)
|
|
35
|
+
# {}
|
|
36
|
+
# end
|
|
37
|
+
|
|
38
|
+
# This method_missing magic does all the heavy lifting for the DSL
|
|
39
|
+
def method_missing(missing_method, *args, &block)
|
|
40
|
+
m_name = missing_method.to_s
|
|
41
|
+
if (section_match = m_name.match(/(\Asection)_?(.*)/))
|
|
42
|
+
build_survey_section(section_match[3].to_s, *args, &block) # Parse "section" method, create new section in this survey
|
|
43
|
+
else
|
|
44
|
+
puts " ERROR: '#{m_name}' not valid method_missing name"
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def build_survey_section(reference_identifier, title, &block)
|
|
49
|
+
puts " Section: #{title}"
|
|
50
|
+
if block_given?
|
|
51
|
+
new_survey_section = SurveySection.new(new_survey_section_id, self, title, {:display_order => self.survey_sections.size + 1, :reference_identifier => reference_identifier})
|
|
52
|
+
new_survey_section.instance_eval(&block)
|
|
53
|
+
add_survey_section(new_survey_section)
|
|
54
|
+
puts " Section added"
|
|
55
|
+
else
|
|
56
|
+
puts " ERROR: A section cannot be empty!"
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def new_survey_section_id
|
|
61
|
+
self.parser.new_survey_section_id
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def add_survey_section(survey_section)
|
|
65
|
+
self.survey_sections << survey_section
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Used to find questions for dependency linking
|
|
69
|
+
def find_question_by_reference(ref_id)
|
|
70
|
+
question = nil
|
|
71
|
+
count = 0
|
|
72
|
+
while question.nil? and count < self.survey_sections.size
|
|
73
|
+
question = self.survey_sections[count].find_question_by_reference(ref_id)
|
|
74
|
+
count += 1
|
|
75
|
+
end
|
|
76
|
+
question
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def reconcile_dependencies
|
|
81
|
+
|
|
82
|
+
@survey_sections.each do |section|
|
|
83
|
+
section.questions.each do |question|
|
|
84
|
+
question.dependency.dependency_conditions.each { |con| con.reconcile_dependencies} unless question.dependency.nil?
|
|
85
|
+
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def to_yml
|
|
92
|
+
out =[ %(survey_#{@id}:) ]
|
|
93
|
+
out << %( id: #{@id})
|
|
94
|
+
out << %( title: "#{@title}")
|
|
95
|
+
out << %( description: "#{@description}")
|
|
96
|
+
out << %( access_code: #{@access_code})
|
|
97
|
+
out << %( active_at: #{@active_at})
|
|
98
|
+
out << %( inactive_at: #{@inactive_at})
|
|
99
|
+
(out << nil ).join("\r\n")
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def to_file
|
|
103
|
+
"survey -#{self.title}- written to file..."
|
|
104
|
+
File.open(self.parser.surveys_yml, File::CREAT|File::APPEND|File::WRONLY) {|f| f << to_yml}
|
|
105
|
+
self.survey_sections.compact.map(&:to_file)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
end
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
require File.dirname(__FILE__) + '/question_group'
|
|
2
|
+
require File.dirname(__FILE__) + '/question'
|
|
3
|
+
require File.dirname(__FILE__) + '/answer'
|
|
4
|
+
require File.dirname(__FILE__) + '/dependency'
|
|
5
|
+
require File.dirname(__FILE__) + '/dependency_condition'
|
|
6
|
+
require File.dirname(__FILE__) + '/columnizer'
|
|
7
|
+
require 'activesupport'
|
|
8
|
+
#require 'activesupport/lib/active_support/core_ext/string/inflections'
|
|
9
|
+
|
|
10
|
+
class SurveySection
|
|
11
|
+
include Columnizer
|
|
12
|
+
|
|
13
|
+
# Context, Content, Display, Reference, Children, Placeholders
|
|
14
|
+
attr_accessor :id, :survey_id, :parser
|
|
15
|
+
attr_accessor :title, :description
|
|
16
|
+
attr_accessor :display_order
|
|
17
|
+
attr_accessor :reference_identifier, :data_export_identifier
|
|
18
|
+
attr_accessor :question_groups, :questions, :grid_answers
|
|
19
|
+
attr_accessor :current_question_group, :current_question, :current_dependency
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# id, survey, and title required
|
|
23
|
+
def initialize(id, survey, title, options = {})
|
|
24
|
+
self.id = id
|
|
25
|
+
self.survey_id = survey.id
|
|
26
|
+
self.parser = survey.parser
|
|
27
|
+
self.title = title.strip
|
|
28
|
+
self.questions = []
|
|
29
|
+
self.question_groups = []
|
|
30
|
+
self.grid_answers = []
|
|
31
|
+
self.current_question_group = nil
|
|
32
|
+
self.current_question = nil
|
|
33
|
+
self.current_dependency = nil
|
|
34
|
+
self.default_options(title).merge(options).each{|key,value| self.instance_variable_set("@#{key}", value)}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def default_options(title)
|
|
38
|
+
{ :data_export_identifier => Columnizer.to_normalized_column(title)
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# This method_missing magic does all the heavy lifting for the DSL
|
|
43
|
+
def method_missing(missing_method, *args, &block)
|
|
44
|
+
method_name, reference_identifier = missing_method.to_s.split("_")
|
|
45
|
+
|
|
46
|
+
case method_name
|
|
47
|
+
|
|
48
|
+
when "group", "g", "grid", "repeater"
|
|
49
|
+
puts " Group: #{reference_identifier}"
|
|
50
|
+
raise "Error: A question group cannot be empty" unless block_given?
|
|
51
|
+
|
|
52
|
+
options = {:reference_identifier => reference_identifier, :display_type => (method_name =~ /grid|repeater/)? method_name : nil }
|
|
53
|
+
self.question_groups << (self.current_question_group = QuestionGroup.new(self, args, options))
|
|
54
|
+
self.instance_eval(&block)
|
|
55
|
+
clear_current_question_group
|
|
56
|
+
|
|
57
|
+
when "question", "q", "label", "image"
|
|
58
|
+
puts " Question: #{reference_identifier}"
|
|
59
|
+
raise "Error: I'm dropping the block like it's hot" if block_given?
|
|
60
|
+
|
|
61
|
+
clear_current_question
|
|
62
|
+
options = {:reference_identifier => reference_identifier, :question_group_id => (current_question_group ? current_question_group.id : nil)}
|
|
63
|
+
options.merge!({:display_type => "label"}) if method_name == "label"
|
|
64
|
+
options.merge!({:display_type => "image"}) if method_name == "image"
|
|
65
|
+
self.questions << (self.current_question = Question.new(self, args, options))
|
|
66
|
+
add_grid_answers if self.current_question_group and self.current_question_group.display_type == "grid"
|
|
67
|
+
|
|
68
|
+
when "dependency", "d"
|
|
69
|
+
puts " Dependency: #{reference_identifier}"
|
|
70
|
+
raise "Error: I'm dropping the block like it's hot" if block_given?
|
|
71
|
+
raise "Error: " unless self.current_question
|
|
72
|
+
|
|
73
|
+
options = {:reference_identifier => reference_identifier}
|
|
74
|
+
self.current_question.dependency = (self.current_dependency = Dependency.new(current_question, args, options))
|
|
75
|
+
|
|
76
|
+
when "condition", "c"
|
|
77
|
+
puts " Condition: #{reference_identifier}"
|
|
78
|
+
raise "Error, I'm dropping the block like it's hot" if block_given?
|
|
79
|
+
raise "Error: No current dependency" unless self.current_dependency
|
|
80
|
+
|
|
81
|
+
options = {:reference_identifier => reference_identifier}
|
|
82
|
+
self.current_dependency.add_dependency_condition DependencyCondition.new(self, args, options)
|
|
83
|
+
|
|
84
|
+
when "answer", "a"
|
|
85
|
+
puts " Answer: #{reference_identifier}"
|
|
86
|
+
raise "Error, I'm dropping the block like it's hot" if block_given?
|
|
87
|
+
|
|
88
|
+
if self.current_question_group and self.current_question_group.display_type == "grid"
|
|
89
|
+
options = {:reference_identifier => reference_identifier, :display_order => self.grid_answers.size + 1}
|
|
90
|
+
self.grid_answers << Answer.new(nil, args, options)
|
|
91
|
+
else
|
|
92
|
+
raise "Error: No current question" unless self.current_question
|
|
93
|
+
options = {:reference_identifier => reference_identifier, :display_order => self.current_question.answers.size + 1}
|
|
94
|
+
self.current_question.answers << Answer.new(self.current_question, args, options)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
else
|
|
98
|
+
raise " ERROR: '#{m_name}' not valid method_missing name"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def clear_current_question_group
|
|
103
|
+
self.current_question_group = nil
|
|
104
|
+
self.grid_answers = []
|
|
105
|
+
self.current_question = nil
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def clear_current_question
|
|
109
|
+
self.current_question = nil
|
|
110
|
+
self.current_dependency = nil
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def add_grid_answers
|
|
114
|
+
self.grid_answers.each do |grid_answer|
|
|
115
|
+
my_answer = grid_answer.dup
|
|
116
|
+
my_answer.id = self.parser.new_answer_id
|
|
117
|
+
my_answer.question_id = self.current_question.id
|
|
118
|
+
my_answer.parser = self.parser
|
|
119
|
+
self.current_question.answers << my_answer
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Used to find questions for dependency linking
|
|
124
|
+
def find_question_by_reference(ref_id)
|
|
125
|
+
question = nil
|
|
126
|
+
count = 0
|
|
127
|
+
while question.nil? and count < questions.size
|
|
128
|
+
question = self.questions[count] if self.questions[count].reference_identifier == ref_id
|
|
129
|
+
count +=1
|
|
130
|
+
end
|
|
131
|
+
question
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def to_yml
|
|
136
|
+
out =[ %(#{@data_export_identifier}_#{@id}:) ]
|
|
137
|
+
out << %( id: #{@id})
|
|
138
|
+
out << %( survey_id: #{@survey_id})
|
|
139
|
+
out << %( title: "#{@title}")
|
|
140
|
+
out << %( description: "#{@description}")
|
|
141
|
+
out << %( display_order: #{display_order})
|
|
142
|
+
out << %( reference_identifier: #{@reference_identifier})
|
|
143
|
+
out << %( data_export_identifier: "#{@data_export_identifier}")
|
|
144
|
+
(out << nil ).join("\r\n")
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def to_file
|
|
148
|
+
File.open(self.parser.survey_sections_yml, File::CREAT|File::APPEND|File::WRONLY) {|f| f << to_yml}
|
|
149
|
+
self.question_groups.compact.map(&:to_file)
|
|
150
|
+
self.questions.compact.map(&:to_file)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
end
|