qualtrics 0.5.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.
Files changed (72) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +3 -0
  3. data/.gitignore +23 -0
  4. data/.rspec +1 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +29 -0
  8. data/Rakefile +7 -0
  9. data/lib/qualtrics.rb +52 -0
  10. data/lib/qualtrics/configuration.rb +17 -0
  11. data/lib/qualtrics/distribution.rb +90 -0
  12. data/lib/qualtrics/entity.rb +55 -0
  13. data/lib/qualtrics/mailer.rb +111 -0
  14. data/lib/qualtrics/message.rb +75 -0
  15. data/lib/qualtrics/operation.rb +77 -0
  16. data/lib/qualtrics/panel.rb +69 -0
  17. data/lib/qualtrics/panel_import.rb +41 -0
  18. data/lib/qualtrics/panel_import_file.rb +28 -0
  19. data/lib/qualtrics/recipient.rb +109 -0
  20. data/lib/qualtrics/recipient_import_row.rb +40 -0
  21. data/lib/qualtrics/response.rb +35 -0
  22. data/lib/qualtrics/survey.rb +80 -0
  23. data/lib/qualtrics/survey_import.rb +26 -0
  24. data/lib/qualtrics/transaction.rb +70 -0
  25. data/lib/qualtrics/version.rb +3 -0
  26. data/qualtrics.gemspec +34 -0
  27. data/spec/fixtures/sample_survey.xml +2 -0
  28. data/spec/fixtures/vcr_cassettes/Qualtrics_Distribution/creating_to_qualtrics/can_retrieve_a_panel_of_distributions_in_qualtrics.yml +655 -0
  29. data/spec/fixtures/vcr_cassettes/Qualtrics_Distribution/creating_to_qualtrics/can_update_itself.yml +599 -0
  30. data/spec/fixtures/vcr_cassettes/Qualtrics_Mailer/creating_to_qualtrics/sends_a_reminder_to_a_distribution.yml +493 -0
  31. data/spec/fixtures/vcr_cassettes/Qualtrics_Mailer/creating_to_qualtrics/sends_a_survey_to_a_panel_and_creates_a_distribution.yml +441 -0
  32. data/spec/fixtures/vcr_cassettes/Qualtrics_Mailer/creating_to_qualtrics/sends_a_survey_to_an_individual_and_creates_a_distribution.yml +441 -0
  33. data/spec/fixtures/vcr_cassettes/Qualtrics_Message/creating_to_qualtrics/persists_to_qualtrics.yml +56 -0
  34. data/spec/fixtures/vcr_cassettes/Qualtrics_Message/creating_to_qualtrics/populates_the_message_id_when_successful.yml +56 -0
  35. data/spec/fixtures/vcr_cassettes/Qualtrics_Message/creating_to_qualtrics/populates_the_success_attribute.yml +56 -0
  36. data/spec/fixtures/vcr_cassettes/Qualtrics_Message/retrieves_an_array_of_all_panels_in_a_library.yml +115 -0
  37. data/spec/fixtures/vcr_cassettes/Qualtrics_Panel/creating_to_qualtrics/persists_to_qualtrics.yml +107 -0
  38. data/spec/fixtures/vcr_cassettes/Qualtrics_Panel/creating_to_qualtrics/populates_the_panel_id_when_successful.yml +107 -0
  39. data/spec/fixtures/vcr_cassettes/Qualtrics_Panel/creating_to_qualtrics/populates_the_success_attribute.yml +107 -0
  40. data/spec/fixtures/vcr_cassettes/Qualtrics_Panel/destroys_a_panel_that_returns_true_when_successful.yml +107 -0
  41. data/spec/fixtures/vcr_cassettes/Qualtrics_Panel/raises_an_error_when_you_attempt_to_save_an_already_presisted_panel.yml +55 -0
  42. data/spec/fixtures/vcr_cassettes/Qualtrics_Panel/retrieves_an_array_of_all_panels_in_a_library.yml +704 -0
  43. data/spec/fixtures/vcr_cassettes/Qualtrics_PanelImport/transmits_to_qualtrics.yml +72 -0
  44. data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/creating_to_qualtrics/persists_to_qualtrics.yml +159 -0
  45. data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/creating_to_qualtrics/populates_the_recipient_id_when_successful.yml +159 -0
  46. data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/creating_to_qualtrics/populates_the_success_attribute.yml +159 -0
  47. data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/creating_to_qualtrics/raises_an_error_when_a_recipient_is_created_without_specifying_a_panel_id.yml +107 -0
  48. data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/recipient_made_in_qualtrics/can_delete_itself_in_qualtrics.yml +311 -0
  49. data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/recipient_made_in_qualtrics/can_update_itself_in_qualtrics.yml +313 -0
  50. data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/recipient_made_in_qualtrics/gets_its_information_in_qualtrics.yml +209 -0
  51. data/spec/fixtures/vcr_cassettes/Qualtrics_Survey/creating_to_qualtrics/can_be_activated_or_deactivated.yml +232 -0
  52. data/spec/fixtures/vcr_cassettes/Qualtrics_Survey/creating_to_qualtrics/destroys_a_survey_that_returns_true_when_successful.yml +128 -0
  53. data/spec/fixtures/vcr_cassettes/Qualtrics_Survey/creating_to_qualtrics/populates_the_survey_id_when_successful.yml +128 -0
  54. data/spec/fixtures/vcr_cassettes/Qualtrics_Survey/creating_to_qualtrics/retrieves_an_array_of_all_surveys.yml +203 -0
  55. data/spec/fixtures/vcr_cassettes/Qualtrics_SurveyImport/transmits_to_qualtrics.yml +128 -0
  56. data/spec/qualtrics/configuration_spec.rb +53 -0
  57. data/spec/qualtrics/distribution_spec.rb +144 -0
  58. data/spec/qualtrics/mailer_spec.rb +127 -0
  59. data/spec/qualtrics/message_spec.rb +105 -0
  60. data/spec/qualtrics/panel_import_file_spec.rb +26 -0
  61. data/spec/qualtrics/panel_import_spec.rb +43 -0
  62. data/spec/qualtrics/panel_spec.rb +80 -0
  63. data/spec/qualtrics/recipient_import_row_spec.rb +62 -0
  64. data/spec/qualtrics/recipient_spec.rb +185 -0
  65. data/spec/qualtrics/response_spec.rb +41 -0
  66. data/spec/qualtrics/survey_import_spec.rb +44 -0
  67. data/spec/qualtrics/survey_spec.rb +125 -0
  68. data/spec/spec_helper.rb +11 -0
  69. data/spec/support/_dotenv.rb +2 -0
  70. data/spec/support/qualtrics.rb +5 -0
  71. data/spec/support/vcr.rb +11 -0
  72. metadata +330 -0
