helena 1.3.1 → 2.0.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +12 -7
- data/.travis.yml +11 -0
- data/Gemfile +6 -3
- data/Gemfile.lock +175 -188
- data/README.md +3 -4
- data/Rakefile +1 -2
- data/app/assets/javascripts/helena/application.js +0 -1
- data/app/assets/stylesheets/helena/{application.css.sass → application.scss} +0 -8
- data/app/controllers/helena/sessions_controller.rb +7 -1
- data/app/helpers/helena/application_helper.rb +18 -0
- data/app/models/helena/concerns/questions/validates_one_label.rb +1 -0
- data/app/models/helena/question.rb +1 -0
- data/app/models/helena/question_group.rb +8 -0
- data/app/models/helena/questions/static_text.rb +4 -0
- data/app/models/helena/session.rb +1 -19
- data/app/models/helena/sub_question.rb +1 -0
- data/app/models/helena/version.rb +4 -0
- data/app/views/helena/questions/_bipolar_radio_matrix.html.slim +36 -0
- data/app/views/helena/questions/{_checkbox_group.html.haml → _checkbox_group.html.slim} +6 -8
- data/app/views/helena/questions/_long_text.html.slim +9 -0
- data/app/views/helena/questions/{_radio_group.html.haml → _radio_group.html.slim} +5 -6
- data/app/views/helena/questions/_radio_matrix.html.slim +27 -0
- data/app/views/helena/questions/_short_text.html.slim +9 -0
- data/app/views/helena/questions/_static_text.html.slim +2 -4
- data/app/views/helena/sessions/_error_messages.html.slim +8 -0
- data/app/views/helena/sessions/edit.html.slim +11 -7
- data/app/views/helena/sessions/end_message.html.slim +1 -1
- data/config/locales/en.yml +13 -12
- data/config/locales/views/sessions/en.yml +1 -0
- data/gemfiles/rails_4.2.gemfile +18 -15
- data/gemfiles/rails_4.2.gemfile.lock +96 -117
- data/gemfiles/rails_5.1.gemfile +6 -3
- data/gemfiles/rails_5.1.gemfile.lock +127 -146
- data/helena.gemspec +16 -20
- data/lib/helena.rb +0 -3
- data/lib/helena/engine.rb +1 -1
- data/lib/helena/survey_importer.rb +4 -1
- data/lib/helena/version.rb +1 -1
- data/spec/controllers/helena/sessions_controller_spec.rb +2 -2
- data/spec/dummy/Rakefile +1 -1
- data/spec/dummy/app/views/layouts/{application.html.haml → application.html.slim} +10 -10
- data/spec/dummy/bin/bundle +1 -1
- data/spec/dummy/bin/rails +1 -1
- data/spec/dummy/config/application.rb +1 -2
- data/spec/dummy/config/boot.rb +2 -2
- data/spec/dummy/config/environment.rb +1 -1
- data/spec/factories/helena/surveys.rb +1 -1
- data/spec/features/helena/manage_session_spec.rb +40 -36
- data/spec/helpers/application_helper_spec.rb +10 -0
- data/spec/models/helena/session_spec.rb +1 -8
- data/spec/models/helena/sub_question_spec.rb +1 -1
- data/spec/spec_helper.rb +3 -1
- data/spec/support/rails_compatibility.rb +1 -1
- data/test_all_gemfiles.sh +7 -0
- metadata +72 -131
- data/.coveralls.yml +0 -2
- data/Appraisals +0 -12
- data/app/assets/stylesheets/helena/forms.css.sass +0 -13
- data/app/assets/stylesheets/helena/layout.css.sass +0 -3
- data/app/assets/stylesheets/helena/question_groups.css.sass +0 -4
- data/app/views/helena/questions/_bipolar_radio_matrix.html.haml +0 -56
- data/app/views/helena/questions/_long_text.html.haml +0 -8
- data/app/views/helena/questions/_radio_matrix.html.haml +0 -53
- data/app/views/helena/questions/_short_text.html.haml +0 -8
- data/config/initializers/simple_form_bootstrap.rb +0 -132
data/README.md
CHANGED
@@ -1,12 +1,11 @@
|
|
1
|
-
[](https://codeclimate.com/github/gurix/helena/maintainability)
|
2
|
+
[](https://codeclimate.com/github/gurix/helena/test_coverage)
|
3
3
|
[](https://rubygems.org/gems/helena)
|
4
4
|
# Helena
|
5
|
-
Helena is an online survey/test framework designed for agile survey/test development, longitudinal studies and instant feedback.
|
5
|
+
Helena is an online survey/test framework designed for agile survey/test development, longitudinal studies and instant feedback. It was designed as modular part of the platform Laufbahndiagnostik (https://laufbahndiagnostik.ch/en).
|
6
6
|
|
7
7
|

|
8
8
|
|
9
|
-
Demo: http://helena-demo.herokuapp.com (https://github.com/gurix/helena-demo)
|
10
9
|
## Requirements
|
11
10
|
* Ruby (2.3.1 or higher)
|
12
11
|
* Rails (4.2 or higher)
|
data/Rakefile
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
require 'rubygems'
|
3
2
|
begin
|
4
3
|
require 'bundler/setup'
|
@@ -17,7 +16,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
|
|
17
16
|
rdoc.rdoc_files.include('lib/**/*.rb')
|
18
17
|
end
|
19
18
|
|
20
|
-
APP_RAKEFILE = File.expand_path('
|
19
|
+
APP_RAKEFILE = File.expand_path('spec/dummy/Rakefile', __dir__)
|
21
20
|
load 'rails/tasks/engine.rake'
|
22
21
|
|
23
22
|
Bundler::GemHelper.install_tasks
|
@@ -4,6 +4,7 @@ module Helena
|
|
4
4
|
before_action :load_session, only: %i[edit update]
|
5
5
|
before_action :update_answers, only: :update
|
6
6
|
before_action :answer_errors, only: :update
|
7
|
+
before_action :flash_errors, only: :update
|
7
8
|
after_action :update_last_question_group_id, only: :update
|
8
9
|
|
9
10
|
def show
|
@@ -41,7 +42,7 @@ module Helena
|
|
41
42
|
@session = Helena::Session.find_by token: params[:token]
|
42
43
|
@survey = @session.survey
|
43
44
|
@version = @survey.versions.find @session.version_id
|
44
|
-
render plain: 'Version not active', status: '404' unless @version
|
45
|
+
render plain: 'Version not active', status: '404' unless @version&.active
|
45
46
|
@question_group = question_group
|
46
47
|
end
|
47
48
|
|
@@ -60,6 +61,7 @@ module Helena
|
|
60
61
|
|
61
62
|
def session_params
|
62
63
|
return unless params[:session]
|
64
|
+
|
63
65
|
params.require(:session).permit(answers: @question_group.question_codes, question_types: @question_group.question_codes)
|
64
66
|
end
|
65
67
|
|
@@ -92,6 +94,10 @@ module Helena
|
|
92
94
|
@errors = errors
|
93
95
|
end
|
94
96
|
|
97
|
+
def flash_errors
|
98
|
+
flash.now[:danger] = t('errors.messages.any_errors_flash') if @errors.any?
|
99
|
+
end
|
100
|
+
|
95
101
|
def session_report
|
96
102
|
Slim::Template.new { @version.session_report }.render(self).html_safe if @version.session_report
|
97
103
|
end
|
@@ -1,4 +1,22 @@
|
|
1
1
|
module Helena
|
2
2
|
module ApplicationHelper
|
3
|
+
def requeired_note
|
4
|
+
content_tag(:span, t('helena.shared.required_indicator'), aria: { hidden: true }) +
|
5
|
+
content_tag(:span, t('helena.shared.sr_required_indicator'), class: 'sr-only')
|
6
|
+
end
|
7
|
+
|
8
|
+
def question_label(question, options = {})
|
9
|
+
content_tag((options[:dummy] ? :span : :label), class: :label, for: "question_#{question.code}") do
|
10
|
+
raw(question.question_text) + (requeired_note if question.required?)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# Allows to set the header title within the text
|
15
|
+
# i.e `h1 = title 'This will be also shown in the title tag'`
|
16
|
+
# Do not forgot to set `content_for(:title)` within the title tag in the head.
|
17
|
+
def title(page_title)
|
18
|
+
content_for(:title, page_title)
|
19
|
+
page_title
|
20
|
+
end
|
3
21
|
end
|
4
22
|
end
|
@@ -13,6 +13,7 @@ module Helena
|
|
13
13
|
def labels_preselection
|
14
14
|
selected_labels = labels.select { |label| label.preselected == true }
|
15
15
|
return if selected_labels.size <= 1
|
16
|
+
|
16
17
|
selected_labels.each { |label| label.errors.add(:preselected, I18n.t(:taken, scope: 'activerecord.errors.messages')) }
|
17
18
|
errors.add(:labels, I18n.t('helena.admin.radio_group.only_one_preselection_allowed')) # TODO: How to manage this translation?
|
18
19
|
end
|
@@ -15,5 +15,13 @@ module Helena
|
|
15
15
|
def question_codes
|
16
16
|
questions.map { |question| [question.code] + question.sub_questions.map(&:code) }.flatten
|
17
17
|
end
|
18
|
+
|
19
|
+
def question_texts
|
20
|
+
questions.map { |question| [question.code, question.question_text] + subquestion_texts(question) }.flatten
|
21
|
+
end
|
22
|
+
|
23
|
+
def subquestion_texts(question)
|
24
|
+
question.sub_questions.map { |sub_question| [sub_question.code, sub_question.text] }
|
25
|
+
end
|
18
26
|
end
|
19
27
|
end
|
@@ -26,7 +26,6 @@ module Helena
|
|
26
26
|
def answers_as_yaml=(yaml)
|
27
27
|
parsed_answers = YAML.safe_load yaml
|
28
28
|
update_answers parsed_answers
|
29
|
-
remove_unparsed_answers parsed_answers
|
30
29
|
end
|
31
30
|
|
32
31
|
def reset_tokens
|
@@ -46,14 +45,6 @@ module Helena
|
|
46
45
|
Hash[*answers.map { |answer| [answer[:code], answer[:value]] }.flatten]
|
47
46
|
end
|
48
47
|
|
49
|
-
def self.answer_codes
|
50
|
-
answer_codes = []
|
51
|
-
all.each do |session|
|
52
|
-
answer_codes += session.answers.map(&:code) - answer_codes
|
53
|
-
end
|
54
|
-
answer_codes
|
55
|
-
end
|
56
|
-
|
57
48
|
private
|
58
49
|
|
59
50
|
def generate_token(size)
|
@@ -65,19 +56,10 @@ module Helena
|
|
65
56
|
end
|
66
57
|
|
67
58
|
def update_answers(parsed_answers)
|
59
|
+
answers.delete_all
|
68
60
|
parsed_answers.each do |code, value|
|
69
|
-
answer = answers.where(code: code).first
|
70
|
-
if answer
|
71
|
-
next if answer.value == value
|
72
|
-
answer.delete
|
73
|
-
end
|
74
61
|
answers << Helena::Answer.build_generic(code, value, '')
|
75
62
|
end
|
76
63
|
end
|
77
|
-
|
78
|
-
def remove_unparsed_answers(parsed_answers)
|
79
|
-
unparsed_answers = answers.map(&:code) - parsed_answers.keys
|
80
|
-
answers.in(code: unparsed_answers).destroy
|
81
|
-
end
|
82
64
|
end
|
83
65
|
end
|
@@ -28,6 +28,10 @@ module Helena
|
|
28
28
|
question_groups.map(&:question_codes).flatten
|
29
29
|
end
|
30
30
|
|
31
|
+
def question_texts
|
32
|
+
question_groups.map(&:question_texts).flatten
|
33
|
+
end
|
34
|
+
|
31
35
|
def question_code_occurences
|
32
36
|
question_codes.each_with_object(Hash.new(0)) { |word, counts| counts[word] += 1 }
|
33
37
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
- label_width = "#{60 / question.labels.count}%"
|
2
|
+
.radio_matrix class="#{'helena-error' if errors.present?}"
|
3
|
+
= question_label(question, dummy: true)
|
4
|
+
|
5
|
+
table
|
6
|
+
thead
|
7
|
+
tr
|
8
|
+
td
|
9
|
+
- question.labels.each do |label|
|
10
|
+
th width="#{label_width}"
|
11
|
+
= label.text
|
12
|
+
td
|
13
|
+
|
14
|
+
tbody
|
15
|
+
- question.sub_questions.each do |sub_question|
|
16
|
+
tr class="#{'error' if errors[sub_question.code]}" id="question_#{sub_question.code}"
|
17
|
+
td
|
18
|
+
= sub_question.parts.first
|
19
|
+
- if errors[sub_question.code]
|
20
|
+
.error= errors[sub_question.code]
|
21
|
+
- question.labels.each_with_index do |label, index|
|
22
|
+
- checked = answers[sub_question.code].to_s == label.value.to_s if answers[sub_question.code].present?
|
23
|
+
- checked ||= (label.preselected? ? true : false)
|
24
|
+
th width="#{label_width}"
|
25
|
+
label
|
26
|
+
= form.simple_fields_for :answers do |answer_form|
|
27
|
+
= answer_form.radio_button sub_question.code, label.value, checked: checked
|
28
|
+
span.label-text
|
29
|
+
- if index < (question.labels.size / 2)
|
30
|
+
= "#{label.text} #{sub_question.parts.first}"
|
31
|
+
- elsif index > (question.labels.size / 2)
|
32
|
+
= "#{label.text} #{sub_question.parts.last}"
|
33
|
+
- else
|
34
|
+
= label.text
|
35
|
+
td
|
36
|
+
= sub_question.parts.last if sub_question.splitted?
|
@@ -1,17 +1,15 @@
|
|
1
|
-
.checkbox_group
|
2
|
-
|
3
|
-
|
4
|
-
= ' *' if question.required
|
1
|
+
fieldset.checkbox_group class="#{'helena-error' if errors[question.code]}"
|
2
|
+
legend= question_label(question, dummy: true)
|
3
|
+
|
5
4
|
= form.simple_fields_for :answers do |answer_form|
|
6
5
|
- question.sub_questions.each do |sub_question|
|
7
6
|
.checkbox
|
8
|
-
|
7
|
+
label
|
9
8
|
- checked = (answers[sub_question.code].to_s == sub_question.value.to_s) if sub_question.value
|
10
9
|
- checked ||= sub_question.preselected
|
11
10
|
= answer_form.input_field sub_question.code, as: :boolean,
|
12
11
|
value: sub_question.value,
|
13
|
-
checked: checked
|
14
|
-
class: 'form-control'
|
12
|
+
checked: checked
|
15
13
|
= sub_question.text
|
16
14
|
- if errors[question.code]
|
17
|
-
.
|
15
|
+
.error= errors[question.code]
|
@@ -0,0 +1,9 @@
|
|
1
|
+
.long_text class="#{'helena-error' if errors[question.code]}"
|
2
|
+
= question_label(question)
|
3
|
+
= form.simple_fields_for :answers do |answer_form|
|
4
|
+
- params = { value: answers[question.code], id: "question_#{question.code}" }
|
5
|
+
- params = params.merge(aria: { describedby: "#{question.code}_error" }, invalid: true) if errors[question.code]
|
6
|
+
- params = params.merge(required: true) if question.required
|
7
|
+
= answer_form.text_area(question.code, params)
|
8
|
+
- if errors[question.code]
|
9
|
+
.error id="#{question.code}_error" = errors[question.code]
|
@@ -1,13 +1,12 @@
|
|
1
|
-
.radio_group.
|
2
|
-
|
3
|
-
|
4
|
-
= ' *' if question.required
|
1
|
+
fieldset.radio_group id="question_#{question.code}" class="#{'helena-error' if errors[question.code]}"
|
2
|
+
legend = question_label(question, dummy: true)
|
3
|
+
|
5
4
|
- question.labels.each do |label|
|
6
5
|
.radio
|
7
|
-
|
6
|
+
label
|
8
7
|
- checked = (answers[question.code].to_s == label.value.to_s) || label.preselected
|
9
8
|
= form.simple_fields_for :answers do |answer_form|
|
10
9
|
= answer_form.radio_button question.code, label.value, checked: checked
|
11
10
|
= label.text
|
12
11
|
- if errors[question.code]
|
13
|
-
.
|
12
|
+
.error= errors[question.code]
|
@@ -0,0 +1,27 @@
|
|
1
|
+
- label_width = "#{80 / question.labels.count}%"
|
2
|
+
|
3
|
+
.radio_matrix class="#{'helena-error' if errors.present?}"
|
4
|
+
= question_label(question, dummy: true)
|
5
|
+
|
6
|
+
table.table
|
7
|
+
thead
|
8
|
+
tr
|
9
|
+
th
|
10
|
+
- question.labels.each do |label|
|
11
|
+
th width="#{label_width}"
|
12
|
+
= label.text
|
13
|
+
tbody
|
14
|
+
- question.sub_questions.each do |sub_question|
|
15
|
+
tr class="#{'error' if errors[sub_question.code]}" id="question_#{sub_question.code}"
|
16
|
+
td
|
17
|
+
= sub_question.parts.first
|
18
|
+
- if errors[sub_question.code]
|
19
|
+
.error= errors[sub_question.code]
|
20
|
+
- question.labels.each do |label|
|
21
|
+
- checked = answers[sub_question.code].to_s == label.value.to_s if answers[sub_question.code].present?
|
22
|
+
- checked ||= (label.preselected? ? true : false)
|
23
|
+
th width="#{label_width}"
|
24
|
+
label
|
25
|
+
= form.simple_fields_for :answers do |answer_form|
|
26
|
+
= answer_form.radio_button sub_question.code, label.value, checked: checked
|
27
|
+
.label-text= label.text
|
@@ -0,0 +1,9 @@
|
|
1
|
+
.short_text class="#{'helena-error' if errors[question.code]}"
|
2
|
+
= question_label(question)
|
3
|
+
= form.simple_fields_for :answers do |answer_form|
|
4
|
+
- params = { value: answers[question.code], id: "question_#{question.code}" }
|
5
|
+
- params = params.merge(aria: { describedby: "#{question.code}_error" }, invalid: true) if errors[question.code]
|
6
|
+
- params = params.merge(required: true) if question.required
|
7
|
+
= answer_form.text_field(question.code, params)
|
8
|
+
- if errors[question.code]
|
9
|
+
.error id="#{question.code}_error" = errors[question.code]
|
@@ -1,18 +1,22 @@
|
|
1
1
|
= render 'progressbar' if @version.settings && @version.settings[:display_progressbar] == "1"
|
2
2
|
#survey_content
|
3
|
-
h1.survey_title
|
4
|
-
= @version.survey_detail.title
|
3
|
+
h1.survey_title = title @version.survey_detail.title
|
5
4
|
fieldset id="#{dom_id @question_group}"
|
6
|
-
legend
|
5
|
+
legend
|
6
|
+
- if @question_group.title
|
7
|
+
= @question_group.title
|
8
|
+
- else
|
9
|
+
.sr-only= t('.question_group_legend', page: (@question_group.previous_items.size + 1).to_i)
|
7
10
|
= simple_form_for(@session, url: helena.session_path(@session.token), as: :session) do |f|
|
11
|
+
= render 'error_messages'if @errors.any?
|
8
12
|
- @questions.each do |question|
|
9
13
|
= render "#{question.class.to_s.underscore}", form: f, question: question, answers: @answers, errors: @errors
|
10
14
|
|
11
15
|
= hidden_field_tag :question_group, @question_group.id
|
12
16
|
|
13
17
|
- if @question_group.previous_items.any? && @question_group.allow_to_go_back?
|
14
|
-
= link_to t('.back'), edit_session_path(@session.token, question_group: @question_group.previous_item), class: '
|
18
|
+
= link_to t('.back'), edit_session_path(@session.token, question_group: @question_group.previous_item), class: 'back_button'
|
15
19
|
|
16
|
-
= f.submit class: '
|
17
|
-
|
18
|
-
|
20
|
+
= f.submit class: 'next_button', value: @question_group.last? ? t('.save') : t('.next')
|
21
|
+
- if @questions.map{ |question| question.try :required }.any?
|
22
|
+
span aria-hidden="true" = t('helena.shared.required')
|
data/config/locales/en.yml
CHANGED
@@ -1,19 +1,20 @@
|
|
1
1
|
en:
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
add: Add
|
8
|
-
show: Show
|
9
|
-
required: '* indicates required fields'
|
10
|
-
code_hint: The code should consist only of lowercase letters, numbers and underscores. Don't begin with a digit or an underscore and don't end with an underscore.
|
11
|
-
move_up: Move up
|
12
|
-
move_down: Move down
|
13
|
-
untitled: Untitled
|
2
|
+
helena:
|
3
|
+
shared:
|
4
|
+
required_indicator: "*"
|
5
|
+
sr_required_indicator: "(Mandatory)"
|
6
|
+
required: '* indicates required fields'
|
14
7
|
|
8
|
+
layouts:
|
9
|
+
application:
|
10
|
+
brand_name: Helena
|
15
11
|
actions:
|
16
12
|
deleted: "'%{resource}' successfully deleted"
|
17
13
|
updated: "'%{resource}' successfully updated"
|
18
14
|
created: "'%{resource}' successfully created"
|
19
15
|
error: Ooopss... something is wrong, please check your input
|
16
|
+
|
17
|
+
errors:
|
18
|
+
messages:
|
19
|
+
any_errors: Problems have arisen in answering the questions. Please check your input.
|
20
|
+
any_errors_flash: One or more questions have not been answered correctly. You cannot continue until the correct answers have been given.
|