helena 1.3.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![
|
2
|
-
[![
|
1
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/0a74377cf11aeb621333/maintainability)](https://codeclimate.com/github/gurix/helena/maintainability)
|
2
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/0a74377cf11aeb621333/test_coverage)](https://codeclimate.com/github/gurix/helena/test_coverage)
|
3
3
|
[![Rubygems Version](https://img.shields.io/gem/v/helena.svg?style=flat)](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
|
![swiss made software](https://raw.githubusercontent.com/gurix/helena/master/app/assets/images/helena/swissmadesoftware.png "swiss made software")
|
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.
|