@@ -0,0 +1,75 @@
1
+ module Qualtrics
2
+ class Message < Entity
3
+ ALLOWED_CATEGORIES = %w(InviteEmails InactiveSurveyMessages ReminderEmails
4
+ ThankYouEmails EndOfSurveyMessages GeneralMessages
5
+ ValidationMessages LookAndFeelMessages)
6
+
7
+ attr_accessor :id, :name, :category, :body, :language
8
+ validates :category, inclusion: { in: ALLOWED_CATEGORIES }
9
+
10
+ def self.all(library_id = nil)
11
+ lib_id = library_id || configuration.default_library_id
12
+ response = get('getLibraryMessages', {'LibraryID' => lib_id})
13
+ if response.success?
14
+ response.result.map do |category, messages|
15
+ messages.map do |message_id, name|
16
+ new(id: message_id, name: name, category: category_map[category])
17
+ end
18
+ end.flatten
19
+ else
20
+ []
21
+ end
22
+ end
23
+
24
+ def initialize(options={})
25
+ @name = options[:name]
26
+ @id = options[:id]
27
+ @category = options[:category]
28
+ @library_id = options[:library_id]
29
+ @body = options[:body]
30
+ @language = options[:language] || 'EN'
31
+ end
32
+ #
33
+ def save
34
+ return false if !valid?
35
+ response = post('createLibraryMessage', attributes)
36
+
37
+ if response.success?
38
+ self.id = response.result['MessageID']
39
+ true
40
+ else
41
+ false
42
+ end
43
+ end
44
+ #
45
+ # def destroy
46
+ # response = post('deleteLibraryMessage', {
47
+ # 'LibraryID' => library_id,
48
+ # 'MessageID' => self.id
49
+ # })
50
+ # response.success?
51
+ # end
52
+ #
53
+ def attributes
54
+ {
55
+ 'LibraryID' => library_id,
56
+ 'Category' => category,
57
+ 'Name' => name,
58
+ 'Message' => body,
59
+ 'Language' => language
60
+ }
61
+ end
62
+
63
+ def self.category_map
64
+ {
65
+ 'INVITE' => 'InviteEmails',
66
+ 'REMINDER' => 'ReminderEmails',
67
+ 'THANKYOU' => 'ThankYouEmails',
68
+ 'ENDOFSURVEY' => 'EndOfSurveyMessages',
69
+ 'INACTIVESURVEY' => 'InactiveSurveyMessages',
70
+ 'GENERAL' => 'GeneralMessages',
71
+ 'LOOKANDFEEL' => 'LookAndFeelMessages'
72
+ }
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,77 @@
1
+ module Qualtrics
2
+ class Operation
3
+ attr_reader :http_method, :action, :options, :entity_name, :command
4
+ REQUEST_METHOD_WHITELIST = [:get, :post]
5
+ @@listeners = []
6
+
7
+ def initialize(http_method, action, options)
8
+ @http_method = http_method
9
+ @action = action
10
+ @options = options
11
+ @entity_name = action.gsub(/(create|delete|update)/, '')
12
+ @command = $1
13
+ end
14
+
15
+ def issue_request
16
+ raise Qualtrics::UnexpectedRequestMethod if !REQUEST_METHOD_WHITELIST.include?(http_method)
17
+
18
+ body = options.dup.merge(default_params)
19
+ body['Request'] = action
20
+
21
+ Qualtrics::Response.new(connection.send(http_method, path, body)).tap do |response|
22
+ if !@listeners_disabled
23
+ @@listeners.each do |listener|
24
+ listener.received_response(self, response)
25
+ end
26
+ end
27
+ end
28
+ end
29
+
30
+ def disable_listeners(&block)
31
+ @listeners_disabled = true
32
+ block.call(self)
33
+ @listeners_disabled = nil
34
+ end
35
+
36
+ class << self
37
+ def add_listener(listener)
38
+ @@listeners << listener
39
+ end
40
+
41
+ def delete_listener(listener)
42
+ @@listeners.delete(listener)
43
+ end
44
+
45
+ def flush_listeners!
46
+ @@listeners = []
47
+ end
48
+ end
49
+
50
+ protected
51
+ def path
52
+ '/WRAPI/ControlPanel/api.php'
53
+ end
54
+
55
+ def connection
56
+ @connection ||= Faraday.new(:url => 'https://survey.qualtrics.com') do |faraday|
57
+ faraday.request :multipart
58
+ faraday.request :url_encoded
59
+ faraday.use ::FaradayMiddleware::FollowRedirects, limit: 3
60
+ faraday.adapter Faraday.default_adapter
61
+ end
62
+ end
63
+
64
+ def default_params
65
+ {
66
+ 'User' => configuration.user,
67
+ 'Token' => configuration.token,
68
+ 'Version' => configuration.version,
69
+ 'Format' => 'JSON'
70
+ }
71
+ end
72
+
73
+ def configuration
74
+ Qualtrics.configuration
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,69 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'json'
4
+
5
+ module Qualtrics
6
+ class Panel < Entity
7
+ attr_accessor :id, :name, :category
8
+
9
+ def self.all(library_id = nil)
10
+ lib_id = library_id || configuration.default_library_id
11
+ response = get('getPanels', {'LibraryID' => lib_id})
12
+ if response.success?
13
+ response.result['Panels'].map do |panel|
14
+ new(underscore_attributes(panel))
15
+ end
16
+ else
17
+ []
18
+ end
19
+ end
20
+
21
+ def self.attribute_map
22
+ {
23
+ 'LibraryID' => :library_id,
24
+ 'Category' => :category,
25
+ 'Name' => :name,
26
+ 'PanelID' => :id
27
+ }
28
+ end
29
+
30
+ def initialize(options={})
31
+ @name = options[:name]
32
+ @id = options[:id]
33
+ @category = options[:category]
34
+ @library_id = options[:library_id]
35
+ end
36
+
37
+ def save
38
+ response = nil
39
+ if persisted?
40
+ raise Qualtrics::UpdateNotAllowed
41
+ else
42
+ response = post('createPanel', attributes)
43
+ end
44
+
45
+ if response.success?
46
+ self.id = response.result['PanelID']
47
+ true
48
+ else
49
+ false
50
+ end
51
+ end
52
+
53
+ def destroy
54
+ response = post('deletePanel', {
55
+ 'LibraryID' => library_id,
56
+ 'PanelID' => self.id
57
+ })
58
+ response.success?
59
+ end
60
+
61
+ def attributes
62
+ {
63
+ 'LibraryID' => library_id,
64
+ 'Category' => category,
65
+ 'Name' => name
66
+ }
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,41 @@
1
+ require "qualtrics/panel_import_file"
2
+
3
+ module Qualtrics
4
+ class PanelImport < Entity
5
+ attr_accessor :panel, :recipients
6
+
7
+ def initialize(options={})
8
+ @panel = options[:panel]
9
+ @recipients = options[:recipients]
10
+ end
11
+
12
+ def save
13
+ payload = headers
14
+ payload['LibraryID'] = library_id
15
+ payload['ColumnHeaders'] = 1
16
+ file = Qualtrics::PanelImportFile.new(@recipients)
17
+ payload['Data'] = Faraday::UploadIO.new(file.temp_file, 'text/csv')
18
+ post 'importPanel', payload
19
+ true
20
+ end
21
+
22
+ def headers
23
+ {}.tap do |import_headers|
24
+ Qualtrics::RecipientImportRow.fields.each_with_index.map do |field, index|
25
+ import_headers[self.class.attributes[field]] = index + 1
26
+ end
27
+ end
28
+ end
29
+
30
+ def self.attributes
31
+ {
32
+ 'email' => 'Email',
33
+ 'first_name' => 'FirstName',
34
+ 'last_name' => 'LastName',
35
+ 'external_ref' => 'ExternalRef',
36
+ 'unsubscribed' =>'Unsubscribed',
37
+ 'language' => 'Language'
38
+ }
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ require 'tempfile'
2
+ require 'csv'
3
+ require 'qualtrics/recipient_import_row'
4
+
5
+ module Qualtrics
6
+ class PanelImportFile
7
+ attr_reader :recipients
8
+
9
+ def initialize(recipients)
10
+ @recipients = recipients
11
+ end
12
+
13
+ def temp_file
14
+ if @temp_file.nil?
15
+ tmp_file = Tempfile.new('panel_import')
16
+ csv_path = tmp_file.path
17
+ tmp_file.close
18
+ CSV.open(csv_path, 'wb', :force_quotes => true, :write_headers => true, :headers => Qualtrics::RecipientImportRow.fields) do |csv|
19
+ @recipients.each do |recipient|
20
+ csv << Qualtrics::RecipientImportRow.new(recipient).to_a
21
+ end
22
+ end
23
+ @temp_file = csv_path
24
+ end
25
+ @temp_file
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,109 @@
1
+ module Qualtrics
2
+ class Recipient < Entity
3
+ attr_accessor :email, :first_name, :last_name, :language, :external_data, :embedded_data, :unsubscribed, :panel_id, :id
4
+ validates :panel_id, presence: true
5
+ # qualtrics_attribute :library_id, 'LibraryID'
6
+
7
+ def initialize(options={})
8
+ set_attributes(options)
9
+ @panel_id = options[:panel_id]
10
+ @id = options[:id]
11
+ end
12
+
13
+ def set_attributes(options={})
14
+ @email = options[:email]
15
+ @first_name = options[:first_name]
16
+ @last_name = options[:last_name]
17
+ @language = options[:language]
18
+ @external_data = options[:external_data]
19
+ @embedded_data = options[:embedded_data]
20
+ @unsubscribed = options[:unsubscribed]
21
+ end
22
+
23
+ def attributes
24
+ {
25
+ 'LibraryID' => library_id,
26
+ 'PanelID' => panel_id,
27
+ 'Email' => email,
28
+ 'FirstName' => first_name,
29
+ 'LastName' => last_name,
30
+ 'ExternalDataRef' => external_data,
31
+ 'Language' => language,
32
+ 'ED' => embedded_data,
33
+ 'Unsubscribed' => unsubscribed
34
+ }.delete_if {|key, value| value.nil? }
35
+ end
36
+
37
+ def panel=(panel)
38
+ self.panel_id = panel.id
39
+ end
40
+
41
+ def save
42
+ return false if !valid?
43
+ response = post('addRecipient', attributes)
44
+
45
+ if response.success?
46
+ self.id = response.result['RecipientID']
47
+ true
48
+ else
49
+ false
50
+ end
51
+ end
52
+
53
+ def info_hash
54
+ response = get('getRecipient', {'LibraryID' => library_id, 'RecipientID' => id})
55
+ if response.success? && !response.result['Recipient'].nil?
56
+ response.result['Recipient']
57
+ else
58
+ false
59
+ end
60
+ end
61
+
62
+ def update(options={})
63
+ set_attributes(options)
64
+ response = post('updateRecipient', update_params)
65
+
66
+ if response.success?
67
+ true
68
+ else
69
+ false
70
+ end
71
+ end
72
+
73
+ def delete
74
+ response = post('removeRecipient', {
75
+ 'LibraryID' => library_id,
76
+ 'RecipientID' => id,
77
+ 'PanelID' => panel_id
78
+ })
79
+
80
+ if response.success?
81
+ true
82
+ else
83
+ false
84
+ end
85
+ end
86
+
87
+ def self.attribute_map
88
+ {
89
+ 'LibraryID' => :library_id,
90
+ 'PanelID' => :panel_id,
91
+ 'Email' => :email,
92
+ 'FirstName' => :first_name,
93
+ 'LastName' => :last_name,
94
+ 'ExternalDataRef' => :external_data,
95
+ 'Language' => :language,
96
+ 'ED' => :embedded_data,
97
+ 'Unsubscribed' => :unsubscribed
98
+ }
99
+ end
100
+
101
+ protected
102
+ def update_params
103
+ {
104
+ 'LibraryID' => library_id,
105
+ 'RecipientID' => id
106
+ }.merge(attributes)
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,40 @@
1
+ module Qualtrics
2
+ class RecipientImportRow
3
+ attr_reader :recipient
4
+
5
+ def initialize(recipient)
6
+ @recipient = recipient
7
+ end
8
+
9
+ def to_a
10
+ self.class.fields.map do |field|
11
+ field_map[field]
12
+ end
13
+ end
14
+
15
+ def field_map
16
+ {
17
+ 'first_name' => recipient.first_name,
18
+ 'last_name' => recipient.last_name,
19
+ 'email' => recipient.email,
20
+ 'embedded_data' => recipient.embedded_data,
21
+ 'external_data' => recipient.external_data,
22
+ 'unsubscribed' => recipient.unsubscribed,
23
+ 'language' => recipient.language
24
+ }
25
+ end
26
+ class << self
27
+ def fields
28
+ [
29
+ 'first_name',
30
+ 'last_name',
31
+ 'email',
32
+ 'embedded_data',
33
+ 'external_data',
34
+ 'unsubscribed',
35
+ 'language'
36
+ ]
37
+ end
38
+ end
39
+ end
40
+ end