helena 0.3.3

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.
Files changed (202) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.gitignore +15 -0
  4. data/.rubocop.yml +16 -0
  5. data/.travis.yml +6 -0
  6. data/Gemfile +31 -0
  7. data/Gemfile.lock +290 -0
  8. data/LICENSE +16 -0
  9. data/README.md +90 -0
  10. data/Rakefile +29 -0
  11. data/app/assets/images/helena/.keep +0 -0
  12. data/app/assets/images/helena/helena_mobile_view.jpg +0 -0
  13. data/app/assets/images/helena/icons/export.svg +29 -0
  14. data/app/assets/images/helena/swissmadesoftware.png +0 -0
  15. data/app/assets/javascripts/helena/application.js +4 -0
  16. data/app/assets/javascripts/helena/survey.js +2 -0
  17. data/app/assets/stylesheets/helena/admin/labels.css.sass +7 -0
  18. data/app/assets/stylesheets/helena/admin/layout.css.sass +6 -0
  19. data/app/assets/stylesheets/helena/admin/question_groups.css.sass +7 -0
  20. data/app/assets/stylesheets/helena/admin/questions.css.sass +9 -0
  21. data/app/assets/stylesheets/helena/admin/sessions.css.sass +5 -0
  22. data/app/assets/stylesheets/helena/admin/subquestions.css.sass +7 -0
  23. data/app/assets/stylesheets/helena/admin/versions.css.sass +9 -0
  24. data/app/assets/stylesheets/helena/application.css.sass +21 -0
  25. data/app/assets/stylesheets/helena/forms.css.sass +13 -0
  26. data/app/assets/stylesheets/helena/layout.css.sass +3 -0
  27. data/app/assets/stylesheets/helena/question_groups.css.sass +4 -0
  28. data/app/controllers/helena/admin/application_controller.rb +31 -0
  29. data/app/controllers/helena/admin/question_groups_controller.rb +78 -0
  30. data/app/controllers/helena/admin/questions/checkbox_groups_controller.rb +17 -0
  31. data/app/controllers/helena/admin/questions/checkbox_matrix_controller.rb +9 -0
  32. data/app/controllers/helena/admin/questions/long_texts_controller.rb +13 -0
  33. data/app/controllers/helena/admin/questions/radio_groups_controller.rb +17 -0
  34. data/app/controllers/helena/admin/questions/radio_matrix_controller.rb +9 -0
  35. data/app/controllers/helena/admin/questions/short_texts_controller.rb +13 -0
  36. data/app/controllers/helena/admin/questions/static_texts_controller.rb +13 -0
  37. data/app/controllers/helena/admin/questions_controller.rb +105 -0
  38. data/app/controllers/helena/admin/sessions_controller.rb +35 -0
  39. data/app/controllers/helena/admin/surveys_controller.rb +78 -0
  40. data/app/controllers/helena/admin/versions_controller.rb +71 -0
  41. data/app/controllers/helena/application_controller.rb +15 -0
  42. data/app/controllers/helena/concerns/questions/matrix_questions.rb +24 -0
  43. data/app/controllers/helena/sessions_controller.rb +93 -0
  44. data/app/controllers/helena/surveys_controller.rb +11 -0
  45. data/app/helpers/helena/application_helper.rb +4 -0
  46. data/app/models/helena/answer.rb +48 -0
  47. data/app/models/helena/boolean_answer.rb +5 -0
  48. data/app/models/helena/concerns/application_model.rb +19 -0
  49. data/app/models/helena/concerns/questions/requirable.rb +19 -0
  50. data/app/models/helena/concerns/questions/validates_one_label.rb +22 -0
  51. data/app/models/helena/integer_answer.rb +5 -0
  52. data/app/models/helena/label.rb +18 -0
  53. data/app/models/helena/question.rb +63 -0
  54. data/app/models/helena/question_group.rb +18 -0
  55. data/app/models/helena/questions/checkbox_group.rb +17 -0
  56. data/app/models/helena/questions/long_text.rb +9 -0
  57. data/app/models/helena/questions/radio_group.rb +12 -0
  58. data/app/models/helena/questions/radio_matrix.rb +24 -0
  59. data/app/models/helena/questions/short_text.rb +9 -0
  60. data/app/models/helena/questions/static_text.rb +7 -0
  61. data/app/models/helena/session.rb +76 -0
  62. data/app/models/helena/string_answer.rb +5 -0
  63. data/app/models/helena/sub_question.rb +27 -0
  64. data/app/models/helena/survey.rb +29 -0
  65. data/app/models/helena/survey_detail.rb +10 -0
  66. data/app/models/helena/version.rb +33 -0
  67. data/app/views/helena/admin/question_groups/_form.html.haml +3 -0
  68. data/app/views/helena/admin/question_groups/edit.html.haml +3 -0
  69. data/app/views/helena/admin/question_groups/index.html.haml +41 -0
  70. data/app/views/helena/admin/question_groups/new.html.haml +3 -0
  71. data/app/views/helena/admin/questions/_form.html.haml +15 -0
  72. data/app/views/helena/admin/questions/_labels.html.haml +24 -0
  73. data/app/views/helena/admin/questions/_sub_questions.html.haml +32 -0
  74. data/app/views/helena/admin/questions/edit.html.haml +3 -0
  75. data/app/views/helena/admin/questions/index.html.haml +49 -0
  76. data/app/views/helena/admin/questions/new.html.haml +3 -0
  77. data/app/views/helena/admin/sessions/index.html.haml +46 -0
  78. data/app/views/helena/admin/surveys/_form.html.haml +10 -0
  79. data/app/views/helena/admin/surveys/edit.html.haml +5 -0
  80. data/app/views/helena/admin/surveys/index.html.haml +35 -0
  81. data/app/views/helena/admin/surveys/new.html.haml +3 -0
  82. data/app/views/helena/admin/versions/_form.html.haml +5 -0
  83. data/app/views/helena/admin/versions/default_session_report.html.haml +17 -0
  84. data/app/views/helena/admin/versions/edit.html.haml +3 -0
  85. data/app/views/helena/admin/versions/index.html.haml +35 -0
  86. data/app/views/helena/admin/versions/new.html.haml +3 -0
  87. data/app/views/helena/questions/_checkbox_group.html.haml +17 -0
  88. data/app/views/helena/questions/_long_text.html.haml +8 -0
  89. data/app/views/helena/questions/_radio_group.html.haml +13 -0
  90. data/app/views/helena/questions/_radio_matrix.html.haml +27 -0
  91. data/app/views/helena/questions/_short_text.html.haml +8 -0
  92. data/app/views/helena/sessions/edit.html.haml +16 -0
  93. data/app/views/helena/sessions/end_message.html.haml +5 -0
  94. data/app/views/helena/surveys/_survey.html.haml +3 -0
  95. data/app/views/helena/surveys/index.html.haml +2 -0
  96. data/bin/rails +8 -0
  97. data/config/i18n-tasks.yml +3 -0
  98. data/config/initializers/simple_form.rb +162 -0
  99. data/config/initializers/simple_form_bootstrap.rb +132 -0
  100. data/config/locales/en.yml +20 -0
  101. data/config/locales/models/label/en.yml +14 -0
  102. data/config/locales/models/question/en.yml +47 -0
  103. data/config/locales/models/question_group/en.yml +11 -0
  104. data/config/locales/models/session/en.yml +10 -0
  105. data/config/locales/models/sub_question/en.yml +14 -0
  106. data/config/locales/models/survey/en.yml +6 -0
  107. data/config/locales/models/version/en.yml +12 -0
  108. data/config/locales/simple_form.en.yml +26 -0
  109. data/config/locales/views/admin/question_groups/en.yml +12 -0
  110. data/config/locales/views/admin/questions/en.yml +16 -0
  111. data/config/locales/views/admin/sessions/en.yml +11 -0
  112. data/config/locales/views/admin/surveys/en.yml +10 -0
  113. data/config/locales/views/admin/versions/en.yml +8 -0
  114. data/config/locales/views/sessions/en.yml +9 -0
  115. data/config/routes.rb +28 -0
  116. data/db/seeds.rb +182 -0
  117. data/gpl-3.0.txt +674 -0
  118. data/helena.gemspec +40 -0
  119. data/lib/helena/engine.rb +6 -0
  120. data/lib/helena/version.rb +3 -0
  121. data/lib/helena/version_publisher.rb +16 -0
  122. data/lib/helena.rb +10 -0
  123. data/lib/tasks/helena_tasks.rake +4 -0
  124. data/lib/templates/erb/scaffold/_form.html.erb +13 -0
  125. data/spec/controllers/helena/admin/question_group_controller_spec.rb +59 -0
  126. data/spec/controllers/helena/admin/questions_controller_spec.rb +60 -0
  127. data/spec/controllers/helena/admin/sessions_controller_spec.rb +84 -0
  128. data/spec/controllers/helena/admin/survey_controller_spec.rb +89 -0
  129. data/spec/controllers/helena/admin/versions_controller_spec.rb +19 -0
  130. data/spec/controllers/helena/application_controller_spec.rb +15 -0
  131. data/spec/controllers/helena/sessions_controller_spec.rb +24 -0
  132. data/spec/dummy/README.rdoc +28 -0
  133. data/spec/dummy/Rakefile +6 -0
  134. data/spec/dummy/app/assets/images/.keep +0 -0
  135. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  136. data/spec/dummy/app/controllers/application_controller.rb +9 -0
  137. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  138. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  139. data/spec/dummy/app/mailers/.keep +0 -0
  140. data/spec/dummy/app/models/.keep +0 -0
  141. data/spec/dummy/app/models/concerns/.keep +0 -0
  142. data/spec/dummy/app/views/layouts/application.html.haml +24 -0
  143. data/spec/dummy/bin/bundle +3 -0
  144. data/spec/dummy/bin/rails +4 -0
  145. data/spec/dummy/bin/rake +4 -0
  146. data/spec/dummy/config/application.rb +27 -0
  147. data/spec/dummy/config/boot.rb +5 -0
  148. data/spec/dummy/config/environment.rb +5 -0
  149. data/spec/dummy/config/environments/development.rb +29 -0
  150. data/spec/dummy/config/environments/production.rb +80 -0
  151. data/spec/dummy/config/environments/test.rb +36 -0
  152. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  153. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  154. data/spec/dummy/config/initializers/session_store.rb +3 -0
  155. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  156. data/spec/dummy/config/locales/en.yml +23 -0
  157. data/spec/dummy/config/mongoid.yml +14 -0
  158. data/spec/dummy/config/routes.rb +4 -0
  159. data/spec/dummy/config.ru +4 -0
  160. data/spec/dummy/db/seeds.rb +1 -0
  161. data/spec/dummy/lib/assets/.keep +0 -0
  162. data/spec/dummy/log/.keep +0 -0
  163. data/spec/dummy/public/404.html +58 -0
  164. data/spec/dummy/public/422.html +58 -0
  165. data/spec/dummy/public/500.html +57 -0
  166. data/spec/dummy/public/favicon.ico +0 -0
  167. data/spec/factories/helena/answers.rb +24 -0
  168. data/spec/factories/helena/labels.rb +7 -0
  169. data/spec/factories/helena/question_groups.rb +6 -0
  170. data/spec/factories/helena/questions.rb +14 -0
  171. data/spec/factories/helena/sessions.rb +9 -0
  172. data/spec/factories/helena/sub_questions.rb +7 -0
  173. data/spec/factories/helena/survey_details.rb +6 -0
  174. data/spec/factories/helena/surveys.rb +7 -0
  175. data/spec/factories/helena/versions.rb +11 -0
  176. data/spec/features/helena/admin/manage_question_group_spec.rb +101 -0
  177. data/spec/features/helena/admin/manage_question_spec.rb +105 -0
  178. data/spec/features/helena/admin/manage_session_spec.rb +40 -0
  179. data/spec/features/helena/admin/manage_survey_spec.rb +141 -0
  180. data/spec/features/helena/admin/manage_version_spec.rb +74 -0
  181. data/spec/features/helena/admin/question_types/manage_checkbox_group_question_spec.rb +55 -0
  182. data/spec/features/helena/admin/question_types/manage_long_text_question_spec.rb +21 -0
  183. data/spec/features/helena/admin/question_types/manage_radio_group_question_spec.rb +59 -0
  184. data/spec/features/helena/admin/question_types/manage_radio_matrix_question_spec.rb +80 -0
  185. data/spec/features/helena/admin/question_types/manage_short_text_question_spec.rb +19 -0
  186. data/spec/features/helena/admin/question_types/manage_static_text_question_spec.rb +17 -0
  187. data/spec/features/helena/manage_session_spec.rb +284 -0
  188. data/spec/features/helena/manage_survey_spec.rb +24 -0
  189. data/spec/lib/version_publisher_spec.rb +26 -0
  190. data/spec/models/helena/answer_spec.rb +53 -0
  191. data/spec/models/helena/label_spec.rb +13 -0
  192. data/spec/models/helena/question_group_spec.rb +11 -0
  193. data/spec/models/helena/question_spec.rb +21 -0
  194. data/spec/models/helena/questions/radio_group_spec.rb +32 -0
  195. data/spec/models/helena/questions/radio_matrix_spec.rb +32 -0
  196. data/spec/models/helena/questions/short_text_spec.rb +10 -0
  197. data/spec/models/helena/session_spec.rb +20 -0
  198. data/spec/models/helena/sub_question_spec.rb +28 -0
  199. data/spec/models/helena/survey_spec.rb +14 -0
  200. data/spec/models/helena/version_spec.rb +15 -0
  201. data/spec/spec_helper.rb +43 -0
  202. metadata +485 -0
