ask_it 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/74596429080573c6b707/maintainability)](https://codeclimate.com/github/growth-constant/ask_it/maintainability)
|
4
|
+
[![Test Coverage](https://api.codeclimate.com/v1/badges/74596429080573c6b707/test_coverage)](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
|