limesurvey_rails 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +23 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +29 -0
  6. data/Rakefile +42 -0
  7. data/app/assets/images/limesurvey_rails/.gitkeep +0 -0
  8. data/app/assets/javascripts/limesurvey_rails/application.js +15 -0
  9. data/app/assets/javascripts/limesurvey_rails/surveys.js +2 -0
  10. data/app/assets/stylesheets/limesurvey_rails/application.css +13 -0
  11. data/app/assets/stylesheets/limesurvey_rails/surveys.css +4 -0
  12. data/app/assets/stylesheets/scaffold.css +56 -0
  13. data/app/controllers/limesurvey_rails/application_controller.rb +3 -0
  14. data/app/controllers/limesurvey_rails/surveys_controller.rb +51 -0
  15. data/app/helpers/limesurvey_rails/application_helper.rb +4 -0
  16. data/app/helpers/limesurvey_rails/surveys_helper.rb +4 -0
  17. data/app/models/limesurvey_rails/no_persistence_active_model.rb +33 -0
  18. data/app/models/limesurvey_rails/survey.rb +151 -0
  19. data/app/models/limesurvey_rails/survey_participation.rb +78 -0
  20. data/app/views/limesurvey_rails/surveys/_form.html.haml +16 -0
  21. data/app/views/limesurvey_rails/surveys/edit.html.haml +7 -0
  22. data/app/views/limesurvey_rails/surveys/index.html.haml +21 -0
  23. data/app/views/limesurvey_rails/surveys/new.html.haml +5 -0
  24. data/app/views/limesurvey_rails/surveys/show.html.haml +12 -0
  25. data/config/locales/en.yml +2 -0
  26. data/config/locales/it.yml +15 -0
  27. data/config/routes.rb +7 -0
  28. data/db/migrate/20140130110450_create_limesurvey_rails_survey_participations.rb +11 -0
  29. data/lib/limesurvey_rails.rb +10 -0
  30. data/lib/limesurvey_rails/configuration.rb +31 -0
  31. data/lib/limesurvey_rails/engine.rb +21 -0
  32. data/lib/limesurvey_rails/exceptions.rb +7 -0
  33. data/lib/limesurvey_rails/limesurvey_rails.rb +86 -0
  34. data/lib/limesurvey_rails/participant.rb +86 -0
  35. data/lib/limesurvey_rails/version.rb +3 -0
  36. data/limesurvey_rails.gemspec +30 -0
  37. data/script/rails +8 -0
  38. data/spec/dummy/README.rdoc +261 -0
  39. data/spec/dummy/Rakefile +7 -0
  40. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  41. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  42. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  43. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  44. data/spec/dummy/app/mailers/.gitkeep +0 -0
  45. data/spec/dummy/app/models/.gitkeep +0 -0
  46. data/spec/dummy/app/models/test_model.rb +7 -0
  47. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  48. data/spec/dummy/config.ru +4 -0
  49. data/spec/dummy/config/application.rb +65 -0
  50. data/spec/dummy/config/boot.rb +10 -0
  51. data/spec/dummy/config/database.yml +25 -0
  52. data/spec/dummy/config/environment.rb +7 -0
  53. data/spec/dummy/config/environments/development.rb +37 -0
  54. data/spec/dummy/config/environments/production.rb +67 -0
  55. data/spec/dummy/config/environments/test.rb +38 -0
  56. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  57. data/spec/dummy/config/initializers/inflections.rb +15 -0
  58. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  59. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  60. data/spec/dummy/config/initializers/session_store.rb +8 -0
  61. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  62. data/spec/dummy/config/locales/en.yml +5 -0
  63. data/spec/dummy/config/routes.rb +4 -0
  64. data/spec/dummy/db/development.sqlite3 +0 -0
  65. data/spec/dummy/db/migrate/20140130103310_create_test_models.rb +12 -0
  66. data/spec/dummy/db/schema.rb +34 -0
  67. data/spec/dummy/lib/assets/.gitkeep +0 -0
  68. data/spec/dummy/log/.gitkeep +0 -0
  69. data/spec/dummy/public/404.html +26 -0
  70. data/spec/dummy/public/422.html +26 -0
  71. data/spec/dummy/public/500.html +25 -0
  72. data/spec/dummy/public/favicon.ico +0 -0
  73. data/spec/dummy/script/rails +6 -0
  74. data/spec/dummy/spec/factories/test_models.rb +10 -0
  75. data/spec/factories/limesurvey_rails_survey_participations.rb +9 -0
  76. data/spec/factories/limesurvey_rails_surveys.rb +8 -0
  77. data/spec/limesurvey_rails_spec.rb +97 -0
  78. data/spec/models/limesurvey_rails/participant_spec.rb +174 -0
  79. data/spec/models/limesurvey_rails/survey_participations_spec.rb +113 -0
  80. data/spec/models/limesurvey_rails/survey_spec.rb +222 -0
  81. data/spec/spec_helper.rb +77 -0
  82. data/spec/support/shared_examples.rb +56 -0
  83. data/spec/support/test_limesurvey_constants.rb +26 -0
  84. data/spec/support/utils.rb +47 -0
  85. 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,7 @@
1
+ %h1 Editing survey
2
+
3
+ = render 'form'
4
+
5
+ = link_to 'Show', @survey
6
+ \|
7
+ = link_to 'Back', surveys_path
@@ -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,5 @@
1
+ %h1 New survey
2
+
3
+ = render 'form'
4
+
5
+ = link_to 'Back', surveys_path
@@ -0,0 +1,12 @@
1
+ %p#notice= notice
2
+
3
+ %p
4
+ %b Title:
5
+ = @survey.title
6
+ %p
7
+ %b Lang:
8
+ = @survey.lang
9
+
10
+ = link_to 'Edit', edit_survey_path(@survey)
11
+ \|
12
+ = link_to 'Back', surveys_path
@@ -0,0 +1,2 @@
1
+ en:
2
+
@@ -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,7 @@
1
+ LimesurveyRails::Engine.routes.draw do
2
+ scope "(:locale)", :locale => /#{I18n.available_locales.join("|")}/ do
3
+ resources :surveys do
4
+ put 'invite_participants', :on => :member
5
+ end
6
+ end
7
+ end
@@ -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
+
@@ -0,0 +1,3 @@
1
+ module LimesurveyRails
2
+ VERSION = "1.1.0"
3
+ end