ask_it 0.1.1
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 +7 -0
- data/Gemfile +11 -0
- data/LICENSE.md +21 -0
- data/README.md +287 -0
- data/Rakefile +29 -0
- data/app/controllers/ask_it/attempts_controller.rb +36 -0
- data/app/controllers/ask_it/surveys_controller.rb +58 -0
- data/app/models/ask_it/answer.rb +99 -0
- data/app/models/ask_it/attempt.rb +108 -0
- data/app/models/ask_it/option.rb +65 -0
- data/app/models/ask_it/options_type.rb +39 -0
- data/app/models/ask_it/predefined_value.rb +24 -0
- data/app/models/ask_it/question.rb +73 -0
- data/app/models/ask_it/question_type.rb +32 -0
- data/app/models/ask_it/section.rb +66 -0
- data/app/models/ask_it/survey.rb +77 -0
- data/config/locales/en.yml +54 -0
- data/config/locales/pt-PT.yml +48 -0
- data/config/locales/pt.yml +48 -0
- data/lib/ask_it/active_record.rb +25 -0
- data/lib/ask_it/engine.rb +8 -0
- data/lib/ask_it/railtie.rb +13 -0
- data/lib/ask_it/version.rb +5 -0
- data/lib/ask_it.rb +20 -0
- data/lib/generators/ask_it/install_generator.rb +57 -0
- data/lib/generators/ask_it/survey_generator.rb +117 -0
- data/lib/generators/templates/active_admin.rb +87 -0
- data/lib/generators/templates/controllers/attempts_controller.rb +11 -0
- data/lib/generators/templates/controllers/surveys_controller.rb +31 -0
- data/lib/generators/templates/helpers/ask_it/surveys_helper.rb +56 -0
- data/lib/generators/templates/migration.rb.tt +59 -0
- data/lib/generators/templates/migration_add_head_number_to_options_table.rb.tt +8 -0
- data/lib/generators/templates/migration_add_mandatory_to_questions_table.rb.tt +8 -0
- data/lib/generators/templates/migration_add_types_to_questions_and_options.rb.tt +15 -0
- data/lib/generators/templates/migration_create_predefined_values_table.rb.tt +16 -0
- data/lib/generators/templates/migration_section.rb.tt +24 -0
- data/lib/generators/templates/migration_update_survey_tables.rb.tt +24 -0
- data/lib/generators/templates/rails_admin.rb +10 -0
- data/lib/generators/templates/views/attempts/_form.html.erb +61 -0
- data/lib/generators/templates/views/attempts/new.html.erb +10 -0
- data/lib/generators/templates/views/surveys/_form.html.erb +78 -0
- data/lib/generators/templates/views/surveys/_option_fields.html.erb +26 -0
- data/lib/generators/templates/views/surveys/_question_fields.html.erb +42 -0
- data/lib/generators/templates/views/surveys/_section_fields.html.erb +29 -0
- data/lib/generators/templates/views/surveys/edit.html.erb +2 -0
- data/lib/generators/templates/views/surveys/index.html.erb +14 -0
- data/lib/generators/templates/views/surveys/new.html.erb +2 -0
- data/lib/survey.rb +7 -0
- data/lib/tasks/changelog.rake +10 -0
- metadata +372 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1a3bc70a98811e199af9ece6bd847d80c8c2ee0d7c6f28b52b76f4633a782158
|
4
|
+
data.tar.gz: 0360a7f42de9cea7e72e36491377fa81168da1b6032f1f4f51acf9788f07d588
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9859e6a7b84f31286137b4c1e40ef3768b8f84e7fdd5e3d50a3658a2fd3dce7fdff89b2792924834f314843af8d4627b5b04be6aae81f803347e489ef5c4fd69
|
7
|
+
data.tar.gz: 234ef09f60f5ce8dfe04e07acde93a3e1862b07094a2fee9a3de988cbcc642814b1ec486c6a0b084ebd6053723f75dda358f1a22289d4188329320d88528186a
|
data/Gemfile
ADDED
data/LICENSE.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# MIT LICENSE
|
2
|
+
|
3
|
+
Copyright (c) 2024 Build It
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,287 @@
|
|
1
|
+
# Ask It
|
2
|
+
|
3
|
+
[](https://codeclimate.com/github/growth-constant/ask_it/maintainability)
|
4
|
+
[](https://codeclimate.com/github/growth-constant/ask_it/test_coverage)
|
5
|
+
|
6
|
+
Ask It is a Rails Engine that brings multi types of quizzes, surveys and contests into your Rails
|
7
|
+
application. Ask It models were designed to be flexible enough in order to be extended and
|
8
|
+
integrated with your own models. Ask It was initially extracted from a real application that handles contests and quizzes.
|
9
|
+
|
10
|
+
## Table of Contents
|
11
|
+
|
12
|
+
- [Documentation](#documentation)
|
13
|
+
- [Main Features](#main-features)
|
14
|
+
- [Installation](#installation)
|
15
|
+
- [Getting Started](#getting-started)
|
16
|
+
- [Adding Survey Participants](#adding-survey-participants)
|
17
|
+
- [Survey Associations](#survey-associations)
|
18
|
+
- [Survey in Views](#survey-in-views)
|
19
|
+
- [Scaffolds and CRUD Frameworks](#scaffolds-and-crud-frameworks)
|
20
|
+
- [How to Use It](#how-to-use-it)
|
21
|
+
- [Compatibility](#compatibility)
|
22
|
+
- [License](#license)
|
23
|
+
|
24
|
+
## Documentation
|
25
|
+
|
26
|
+
You can view the Ask It documentation in RDoc format here:
|
27
|
+
|
28
|
+
http://rubydoc.info/github/dr-click/questionnaire/master/frames
|
29
|
+
|
30
|
+
## Main Features:
|
31
|
+
- Ask It can limit the number of attempts for each participant, can have multiple sections
|
32
|
+
- Sections can have multiple questions
|
33
|
+
- Questions can have multiple answers
|
34
|
+
- Answers can have different weights and types (multi choices, single choice, number, text)
|
35
|
+
- Can use 2 languages (Main language field, Localized field) for Surveys, Sections, Questions and Answers attributes
|
36
|
+
- Base Scaffold Support for Active Admin, Rails Admin and default Rails Controllers
|
37
|
+
- Base calculation for scores
|
38
|
+
- Easy integration with your project
|
39
|
+
|
40
|
+
## Installation
|
41
|
+
|
42
|
+
Add Ask It to your Gemfile for Rails 6 and 7:
|
43
|
+
|
44
|
+
```ruby
|
45
|
+
gem 'ask_it'
|
46
|
+
```
|
47
|
+
or
|
48
|
+
```ruby
|
49
|
+
gem 'ask_it'
|
50
|
+
|
51
|
+
```
|
52
|
+
|
53
|
+
Now generate and run migrations:
|
54
|
+
```sh
|
55
|
+
rails generate ask_it:install
|
56
|
+
|
57
|
+
bundle exec rake db:migrate
|
58
|
+
```
|
59
|
+
|
60
|
+
## Getting started with Ask It
|
61
|
+
|
62
|
+
## Survey inside your models
|
63
|
+
To make a model aware of you just need to add `has_surveys` on it:
|
64
|
+
```ruby
|
65
|
+
class User < ActiveRecord::Base
|
66
|
+
has_surveys
|
67
|
+
|
68
|
+
#... (your code) ...
|
69
|
+
end
|
70
|
+
```
|
71
|
+
There is the concept of participant, in our example we choose the User Model.
|
72
|
+
Every participant can respond to surveys and every response is registered as a attempt.
|
73
|
+
By default, survey logic assumes an infinite number of attempts per participant
|
74
|
+
but if your surveys need to have a maximum number of attempts
|
75
|
+
you can pass the attribute `attempts_number` when creating them.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
# Each Participant can respond 4 times this survey
|
79
|
+
AskIt::Survey.new(:name => "Star Wars Quiz", :attempts_number => 4)
|
80
|
+
```
|
81
|
+
## Ask It used in your controllers
|
82
|
+
|
83
|
+
In this example we are using the current_user helper
|
84
|
+
but you can do it in the way you want.
|
85
|
+
|
86
|
+
```ruby
|
87
|
+
class ContestsController < ApplicationController
|
88
|
+
|
89
|
+
helper_method :survey, :participant
|
90
|
+
|
91
|
+
# create a new attempt to this survey
|
92
|
+
def new
|
93
|
+
@survey = AskIt::Survey.active.last
|
94
|
+
@attempt = @survey.attempts.new
|
95
|
+
@attempt.answers.build
|
96
|
+
@participant = current_user # you have to decide what to do here
|
97
|
+
end
|
98
|
+
|
99
|
+
# create a new attempt in this survey
|
100
|
+
# an attempt needs to have a participant assigned
|
101
|
+
def create
|
102
|
+
@survey = AskIt::Survey.active.last
|
103
|
+
@attempt = @survey.attempts.new(attempt_params)
|
104
|
+
@attempt.participant = current_user
|
105
|
+
if @attempt.valid? and @attempt.save
|
106
|
+
redirect_to view_context.new_attempt_path, alert: I18n.t("attempts_controller.#{action_name}")
|
107
|
+
else
|
108
|
+
flash.now[:error] = @attempt.errors.full_messages.join(', ')
|
109
|
+
render :action => :new
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
#######
|
114
|
+
private
|
115
|
+
#######
|
116
|
+
|
117
|
+
# Rails 4 Strong Params
|
118
|
+
def attempt_params
|
119
|
+
if Rails::VERSION::MAJOR < 4
|
120
|
+
params[:survey_attempt]
|
121
|
+
else
|
122
|
+
params.require(:survey_attempt).permit(answers_attributes: [:id, :question_id, :option_id, :option_text, :option_number, :predefined_value_id, :_destroy, :finished])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
```
|
128
|
+
|
129
|
+
## Survey Associations
|
130
|
+
To add a survey to a particular model (model has_many :surveys), use the `has_many_surveys` helper
|
131
|
+
```ruby
|
132
|
+
class Lesson < ActiveRecord::Base
|
133
|
+
has_many_surveys
|
134
|
+
|
135
|
+
#... (your code) ...
|
136
|
+
end
|
137
|
+
```
|
138
|
+
Then, create a module mixin that adds `belongs_to` to the survey model based on your class name
|
139
|
+
```ruby
|
140
|
+
# app/models/survey/belongs_to_lesson.rb
|
141
|
+
module Survey
|
142
|
+
module BelongsToLesson
|
143
|
+
extend ActiveSupport::Concern
|
144
|
+
included do
|
145
|
+
belongs_to :lesson
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
```
|
150
|
+
This will dynamically add the association to the survey model. However, you will need to generate a migration in order to add the foreign key to the `survey_surveys` table like so:
|
151
|
+
```ruby
|
152
|
+
class AddLessonIdToSurveySurveys < ActiveRecord::Migration
|
153
|
+
def change
|
154
|
+
add_column :survey_surveys, :lesson_id, :integer
|
155
|
+
end
|
156
|
+
end
|
157
|
+
```
|
158
|
+
|
159
|
+
|
160
|
+
## Survey inside your Views
|
161
|
+
|
162
|
+
### Controlling Survey avaliability per participant
|
163
|
+
To control which page participants see you can use method `avaliable_for_participant?`
|
164
|
+
that checks if the participant already spent his attempts.
|
165
|
+
```erb
|
166
|
+
<h3><%= flash[:alert]%></h3>
|
167
|
+
<h3><%= flash[:error]%></h3>
|
168
|
+
|
169
|
+
<% if @survey.avaliable_for_participant?(@participant) %>
|
170
|
+
<%= render 'form' %>
|
171
|
+
<% else %>
|
172
|
+
<p>
|
173
|
+
<%= @participant.name %> spent all the possible attempts to answer this Survey
|
174
|
+
</p>
|
175
|
+
<% end -%>
|
176
|
+
|
177
|
+
<% # in _form.html.erb %>
|
178
|
+
<h1><%= @survey.name %></h1>
|
179
|
+
<p><%= @survey.description %></p>
|
180
|
+
<%= form_for(@attempt, :url => attempt_scope(@attempt)) do |f| %>
|
181
|
+
<%= f.fields_for :answers do |builder| %>
|
182
|
+
<ul>
|
183
|
+
<% seq = 0 %>
|
184
|
+
<% @survey.sections.each do |section| %>
|
185
|
+
<p><span><%= "#{section.head_number} : " if section.head_number %></span><%= section.name%></p>
|
186
|
+
<p><%= section.description if section.description %></p>
|
187
|
+
<% section.questions.each do |question| %>
|
188
|
+
<% seq += 1 %>
|
189
|
+
<li>
|
190
|
+
<p><span><%= "#{question.head_number} : " if question.head_number %></span><%= question.text %></p>
|
191
|
+
<p><%= question.description if question.description %></p>
|
192
|
+
<% question.options.each do |option| %>
|
193
|
+
|
194
|
+
|
195
|
+
<% if option.options_type_id == AskIt::OptionsType.multi_choices %>
|
196
|
+
<%= hidden_field_tag "survey_attempt[answers_attributes][#{seq}][question_id]", question.id %>
|
197
|
+
<%= check_box_tag "survey_attempt[answers_attributes][#{seq}][option_id]", option.id %>
|
198
|
+
<% seq += 1 %>
|
199
|
+
<% elsif option.options_type_id == AskIt::OptionsType.single_choice %>
|
200
|
+
<%= hidden_field_tag "survey_attempt[answers_attributes][#{seq}][question_id]", question.id %>
|
201
|
+
<%= radio_button_tag "survey_attempt[answers_attributes][#{seq}][option_id]", option.id %>
|
202
|
+
<% elsif option.options_type_id == AskIt::OptionsType.number %>
|
203
|
+
<%= hidden_field_tag "survey_attempt[answers_attributes][#{seq}][question_id]", question.id %>
|
204
|
+
<%= hidden_field_tag "survey_attempt[answers_attributes][#{seq}][option_id]", option.id %>
|
205
|
+
<%= number_field_tag "survey_attempt[answers_attributes][#{seq}][option_number]", "", :style => "width: 40px;" %>
|
206
|
+
<% seq += 1 %>
|
207
|
+
<% elsif option.options_type_id == AskIt::OptionsType.text %>
|
208
|
+
<%= hidden_field_tag "survey_attempt[answers_attributes][#{seq}][question_id]", question.id %>
|
209
|
+
<%= hidden_field_tag "survey_attempt[answers_attributes][#{seq}][option_id]", option.id %>
|
210
|
+
<%= text_field_tag "survey_attempt[answers_attributes][#{seq}][option_text]", "" %>
|
211
|
+
<% seq += 1 %>
|
212
|
+
<% end %>
|
213
|
+
|
214
|
+
<%= option.text %> <br/>
|
215
|
+
<% end -%>
|
216
|
+
</li>
|
217
|
+
<% end -%>
|
218
|
+
<% end -%>
|
219
|
+
</ul>
|
220
|
+
<% end -%>
|
221
|
+
<%= f.submit "Submit" %>
|
222
|
+
<% end -%>
|
223
|
+
```
|
224
|
+
|
225
|
+
### Scaffolds and CRUD frameworks
|
226
|
+
If you are using Rails Admin or Active Admin, you can generate base CRUD screens for Survey with:
|
227
|
+
```sh
|
228
|
+
rails generate survey active_admin
|
229
|
+
|
230
|
+
rails generate survey rails_admin
|
231
|
+
```
|
232
|
+
If you want a simple way to get started you can use the `plain` option which is a simple Rails scaffold to generate the controller and views related with survey logic.
|
233
|
+
By default when you type `rails g survey plain` it generates a controller in the `admin` namespace but you can choose your own namespace as well:
|
234
|
+
```sh
|
235
|
+
rails generate survey plain namespace:contests
|
236
|
+
```
|
237
|
+
|
238
|
+
By default when you generates your controllers using the `plain` command the task
|
239
|
+
generates the associated routes as well.
|
240
|
+
Afterwards if you want to generate more routes, you can using the command:
|
241
|
+
|
242
|
+
```sh
|
243
|
+
rails generate survey routes namespace:admin
|
244
|
+
```
|
245
|
+
|
246
|
+
|
247
|
+
## How to use it
|
248
|
+
Every user has a collection of attempts for each survey that he respond to. Is up to you to
|
249
|
+
make averages and collect reports based on that information.
|
250
|
+
What makes Survey useful is that all the logic behind surveys is now abstracted and well integrated,
|
251
|
+
making your job easier.
|
252
|
+
|
253
|
+
## Hacking with Survey through your Models:
|
254
|
+
|
255
|
+
```ruby
|
256
|
+
# select the first active Survey
|
257
|
+
survey = AskIt::Survey.active.first
|
258
|
+
|
259
|
+
# select all the attempts from this survey
|
260
|
+
survey_answers = survey.attempts
|
261
|
+
|
262
|
+
# check the highest score for current user
|
263
|
+
user_highest_score = survey_answers.for_participant(@user).high_score
|
264
|
+
|
265
|
+
#check the highest score made for this survey
|
266
|
+
global_highest_score = survey_answers.high_score
|
267
|
+
```
|
268
|
+
# Compability
|
269
|
+
|
270
|
+
### Rails
|
271
|
+
Survey supports Rails 3 and 4. For use in Rails 4 without using protected_attributes gem.
|
272
|
+
Rails 4 support is recent, so some minor issues may still be present, please report them.
|
273
|
+
|
274
|
+
### Active Admin
|
275
|
+
Only support versions of Active Admin higher than 0.3.1.
|
276
|
+
|
277
|
+
# License
|
278
|
+
- Modified by [Dr-Click](http://github.com/dr-click)
|
279
|
+
- Copyright © 2013 [Runtime Revolution](http://www.runtime-revolution.com), released under the MIT license.
|
280
|
+
- This repository was forked from the original one : https://github.com/clearfunction/questionnaire
|
281
|
+
|
282
|
+
# Thanks to Gems
|
283
|
+
- [Appraisal](https://github.com/thoughtbot/appraisal)
|
284
|
+
- [Code Climate](https://codeclimate.com/)
|
285
|
+
- [Simplecov](https://github.com/colszowka/simplecov)
|
286
|
+
- [Rspec](https://github.com/rspec/rspec)
|
287
|
+
- [Rails](https://github.com/rails/rails)
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
begin
|
5
|
+
require 'bundler/setup'
|
6
|
+
rescue LoadError
|
7
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'rake'
|
11
|
+
require 'rdoc/task'
|
12
|
+
require 'rspec/core/rake_task'
|
13
|
+
load 'tasks/changelog.rake'
|
14
|
+
|
15
|
+
desc 'Run all specs across all appraisals'
|
16
|
+
task :spec do
|
17
|
+
sh 'bundle exec appraisal rspec'
|
18
|
+
end
|
19
|
+
|
20
|
+
task default: :spec
|
21
|
+
|
22
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
23
|
+
rdoc.rdoc_dir = 'rdoc'
|
24
|
+
rdoc.title = 'Ask It'
|
25
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
26
|
+
rdoc.rdoc_files.include('README.md')
|
27
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
28
|
+
rdoc.rdoc_files.include('app/**/*.rb')
|
29
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AskIt
|
4
|
+
# Handles creation and management of survey attempts
|
5
|
+
class AttemptsController < ActionController::Base
|
6
|
+
# Handles creation and management of survey attempts
|
7
|
+
# helper 'ask_it/surveys'
|
8
|
+
# include AskIt::SurveysHelper
|
9
|
+
|
10
|
+
def new
|
11
|
+
@survey = Survey.active.last
|
12
|
+
@attempt = @survey.attempts.new
|
13
|
+
@attempt.answers.build
|
14
|
+
@participant = current_user # Adjust as needed
|
15
|
+
end
|
16
|
+
|
17
|
+
def create
|
18
|
+
@survey = Survey.active.last
|
19
|
+
@attempt = @survey.attempts.new(attempt_params)
|
20
|
+
@attempt.participant = current_user # Adjust as needed
|
21
|
+
|
22
|
+
if @attempt.save
|
23
|
+
redirect_to new_ask_it_attempt_path, notice: t('.success')
|
24
|
+
else
|
25
|
+
flash.now[:error] = @attempt.errors.full_messages.join(', ')
|
26
|
+
render :new
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def attempt_params
|
33
|
+
params.require(:attempt).permit(answers_attributes: %i[question_id option_id option_text option_number predefined_value_id])
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AskIt
|
4
|
+
# Handles creation and management of surveys
|
5
|
+
class SurveysController < ActionController::Base
|
6
|
+
before_action :load_survey, only: %i[show edit update]
|
7
|
+
|
8
|
+
def index
|
9
|
+
@surveys = AskIt::Survey.all
|
10
|
+
end
|
11
|
+
|
12
|
+
def new
|
13
|
+
@survey = AskIt::Survey.new
|
14
|
+
end
|
15
|
+
|
16
|
+
def create
|
17
|
+
@survey = AskIt::Survey.new(survey_params)
|
18
|
+
if @survey.valid? && @survey.save
|
19
|
+
redirect_to surveys_path, notice: I18n.t('surveys_controller.create')
|
20
|
+
else
|
21
|
+
render :new
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def edit
|
26
|
+
# @survey is already loaded by before_action
|
27
|
+
end
|
28
|
+
|
29
|
+
def show
|
30
|
+
# @survey is already loaded by before_action
|
31
|
+
end
|
32
|
+
|
33
|
+
def update
|
34
|
+
if @survey.update(survey_params)
|
35
|
+
redirect_to surveys_path, notice: I18n.t('surveys_controller.update')
|
36
|
+
else
|
37
|
+
render :edit
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def load_survey
|
44
|
+
@survey = AskIt::Survey.find(params[:id])
|
45
|
+
rescue ActiveRecord::RecordNotFound
|
46
|
+
redirect_to surveys_path, alert: 'Survey not found'
|
47
|
+
end
|
48
|
+
|
49
|
+
def survey_params
|
50
|
+
params.require(:survey).permit(
|
51
|
+
:name, :description, :active,
|
52
|
+
sections_attributes: [:id, :name, :_destroy,
|
53
|
+
{ questions_attributes: [:id, :text, :question_type, :_destroy,
|
54
|
+
{ options_attributes: %i[id text _destroy] }] }]
|
55
|
+
)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AskIt
|
4
|
+
class Answer < ActiveRecord::Base
|
5
|
+
self.table_name = 'survey_answers'
|
6
|
+
belongs_to :attempt
|
7
|
+
belongs_to :option
|
8
|
+
belongs_to :predefined_value
|
9
|
+
belongs_to :question
|
10
|
+
|
11
|
+
validates :option_id, :question_id, presence: true
|
12
|
+
validates :predefined_value_id, presence: true, if: :requires_predefined_value?
|
13
|
+
validates :option_text, presence: true, if: :requires_text_input?
|
14
|
+
validates :option_number, presence: true, if: :requires_number_input?
|
15
|
+
|
16
|
+
# rails 3 attr_accessible support
|
17
|
+
if Rails::VERSION::MAJOR < 4
|
18
|
+
attr_accessible :option, :attempt, :question, :question_id, :option_id, :predefined_value_id, :attempt_id,
|
19
|
+
:option_text, :option_number
|
20
|
+
end
|
21
|
+
|
22
|
+
before_create :characterize_answer
|
23
|
+
before_save :check_single_choice_with_field_case
|
24
|
+
|
25
|
+
def value
|
26
|
+
if option.nil?
|
27
|
+
AskIt::Option.find(option_id).weight
|
28
|
+
else
|
29
|
+
option.weight
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def correct?
|
34
|
+
correct || option.correct?
|
35
|
+
end
|
36
|
+
|
37
|
+
#######
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
#######
|
42
|
+
|
43
|
+
def characterize_answer
|
44
|
+
self.correct = true if option.correct?
|
45
|
+
end
|
46
|
+
|
47
|
+
def check_single_choice_with_field_case
|
48
|
+
return unless [AskIt::OptionsType.multi_choices,
|
49
|
+
AskIt::OptionsType.single_choice].include?(option.options_type_id)
|
50
|
+
|
51
|
+
self.option_text = nil
|
52
|
+
self.option_number = nil
|
53
|
+
end
|
54
|
+
|
55
|
+
# Validations
|
56
|
+
|
57
|
+
def requires_predefined_value?
|
58
|
+
question&.mandatory? &&
|
59
|
+
question.predefined_values.count.positive? &&
|
60
|
+
!text_based_option?
|
61
|
+
end
|
62
|
+
|
63
|
+
def requires_text_input?
|
64
|
+
option &&
|
65
|
+
question &&
|
66
|
+
question.mandatory? &&
|
67
|
+
question.predefined_values.count.zero? &&
|
68
|
+
text_input_option?
|
69
|
+
end
|
70
|
+
|
71
|
+
def requires_number_input?
|
72
|
+
option &&
|
73
|
+
question &&
|
74
|
+
question.mandatory? &&
|
75
|
+
number_input_option?
|
76
|
+
end
|
77
|
+
|
78
|
+
def text_based_option?
|
79
|
+
[AskIt::OptionsType.text, AskIt::OptionsType.large_text].include?(option.options_type_id)
|
80
|
+
end
|
81
|
+
|
82
|
+
def text_input_option?
|
83
|
+
[
|
84
|
+
AskIt::OptionsType.text,
|
85
|
+
AskIt::OptionsType.multi_choices_with_text,
|
86
|
+
AskIt::OptionsType.single_choice_with_text,
|
87
|
+
AskIt::OptionsType.large_text
|
88
|
+
].include?(option.options_type_id)
|
89
|
+
end
|
90
|
+
|
91
|
+
def number_input_option?
|
92
|
+
[
|
93
|
+
AskIt::OptionsType.number,
|
94
|
+
AskIt::OptionsType.multi_choices_with_number,
|
95
|
+
AskIt::OptionsType.single_choice_with_number
|
96
|
+
].include?(option.options_type_id)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AskIt
|
4
|
+
class Attempt < ActiveRecord::Base
|
5
|
+
self.table_name = 'survey_attempts'
|
6
|
+
|
7
|
+
# relations
|
8
|
+
|
9
|
+
has_many :answers, dependent: :destroy
|
10
|
+
belongs_to :survey
|
11
|
+
belongs_to :participant, polymorphic: true
|
12
|
+
|
13
|
+
# rails 3 attr_accessible support
|
14
|
+
if Rails::VERSION::MAJOR < 4
|
15
|
+
attr_accessible :participant_id, :survey_id, :answers_attributes, :survey, :winner, :participant
|
16
|
+
end
|
17
|
+
|
18
|
+
# validations
|
19
|
+
validates :participant_id, :participant_type,
|
20
|
+
presence: true
|
21
|
+
|
22
|
+
accepts_nested_attributes_for :answers,
|
23
|
+
reject_if: ->(q) { q[:question_id].blank? && q[:option_id].blank? },
|
24
|
+
allow_destroy: true
|
25
|
+
|
26
|
+
# scopes
|
27
|
+
|
28
|
+
scope :for_survey, lambda { |survey|
|
29
|
+
where(survey_id: survey.try(:id))
|
30
|
+
}
|
31
|
+
|
32
|
+
scope :exclude_survey, lambda { |survey|
|
33
|
+
where("NOT survey_id = #{survey.try(:id)}")
|
34
|
+
}
|
35
|
+
|
36
|
+
scope :for_participant, lambda { |participant|
|
37
|
+
where(participant_id: participant.try(:id),
|
38
|
+
participant_type: participant.class.to_s)
|
39
|
+
}
|
40
|
+
|
41
|
+
scope :wins, -> { where(winner: true) }
|
42
|
+
scope :looses, -> { where(winner: false) }
|
43
|
+
scope :scores, -> { order('score DESC') }
|
44
|
+
|
45
|
+
# callbacks
|
46
|
+
|
47
|
+
validate :check_number_of_attempts_by_survey, on: :create
|
48
|
+
after_create :collect_scores
|
49
|
+
|
50
|
+
def correct_answers
|
51
|
+
answers.where(correct: true)
|
52
|
+
end
|
53
|
+
|
54
|
+
def incorrect_answers
|
55
|
+
answers.where(correct: false)
|
56
|
+
end
|
57
|
+
|
58
|
+
def collect_scores!
|
59
|
+
collect_scores
|
60
|
+
save
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.high_score
|
64
|
+
scores.first.score
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def check_number_of_attempts_by_survey
|
70
|
+
attempts = self.class.for_survey(survey).for_participant(participant)
|
71
|
+
upper_bound = survey.attempts_number
|
72
|
+
return unless attempts.size >= upper_bound && upper_bound.nonzero?
|
73
|
+
|
74
|
+
errors.add(:questionnaire_id,
|
75
|
+
'Number of attempts exceeded')
|
76
|
+
end
|
77
|
+
|
78
|
+
def collect_scores
|
79
|
+
multi_select_questions = AskIt::Question.joins(:section)
|
80
|
+
.where(survey_sections: { survey_id: survey.id },
|
81
|
+
survey_questions: {
|
82
|
+
questions_type_id: AskIt::QuestionType.multi_select
|
83
|
+
})
|
84
|
+
if multi_select_questions.empty? # No multi-select questions
|
85
|
+
raw_score = answers.map(&:value).reduce(:+)
|
86
|
+
self.score = raw_score
|
87
|
+
else
|
88
|
+
# Initial score without multi-select questions
|
89
|
+
raw_score = answers.where.not(question_id: multi_select_questions.ids).map(&:value).reduce(:+) || 0
|
90
|
+
multi_select_questions.each do |question|
|
91
|
+
options = question.options
|
92
|
+
correct_question_answers = answers.where(question_id: question.id, correct: true)
|
93
|
+
break if correct_question_answers.empty? # If they didn't select any correct answers, then skip this step
|
94
|
+
|
95
|
+
correct_options_sum = options.correct.map(&:weight).reduce(:+)
|
96
|
+
correct_percentage = correct_question_answers.map(&:value).reduce(:+).fdiv(correct_options_sum)
|
97
|
+
raw_score += correct_percentage
|
98
|
+
if correct_percentage == 1
|
99
|
+
option_value = 1 / options.count.to_f
|
100
|
+
raw_score -= (option_value * answers.where(question_id: question.id, correct: false).count)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
self.score = raw_score || 0
|
104
|
+
save
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|