limesurvey_rails 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +23 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +42 -0
- data/app/assets/images/limesurvey_rails/.gitkeep +0 -0
- data/app/assets/javascripts/limesurvey_rails/application.js +15 -0
- data/app/assets/javascripts/limesurvey_rails/surveys.js +2 -0
- data/app/assets/stylesheets/limesurvey_rails/application.css +13 -0
- data/app/assets/stylesheets/limesurvey_rails/surveys.css +4 -0
- data/app/assets/stylesheets/scaffold.css +56 -0
- data/app/controllers/limesurvey_rails/application_controller.rb +3 -0
- data/app/controllers/limesurvey_rails/surveys_controller.rb +51 -0
- data/app/helpers/limesurvey_rails/application_helper.rb +4 -0
- data/app/helpers/limesurvey_rails/surveys_helper.rb +4 -0
- data/app/models/limesurvey_rails/no_persistence_active_model.rb +33 -0
- data/app/models/limesurvey_rails/survey.rb +151 -0
- data/app/models/limesurvey_rails/survey_participation.rb +78 -0
- data/app/views/limesurvey_rails/surveys/_form.html.haml +16 -0
- data/app/views/limesurvey_rails/surveys/edit.html.haml +7 -0
- data/app/views/limesurvey_rails/surveys/index.html.haml +21 -0
- data/app/views/limesurvey_rails/surveys/new.html.haml +5 -0
- data/app/views/limesurvey_rails/surveys/show.html.haml +12 -0
- data/config/locales/en.yml +2 -0
- data/config/locales/it.yml +15 -0
- data/config/routes.rb +7 -0
- data/db/migrate/20140130110450_create_limesurvey_rails_survey_participations.rb +11 -0
- data/lib/limesurvey_rails.rb +10 -0
- data/lib/limesurvey_rails/configuration.rb +31 -0
- data/lib/limesurvey_rails/engine.rb +21 -0
- data/lib/limesurvey_rails/exceptions.rb +7 -0
- data/lib/limesurvey_rails/limesurvey_rails.rb +86 -0
- data/lib/limesurvey_rails/participant.rb +86 -0
- data/lib/limesurvey_rails/version.rb +3 -0
- data/limesurvey_rails.gemspec +30 -0
- data/script/rails +8 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +13 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/test_model.rb +7 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/config/application.rb +65 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +7 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +38 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +4 -0
- data/spec/dummy/db/development.sqlite3 +0 -0
- data/spec/dummy/db/migrate/20140130103310_create_test_models.rb +12 -0
- data/spec/dummy/db/schema.rb +34 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/dummy/spec/factories/test_models.rb +10 -0
- data/spec/factories/limesurvey_rails_survey_participations.rb +9 -0
- data/spec/factories/limesurvey_rails_surveys.rb +8 -0
- data/spec/limesurvey_rails_spec.rb +97 -0
- data/spec/models/limesurvey_rails/participant_spec.rb +174 -0
- data/spec/models/limesurvey_rails/survey_participations_spec.rb +113 -0
- data/spec/models/limesurvey_rails/survey_spec.rb +222 -0
- data/spec/spec_helper.rb +77 -0
- data/spec/support/shared_examples.rb +56 -0
- data/spec/support/test_limesurvey_constants.rb +26 -0
- data/spec/support/utils.rb +47 -0
- metadata +286 -0
@@ -0,0 +1,78 @@
|
|
1
|
+
module LimesurveyRails
|
2
|
+
class SurveyParticipation < ActiveRecord::Base
|
3
|
+
|
4
|
+
GET_PARTICIPANT_PROPERTY_ATTRIBUTES = %w{ attribute_1 tid completed language usesleft firstname blacklisted validfrom lastname sent validuntil email remindersent mpid emailstatus remindercount }
|
5
|
+
|
6
|
+
attr_accessible :participant_id, :survey_id
|
7
|
+
attr_accessor *GET_PARTICIPANT_PROPERTY_ATTRIBUTES
|
8
|
+
|
9
|
+
validates :participant_id, :uniqueness => {:scope => :survey_id}
|
10
|
+
|
11
|
+
validates_presence_of [:participant_id, :survey_id]
|
12
|
+
validates_presence_of :token, :on => :update
|
13
|
+
|
14
|
+
scope :for_survey, lambda { |survey_id| where(:survey_id => survey_id) }
|
15
|
+
|
16
|
+
after_initialize :assign_ls_token_attributes, :unless => 'new_record?'
|
17
|
+
after_create :add_token_to_limesurvey
|
18
|
+
after_destroy :delete_token_from_limesurvey
|
19
|
+
|
20
|
+
def url
|
21
|
+
LimesurveyRails.configuration.base_url + "/survey/index/sid/#{survey_id}/token/#{token}/lang//newtest/Y"
|
22
|
+
end
|
23
|
+
|
24
|
+
def survey
|
25
|
+
Survey.find(survey_id)
|
26
|
+
end
|
27
|
+
|
28
|
+
%w{ completed sent }.each do |name|
|
29
|
+
define_method "#{name}?" do
|
30
|
+
case send(name)
|
31
|
+
when 'Y'
|
32
|
+
true
|
33
|
+
when 'N'
|
34
|
+
false
|
35
|
+
else
|
36
|
+
true
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def assign_ls_token_attributes
|
44
|
+
begin
|
45
|
+
LimesurveyRails.get_participant_properties(survey_id,token_id,GET_PARTICIPANT_PROPERTY_ATTRIBUTES).each{ |a,v| self.send("#{a}=",v)}
|
46
|
+
rescue RemoteControlError => e
|
47
|
+
if e.message.end_with? 'Invalid tokenid'
|
48
|
+
raise InconsistentParticipantsError, "Participation #{self} was not found on Limesurvey"
|
49
|
+
else
|
50
|
+
raise e
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_token_to_limesurvey
|
56
|
+
opts = [:email, :firstname, :lastname, :attribute_1].inject({}) do |h,a|
|
57
|
+
method = participant.class.send("limesurvey_participant_#{a}_attr")
|
58
|
+
if method and participant.respond_to? method
|
59
|
+
h.merge(a => participant.send(method))
|
60
|
+
else
|
61
|
+
h
|
62
|
+
end
|
63
|
+
end
|
64
|
+
opts.merge!(:emailstatus => (opts[:email].nil? ? 'UNKNOWN' : 'OK') )
|
65
|
+
|
66
|
+
res = LimesurveyRails.add_participants(survey_id,[opts],true)
|
67
|
+
self.token_id = res.first['tid']
|
68
|
+
self.token = res.first['token']
|
69
|
+
save!
|
70
|
+
end
|
71
|
+
|
72
|
+
def delete_token_from_limesurvey
|
73
|
+
res = LimesurveyRails.delete_participants(survey_id,[token_id])
|
74
|
+
raise RemoteControlError, "something went wrong while deleting participant #{token_id} on survey #{survey_id}" unless res.keys == [token_id]
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
= form_for @survey do |f|
|
2
|
+
- if @survey.errors.any?
|
3
|
+
#error_explanation
|
4
|
+
%h2= "#{pluralize(@survey.errors.count, "error")} prohibited this survey from being saved:"
|
5
|
+
%ul
|
6
|
+
- @survey.errors.full_messages.each do |msg|
|
7
|
+
%li= msg
|
8
|
+
|
9
|
+
.field
|
10
|
+
= f.label :title
|
11
|
+
= f.text_field :surveyls_title
|
12
|
+
.field
|
13
|
+
= f.label :lang
|
14
|
+
= f.text_field :surveyls_language
|
15
|
+
.actions
|
16
|
+
= f.submit 'Save'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
%h1 Listing surveys
|
2
|
+
|
3
|
+
%table
|
4
|
+
%tr
|
5
|
+
%th Title
|
6
|
+
%th Lang
|
7
|
+
%th
|
8
|
+
%th
|
9
|
+
%th
|
10
|
+
|
11
|
+
- @surveys.each do |survey|
|
12
|
+
%tr
|
13
|
+
%td= survey.title
|
14
|
+
%td= survey.lang
|
15
|
+
%td= link_to 'Show', survey_path(survey.id)
|
16
|
+
%td= link_to 'Edit', edit_survey_path(survey.id)
|
17
|
+
%td= link_to 'Destroy', survey, :method => :delete, :data => { :confirm => 'Are you sure?' }
|
18
|
+
|
19
|
+
%br
|
20
|
+
|
21
|
+
= link_to 'New Survey', new_survey_path
|
@@ -0,0 +1,15 @@
|
|
1
|
+
it:
|
2
|
+
activerecord:
|
3
|
+
models:
|
4
|
+
"limesurvey_rails/survey":
|
5
|
+
one: questionario
|
6
|
+
other: questionari
|
7
|
+
attributes:
|
8
|
+
"limesurvey_rails/survey":
|
9
|
+
title: titolo
|
10
|
+
language: lingua
|
11
|
+
active: attivo
|
12
|
+
token_sent: partecipanti invitati
|
13
|
+
token_count: partecipanti
|
14
|
+
complete_responses: questionari completati
|
15
|
+
|
data/config/routes.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
class CreateLimesurveyRailsSurveyParticipations < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :limesurvey_rails_survey_participations do |t|
|
4
|
+
t.integer :participant_id
|
5
|
+
t.integer :survey_id
|
6
|
+
t.string :token_id
|
7
|
+
t.string :token
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require "limesurvey_rails/engine"
|
2
|
+
require "limesurvey"
|
3
|
+
require "limesurvey_rails/configuration"
|
4
|
+
require "limesurvey_rails/exceptions"
|
5
|
+
require "limesurvey_rails/participant"
|
6
|
+
require "limesurvey_rails/limesurvey_rails"
|
7
|
+
|
8
|
+
module LimesurveyRails
|
9
|
+
|
10
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module LimesurveyRails
|
2
|
+
|
3
|
+
class << self
|
4
|
+
attr_accessor :configuration
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.configure
|
8
|
+
self.configuration ||= Configuration.new
|
9
|
+
yield(configuration)
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.configured?
|
13
|
+
!(configuration and configuration.api_url and configuration.username and configuration.password).blank?
|
14
|
+
end
|
15
|
+
|
16
|
+
class Configuration
|
17
|
+
attr_accessor :api_url, :username, :password, :auto_connection, :max_number_of_tokens_per_survey
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
@api_url = nil
|
21
|
+
@username = nil
|
22
|
+
@password = nil
|
23
|
+
@auto_connection = false
|
24
|
+
@max_number_of_tokens_per_survey = 100000
|
25
|
+
end
|
26
|
+
|
27
|
+
def base_url
|
28
|
+
@api_url.gsub('/admin/remotecontrol','')
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module LimesurveyRails
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace LimesurveyRails
|
4
|
+
|
5
|
+
config.generators do |g|
|
6
|
+
g.test_framework :rspec, :fixture => false
|
7
|
+
g.fixture_replacement :factory_girl, :dir => 'spec/factories'
|
8
|
+
g.assets false
|
9
|
+
g.helper false
|
10
|
+
g.template_engine :haml
|
11
|
+
end
|
12
|
+
|
13
|
+
# http://edgeguides.rubyonrails.org/engines.html#overriding-models-and-controllers
|
14
|
+
config.to_prepare do
|
15
|
+
Dir.glob(Rails.root + "app/decorators/**/*_decorator*.rb").each do |c|
|
16
|
+
require_dependency(c)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
module LimesurveyRails
|
2
|
+
class GenericError < StandardError; end
|
3
|
+
class RemoteControlError < GenericError; end
|
4
|
+
class WrongArgumentError < GenericError; end
|
5
|
+
class InconsistentParticipantsError < GenericError; end
|
6
|
+
class MultipleParticipationsToSurveyError < GenericError; end
|
7
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module LimesurveyRails
|
2
|
+
API_METHODS = %w{get_session_key release_session_key get_site_settings add_survey delete_survey import_survey get_survey_properties set_survey_properties list_surveys activate_survey export_statistics get_summary add_language delete_survey_language get_language_properties set_language_properties add_group delete_group import_group get_group_properties set_group_properties list_groups list_users delete_question import_question get_question_properties set_question_properties list_questions activate_tokens add_participants delete_participants get_participant_properties set_participant_properties list_participants activate_tokens invite_participants remind_participants add_response export_responses export_responses_by_token}
|
3
|
+
|
4
|
+
class << self
|
5
|
+
attr_accessor :api, :session_key
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.connect(force = false)
|
9
|
+
if !force and connected?
|
10
|
+
false
|
11
|
+
else
|
12
|
+
if configured?
|
13
|
+
self.api = Limesurvey::API.new(configuration.api_url)
|
14
|
+
self.session_key = api.get_session_key(configuration.username,configuration.password) if api
|
15
|
+
end
|
16
|
+
session_key.present? and session_key.is_a? String
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.disconnect
|
21
|
+
if connected?
|
22
|
+
api.release_session_key if api
|
23
|
+
self.api = nil
|
24
|
+
self.session_key = nil
|
25
|
+
api.nil? and session_key.nil?
|
26
|
+
else
|
27
|
+
false
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.connected?
|
32
|
+
begin
|
33
|
+
raise unless configured?
|
34
|
+
raise unless session_key.present?
|
35
|
+
check_result = api.list_surveys(session_key)
|
36
|
+
raise unless check_result.is_a? Array or check_result['status'] == 'No surveys found'
|
37
|
+
true
|
38
|
+
rescue Exception => e
|
39
|
+
if configuration.try(:auto_connection)
|
40
|
+
connect(true)
|
41
|
+
else
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.reset
|
48
|
+
disconnect
|
49
|
+
self.configuration = nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.method_missing(method_name, *arguments, &block)
|
53
|
+
if API_METHODS.include? method_name.to_s
|
54
|
+
if connected?
|
55
|
+
# t0=Time.now
|
56
|
+
result = api.send(method_name, *arguments.unshift(session_key))
|
57
|
+
# puts "#{method_name} in #{Time.now-t0}"
|
58
|
+
if result.is_a? Hash and result["status"] # get exception result
|
59
|
+
case result["status"]
|
60
|
+
when 'OK'
|
61
|
+
true
|
62
|
+
when 'No surveys found'
|
63
|
+
[]
|
64
|
+
when 'No Tokens found'
|
65
|
+
[]
|
66
|
+
when /(left to send)|(No candidate tokens)$/
|
67
|
+
result # get regular result
|
68
|
+
else
|
69
|
+
raise RemoteControlError, "#{method_name} returned a failure response status: #{result["status"]}"
|
70
|
+
end
|
71
|
+
else # get regular result
|
72
|
+
result
|
73
|
+
end
|
74
|
+
else
|
75
|
+
raise RemoteControlError, 'you are disconnected from Limesurvey'
|
76
|
+
end
|
77
|
+
else
|
78
|
+
super
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.respond_to_missing?(method_name, include_private = false)
|
83
|
+
API_METHODS.include? method_name.to_s || super
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
module LimesurveyRails
|
2
|
+
|
3
|
+
module Participant
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def is_a_limesurvey_participant(opts = {})
|
8
|
+
# puts "################################ init #{self} as a limesurvey_participant with opts #{opts}"
|
9
|
+
self.limesurvey_participant = true
|
10
|
+
|
11
|
+
# check options
|
12
|
+
opts.keys.each{|k| raise WrongArgumentError unless [:attribute_1_attr, :email_attr, :firstname_attr, :lastname_attr, :dependent_participations].include? k}
|
13
|
+
|
14
|
+
{:attribute_1_attr => 'id', :email_attr => nil, :firstname_attr => nil, :lastname_attr => nil, :dependent_participations => :nullify }.each do |k,v|
|
15
|
+
class_attribute_name = "limesurvey_participant_#{k}"
|
16
|
+
class_attribute class_attribute_name
|
17
|
+
self.send("#{class_attribute_name}=", (opts[k] or v))
|
18
|
+
end
|
19
|
+
|
20
|
+
has_many :survey_participations, :class_name => "LimesurveyRails::SurveyParticipation", :foreign_key => "participant_id", :dependent => limesurvey_participant_dependent_participations do
|
21
|
+
def for_survey(survey_id)
|
22
|
+
res = where("survey_id == #{survey_id}")
|
23
|
+
raise MultipleParticipationsToSurveyError if res.size > 1
|
24
|
+
res.first
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
LimesurveyRails::SurveyParticipation.class_exec(self){ |klass| belongs_to :participant, :class_name => klass, :foreign_key => 'participant_id'}
|
29
|
+
# LimesurveyRails::SurveyParticipation.belongs_to :participant, :class_name => self.name, :foreign_key => 'participant_id'
|
30
|
+
|
31
|
+
# below the proof that among tests associated classes not get updated even after remove_const calls
|
32
|
+
# see also http://code.activestate.com/lists/ruby-talk/42730/
|
33
|
+
# puts (LimesurveyRails::SurveyParticipation.reflect_on_association(:participant).klass.object_id == self.object_id)
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
def is_a_limesurvey_participant_class?
|
38
|
+
limesurvey_participant
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module InstanceMethods
|
43
|
+
|
44
|
+
def add_to_survey(survey)
|
45
|
+
if survey.is_a? Survey
|
46
|
+
survey.add_participant(self)
|
47
|
+
else
|
48
|
+
raise WrongArgumentError, "expexted Survey object, got #{survey.class}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def remove_from_survey(survey)
|
53
|
+
if survey.is_a? Survey
|
54
|
+
survey.remove_participant(self)
|
55
|
+
else
|
56
|
+
raise WrongArgumentError, "expexted Survey object, got #{survey.class}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def surveys
|
61
|
+
survey_participations.map(&:survey)
|
62
|
+
end
|
63
|
+
|
64
|
+
def available_surveys
|
65
|
+
ids = surveys.map(&:id).map(&:to_s)
|
66
|
+
Survey.all.delete_if{|s| ids.include? s.id }
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.included(receiver)
|
72
|
+
|
73
|
+
# I put this here because I want limesurvey_participant class attribute to be available as soon as you include the gem
|
74
|
+
receiver.class_attribute :limesurvey_participant
|
75
|
+
receiver.limesurvey_participant = false
|
76
|
+
|
77
|
+
receiver.extend ClassMethods
|
78
|
+
receiver.send :include, InstanceMethods
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
ActiveRecord::Base.send :include, Participant
|
84
|
+
|
85
|
+
end
|
86
|
+
|