@@ -0,0 +1,48 @@
1
+ module Helena
2
+ class Answer
3
+ include Helena::Concerns::ApplicationModel
4
+
5
+ field :code, type: String
6
+ field :ip_address, type: String
7
+ field :created_at, type: DateTime, default: -> { DateTime.now }
8
+
9
+ embedded_in :session, inverse_of: :answers
10
+
11
+ validates :code, :ip_address, presence: true
12
+ validates :code, uniqueness: true
13
+
14
+ def self.build_generic(code, value, ip_address)
15
+ value = cast_value(value)
16
+ answer_class_for(value).new(code: code, value: value, ip_address: ip_address)
17
+ end
18
+
19
+ def self.answer_class_for(value)
20
+ case value
21
+ when Fixnum
22
+ Helena::IntegerAnswer
23
+ when TrueClass
24
+ Helena::BooleanAnswer
25
+ when FalseClass
26
+ Helena::BooleanAnswer
27
+ when String
28
+ Helena::StringAnswer
29
+ end
30
+ end
31
+
32
+ def self.cast_value(value)
33
+ if value == 'true'
34
+ true
35
+ elsif value == 'false'
36
+ false
37
+ elsif integer?(value)
38
+ value.to_i
39
+ else
40
+ value.to_s
41
+ end
42
+ end
43
+
44
+ def self.integer?(str)
45
+ Integer(str) rescue false
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,5 @@
1
+ module Helena
2
+ class BooleanAnswer < Answer
3
+ field :value, type: Boolean
4
+ end
5
+ end
@@ -0,0 +1,19 @@
1
+ module Helena
2
+ module Concerns
3
+ module ApplicationModel
4
+ extend ActiveSupport::Concern
5
+
6
+ include Mongoid::Document
7
+ include Mongoid::Timestamps
8
+
9
+ included do
10
+ before_destroy :removable?
11
+
12
+ # Removable is given by default. Override for custom behaviour
13
+ def removable?
14
+ true
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ module Helena
2
+ module Concerns
3
+ module Questions
4
+ module Requirable
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ field :required, type: Boolean, default: false
9
+ end
10
+
11
+ def validate_answers_in(answers)
12
+ errors = {}
13
+ errors[code] = :blank if answers[code].blank? && required
14
+ errors
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ module Helena
2
+ module Concerns
3
+ module Questions
4
+ module ValidatesOneLabel
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ validate :labels_preselection
9
+ end
10
+
11
+ private
12
+
13
+ def labels_preselection
14
+ selected_labels = labels.select { |label| label.preselected == true }
15
+ return if selected_labels.size <= 1
16
+ selected_labels.each { |label| label.errors.add(:preselected, I18n.t(:taken, scope: 'activerecord.errors.messages')) }
17
+ errors.add(:labels, I18n.t('helena.admin.radio_group.only_one_preselection_allowed'))
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ module Helena
2
+ class IntegerAnswer < Answer
3
+ field :value, type: Integer
4
+ end
5
+ end
@@ -0,0 +1,18 @@
1
+ module Helena
2
+ class Label
3
+ include Helena::Concerns::ApplicationModel
4
+ include Mongoid::Orderable
5
+
6
+ field :text, type: String
7
+ field :value, type: String
8
+ field :preselected, type: Boolean, default: false
9
+
10
+ embedded_in :question, inverse_of: :labels
11
+
12
+ orderable
13
+
14
+ validates :text, presence: true
15
+ validates :value, presence: true
16
+ validates :value, uniqueness: true
17
+ end
18
+ end
@@ -0,0 +1,63 @@
1
+ module Helena
2
+ class Question
3
+ include Helena::Concerns::ApplicationModel
4
+ include Mongoid::Orderable
5
+
6
+ CODE_FORMAT = /\A[a-z]([-\w]{,498}[a-z\d])?\Z/
7
+
8
+ TYPES = [
9
+ Helena::Questions::ShortText,
10
+ Helena::Questions::LongText,
11
+ Helena::Questions::StaticText,
12
+ Helena::Questions::RadioGroup,
13
+ Helena::Questions::CheckboxGroup,
14
+ Helena::Questions::RadioMatrix
15
+ ]
16
+
17
+ embedded_in :question_group, inverse_of: :questions
18
+
19
+ embeds_many :labels, class_name: 'Helena::Label'
20
+ embeds_many :sub_questions, class_name: 'Helena::SubQuestion'
21
+
22
+ accepts_nested_attributes_for :labels, allow_destroy: true, reject_if: :reject_labels
23
+ accepts_nested_attributes_for :sub_questions, allow_destroy: true, reject_if: :reject_sub_questions
24
+
25
+ field :code, type: String
26
+ field :question_text, type: String
27
+
28
+ orderable
29
+
30
+ validates :code, presence: true
31
+
32
+ # consist of lowercase characters or digits, not starting with a digit or underscore and not ending with an underscore
33
+ # foo_32: correct, 32_foo: incorrect, _bar: incorrect, bar_: incorrect, FooBaar: incorrect
34
+ validates :code, format: { with: CODE_FORMAT }
35
+ validate :uniqueness_of_code
36
+
37
+ def includes_labels?
38
+ false
39
+ end
40
+
41
+ def includes_subquestions?
42
+ false
43
+ end
44
+
45
+ private
46
+
47
+ def uniqueness_of_code
48
+ question_code_occurences = question_group.version.question_code_occurences
49
+ errors.add :code, :taken if question_code_occurences[code] > 1
50
+ end
51
+
52
+ def reject_labels(attributed)
53
+ attributed['text'].blank? &&
54
+ attributed['value'].blank?
55
+ end
56
+
57
+ def reject_sub_questions(attributed)
58
+ attributed['code'].blank? &&
59
+ attributed['default_value'].blank? &&
60
+ attributed['question_text'].blank?
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,18 @@
1
+ module Helena
2
+ class QuestionGroup
3
+ include Helena::Concerns::ApplicationModel
4
+ include Mongoid::Orderable
5
+
6
+ embedded_in :version
7
+
8
+ embeds_many :questions, class_name: 'Helena::Question'
9
+
10
+ orderable
11
+
12
+ field :title, type: String
13
+
14
+ def question_codes
15
+ questions.map { |question| [question.code] + question.sub_questions.map(&:code) }.flatten
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ module Helena
2
+ module Questions
3
+ class CheckboxGroup < Helena::Question
4
+ include Helena::Concerns::Questions::Requirable
5
+
6
+ def includes_subquestions?
7
+ true
8
+ end
9
+
10
+ def validate_answers_in(answers)
11
+ errors = {}
12
+ errors[code] = :blank if sub_questions.map { |sub_question| answers[sub_question.code] == 0 }.all? && required
13
+ errors
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Helena
2
+ module Questions
3
+ class LongText < Helena::Question
4
+ include Helena::Concerns::Questions::Requirable
5
+
6
+ field :default_value, type: String
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ module Helena
2
+ module Questions
3
+ class RadioGroup < Helena::Question
4
+ include Helena::Concerns::Questions::Requirable
5
+ include Helena::Concerns::Questions::ValidatesOneLabel
6
+
7
+ def includes_labels?
8
+ true
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,24 @@
1
+ module Helena
2
+ module Questions
3
+ class RadioMatrix < Helena::Question
4
+ include Helena::Concerns::Questions::Requirable
5
+ include Helena::Concerns::Questions::ValidatesOneLabel
6
+
7
+ def includes_labels?
8
+ true
9
+ end
10
+
11
+ def includes_subquestions?
12
+ true
13
+ end
14
+
15
+ def validate_answers_in(answers)
16
+ errors = {}
17
+ sub_questions.each do |sub_question|
18
+ errors[sub_question.code] = :blank if answers[sub_question.code].blank? && required
19
+ end
20
+ errors
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,9 @@
1
+ module Helena
2
+ module Questions
3
+ class ShortText < Helena::Question
4
+ include Helena::Concerns::Questions::Requirable
5
+
6
+ field :default_value, type: String
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Helena
2
+ module Questions
3
+ class StaticText < Helena::Question
4
+ field :default_value, type: String
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,76 @@
1
+ module Helena
2
+ class Session
3
+ include Helena::Concerns::ApplicationModel
4
+
5
+ field :token, type: String
6
+ field :view_token, type: String
7
+ field :completed, type: Boolean, default: false
8
+
9
+ belongs_to :survey, inverse_of: :sessions, class_name: 'Helena::Survey'
10
+ belongs_to :version, inverse_of: :sessions, class_name: 'Helena::Version'
11
+
12
+ embeds_many :answers, inverse_of: :session, class_name: 'Helena::Answer'
13
+
14
+ validates :token, :view_token, uniqueness: true
15
+
16
+ before_create :reset_tokens
17
+
18
+ def reset_tokens
19
+ # NOTE: there are (2*26+10)^k tokens available
20
+ # To not run into performance issues we could pregenerate unique tokens in the future
21
+ self.token = generate_token(5) until unique_token_for?
22
+ self.view_token = generate_token(25) until unique_token_for?(:view_token)
23
+ end
24
+
25
+ def self.to_csv
26
+ CSV.generate do |csv|
27
+ csv << fields.keys + uniq_answer_codes
28
+ all.each do |session|
29
+ csv << session.attributes.values_at(*fields.keys) + answer_values_in(session)
30
+ end
31
+ end
32
+ end
33
+
34
+ def as_json(options)
35
+ session = super(options)
36
+ session[:answer] = answers_as_hash
37
+ session
38
+ end
39
+
40
+ def answers_as_hash
41
+ answers.map { |answer| [answer[:code], answer[:value]] }.to_h
42
+ end
43
+
44
+ private
45
+
46
+ def self.answer_values_in(session)
47
+ answers = []
48
+ answer_codes.each do |code|
49
+ answers << session.answers.where(code: code).first.try(&:value)
50
+ end
51
+ answers
52
+ end
53
+
54
+ def self.answer_codes
55
+ answer_codes = []
56
+ all.each do |session|
57
+ answer_codes += session.answers.map(&:code) - answer_codes
58
+ end
59
+ answer_codes
60
+ end
61
+
62
+ def self.uniq_answer_codes
63
+ answer_codes.map do |code|
64
+ fields.keys.include?(code) ? "answer_#{code}" : code
65
+ end
66
+ end
67
+
68
+ def generate_token(size)
69
+ SecureRandom.base64(size).delete('/+=')[0, size]
70
+ end
71
+
72
+ def unique_token_for?(field = :token)
73
+ self.class.where(field => send(field)).blank? && send(field).present?
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,5 @@
1
+ module Helena
2
+ class StringAnswer < Answer
3
+ field :value, type: String
4
+ end
5
+ end
@@ -0,0 +1,27 @@
1
+ module Helena
2
+ class SubQuestion
3
+ include Helena::Concerns::ApplicationModel
4
+ include Mongoid::Orderable
5
+
6
+ field :text, type: String
7
+ field :value, type: String
8
+ field :code, type: String
9
+ field :preselected, type: Boolean
10
+
11
+ embedded_in :question, inverse_of: :sub_questions
12
+
13
+ orderable
14
+
15
+ validates :code, format: { with: Helena::Question::CODE_FORMAT }
16
+ validate :uniqueness_of_code
17
+ validates :text, presence: true, uniqueness: true
18
+
19
+ def uniqueness_of_code
20
+ question_code_occurences = question.question_group.version.question_code_occurences
21
+
22
+ return true if question_code_occurences[code] <= 1
23
+
24
+ errors.add(:code, :taken, value: code)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,29 @@
1
+ module Helena
2
+ class Survey
3
+ include Helena::Concerns::ApplicationModel
4
+ include Mongoid::Orderable
5
+ include Mongoid::Document::Taggable
6
+
7
+ orderable
8
+
9
+ field :name, type: String
10
+ field :language, type: String
11
+ field :description, type: String
12
+
13
+ embeds_many :versions, inverse_of: :survey, class_name: 'Helena::Version'
14
+ has_many :sessions, inverse_of: :survey, dependent: :destroy, class_name: 'Helena::Session'
15
+
16
+ accepts_nested_attributes_for :versions
17
+
18
+ validates :name, presence: true, uniqueness: true
19
+ validates :language, presence: true
20
+
21
+ def draft_version
22
+ versions.find_by version: 0
23
+ end
24
+
25
+ def newest_version
26
+ versions.find_by version: versions.max(:version)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,10 @@
1
+ module Helena
2
+ class SurveyDetail
3
+ include Helena::Concerns::ApplicationModel
4
+
5
+ field :title, type: String
6
+ field :description, type: String
7
+
8
+ embedded_in :version, inverse_of: :survey_detail
9
+ end
10
+ end
@@ -0,0 +1,33 @@
1
+ module Helena
2
+ class Version
3
+ include Helena::Concerns::ApplicationModel
4
+ include Mongoid::Orderable # Needed, because the embedded objects needs this, see
5
+
6
+ field :version, type: Integer, default: 0
7
+ field :notes, type: String
8
+ field :session_report, type: String
9
+
10
+ embedded_in :survey
11
+
12
+ embeds_many :question_groups, class_name: 'Helena::QuestionGroup'
13
+ embeds_many :sessions, class_name: 'Helena::Session'
14
+
15
+ embeds_one :survey_detail, class_name: 'Helena::SurveyDetail'
16
+
17
+ accepts_nested_attributes_for :survey_detail
18
+ accepts_nested_attributes_for :question_groups
19
+
20
+ scope :without_base, -> { where(:version.gt => 0) }
21
+
22
+ validates :version, presence: true
23
+ validates :version, uniqueness: { scope: :survey_id }
24
+
25
+ def question_codes
26
+ question_groups.map(&:question_codes).flatten
27
+ end
28
+
29
+ def question_code_occurences
30
+ question_codes.each_with_object(Hash.new(0)) { |word, counts| counts[word] += 1 }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ = simple_form_for [:admin, @survey, @question_group] do |f|
2
+ = f.input :title
3
+ = f.submit t('shared.save'), class: 'btn btn-success'
@@ -0,0 +1,3 @@
1
+ .helena-admin
2
+ %h2= t 'shared.edit'
3
+ = render 'form'
@@ -0,0 +1,41 @@
1
+ .helena-admin
2
+ %h2
3
+ = Helena::QuestionGroup.model_name.human(count: 2)
4
+ = "(#{@question_groups.count})"
5
+ - if @question_groups.empty?
6
+ .alert.alert-warning
7
+ = t '.empty'
8
+ - else
9
+ .table-responsive
10
+ %table.table.question_groups
11
+ %thead
12
+ %tr
13
+ %th.position= Helena::QuestionGroup.human_attribute_name(:position)
14
+ %th= Helena::QuestionGroup.human_attribute_name(:title)
15
+ %th
16
+ %tbody
17
+ - @question_groups.each do |question_group|
18
+ %tr[question_group]
19
+ %td.position
20
+ = question_group.position
21
+ %td.title
22
+ = question_group.title
23
+ %td
24
+ .btn-group.btn-group-sm
25
+ - unless question_group.last?
26
+ = link_to move_down_admin_survey_question_group_path(@survey, question_group), class: 'btn btn-primary',
27
+ method: :patch,
28
+ title: t('shared.move_down') do
29
+ %i.glyphicon.glyphicon-chevron-down
30
+ - unless question_group.first?
31
+ = link_to move_up_admin_survey_question_group_path(@survey, question_group), class: 'btn btn-primary',
32
+ method: :patch,
33
+ title: t('shared.move_up') do
34
+ %i.glyphicon.glyphicon-chevron-up
35
+
36
+ = link_to t('shared.edit'), edit_admin_survey_question_group_path(@survey, question_group), class: 'btn btn-primary'
37
+ = link_to Helena::Question.model_name.human(count: 2), admin_survey_question_group_questions_path(@survey, question_group), class: 'btn btn-primary'
38
+ = link_to t('shared.delete'), admin_survey_question_group_path(@survey, question_group), method: :delete, class: 'btn btn-danger', data: { confirm: t('shared.delete_question') }
39
+
40
+ %a.btn.btn-success{ href: new_admin_survey_question_group_path}
41
+ = t '.add'
@@ -0,0 +1,3 @@
1
+ .helena-admin
2
+ %h2= t '.new'
3
+ = render 'form'
@@ -0,0 +1,15 @@
1
+ = simple_form_for [:admin, @survey, @question_group, @question] do |f|
2
+ = f.input :question_text
3
+ = f.input :code, hint: t('shared.code_hint')
4
+ = f.input :_type, collection: Helena::Question::TYPES.map { |type| [type.model_name.human, type] }
5
+ - if @question.respond_to? :required
6
+ = f.input :required, as: :boolean
7
+ - if @question.is_a?(Helena::Questions::LongText) || @question.is_a?(Helena::Questions::StaticText)
8
+ = f.input :default_value
9
+ - if @question.is_a?(Helena::Questions::ShortText)
10
+ = f.input :default_value, as: :string
11
+ - if @question.includes_labels?
12
+ = render 'labels', form: f
13
+ - if @question.includes_subquestions?
14
+ = render 'sub_questions', form: f
15
+ = f.submit t('shared.save'), class: 'btn btn-success'
@@ -0,0 +1,24 @@
1
+ %h3= t '.labels'
2
+ .table-responsive
3
+ %table.table.labels
4
+ %thead
5
+ %tr
6
+ %th.position= Helena::Label.human_attribute_name :position
7
+ %th.preselected
8
+ %th.text= Helena::Label.human_attribute_name :text
9
+ %th.value= Helena::Label.human_attribute_name :value
10
+ %th.delete
11
+ %tbody
12
+ = form.simple_fields_for :labels do |label|
13
+ %tr[label.object]
14
+ %td.position
15
+ = label.input :position, as: :integer, label: false
16
+ %td.preselected
17
+ = label.input :preselected, as: :boolean, label: Helena::Label.human_attribute_name(:preselected)
18
+ %td.text
19
+ = label.input :text, label: false
20
+ %td.value
21
+ = label.input :value, label: false
22
+ %td.delete
23
+ - unless label.object.new_record?
24
+ = label.input :_destroy, as: :boolean, label: Helena::Label.human_attribute_name(:_destroy)
@@ -0,0 +1,32 @@
1
+ %h3= t '.options'
2
+ .table-responsive
3
+ %table.table.sub_questions
4
+ %thead
5
+ %tr
6
+ %th.position= Helena::SubQuestion.human_attribute_name :position
7
+ %th.position= Helena::SubQuestion.human_attribute_name :code
8
+ - if @question.is_a?(Helena::Questions::CheckboxGroup)
9
+ %th.preselected
10
+ %th.text= Helena::SubQuestion.human_attribute_name :text
11
+ - if @question.is_a?(Helena::Questions::CheckboxGroup)
12
+ %th.value= Helena::SubQuestion.human_attribute_name :value
13
+ %th.delete
14
+ %tbody
15
+ = form.simple_fields_for :sub_questions do |sub_question|
16
+ %tr[sub_question.object]
17
+ %td.position
18
+ = sub_question.input :position, as: :integer, label: false
19
+ %td.code
20
+ = sub_question.input :code, label: false, hint: t('shared.code_hint')
21
+ - if @question.is_a?(Helena::Questions::CheckboxGroup)
22
+ %td.preselected
23
+ = sub_question.input :preselected, as: :boolean
24
+ %td.text
25
+ = sub_question.input :text, label: false
26
+ - if @question.is_a?(Helena::Questions::CheckboxGroup)
27
+ %td.value
28
+ = sub_question.input :value, label: false, as: :hidden, value: 1
29
+ %td.delete
30
+ - unless sub_question.object.new_record?
31
+ = sub_question.input :_destroy, as: :boolean,
32
+ label: Helena::SubQuestion.human_attribute_name(:_destroy)
@@ -0,0 +1,3 @@
1
+ .helena-admin
2
+ %h2= t 'shared.edit'
3
+ = render 'form'