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.
- checksums.yaml +7 -0
- data/.env.example +3 -0
- data/.gitignore +23 -0
- data/.rspec +1 -0
- data/Gemfile +7 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +7 -0
- data/lib/qualtrics.rb +52 -0
- data/lib/qualtrics/configuration.rb +17 -0
- data/lib/qualtrics/distribution.rb +90 -0
- data/lib/qualtrics/entity.rb +55 -0
- data/lib/qualtrics/mailer.rb +111 -0
- data/lib/qualtrics/message.rb +75 -0
- data/lib/qualtrics/operation.rb +77 -0
- data/lib/qualtrics/panel.rb +69 -0
- data/lib/qualtrics/panel_import.rb +41 -0
- data/lib/qualtrics/panel_import_file.rb +28 -0
- data/lib/qualtrics/recipient.rb +109 -0
- data/lib/qualtrics/recipient_import_row.rb +40 -0
- data/lib/qualtrics/response.rb +35 -0
- data/lib/qualtrics/survey.rb +80 -0
- data/lib/qualtrics/survey_import.rb +26 -0
- data/lib/qualtrics/transaction.rb +70 -0
- data/lib/qualtrics/version.rb +3 -0
- data/qualtrics.gemspec +34 -0
- data/spec/fixtures/sample_survey.xml +2 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Distribution/creating_to_qualtrics/can_retrieve_a_panel_of_distributions_in_qualtrics.yml +655 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Distribution/creating_to_qualtrics/can_update_itself.yml +599 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Mailer/creating_to_qualtrics/sends_a_reminder_to_a_distribution.yml +493 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Mailer/creating_to_qualtrics/sends_a_survey_to_a_panel_and_creates_a_distribution.yml +441 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Mailer/creating_to_qualtrics/sends_a_survey_to_an_individual_and_creates_a_distribution.yml +441 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Message/creating_to_qualtrics/persists_to_qualtrics.yml +56 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Message/creating_to_qualtrics/populates_the_message_id_when_successful.yml +56 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Message/creating_to_qualtrics/populates_the_success_attribute.yml +56 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Message/retrieves_an_array_of_all_panels_in_a_library.yml +115 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Panel/creating_to_qualtrics/persists_to_qualtrics.yml +107 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Panel/creating_to_qualtrics/populates_the_panel_id_when_successful.yml +107 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Panel/creating_to_qualtrics/populates_the_success_attribute.yml +107 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Panel/destroys_a_panel_that_returns_true_when_successful.yml +107 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Panel/raises_an_error_when_you_attempt_to_save_an_already_presisted_panel.yml +55 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Panel/retrieves_an_array_of_all_panels_in_a_library.yml +704 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_PanelImport/transmits_to_qualtrics.yml +72 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/creating_to_qualtrics/persists_to_qualtrics.yml +159 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/creating_to_qualtrics/populates_the_recipient_id_when_successful.yml +159 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/creating_to_qualtrics/populates_the_success_attribute.yml +159 -0
- 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
- data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/recipient_made_in_qualtrics/can_delete_itself_in_qualtrics.yml +311 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/recipient_made_in_qualtrics/can_update_itself_in_qualtrics.yml +313 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Recipient/recipient_made_in_qualtrics/gets_its_information_in_qualtrics.yml +209 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Survey/creating_to_qualtrics/can_be_activated_or_deactivated.yml +232 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Survey/creating_to_qualtrics/destroys_a_survey_that_returns_true_when_successful.yml +128 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Survey/creating_to_qualtrics/populates_the_survey_id_when_successful.yml +128 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_Survey/creating_to_qualtrics/retrieves_an_array_of_all_surveys.yml +203 -0
- data/spec/fixtures/vcr_cassettes/Qualtrics_SurveyImport/transmits_to_qualtrics.yml +128 -0
- data/spec/qualtrics/configuration_spec.rb +53 -0
- data/spec/qualtrics/distribution_spec.rb +144 -0
- data/spec/qualtrics/mailer_spec.rb +127 -0
- data/spec/qualtrics/message_spec.rb +105 -0
- data/spec/qualtrics/panel_import_file_spec.rb +26 -0
- data/spec/qualtrics/panel_import_spec.rb +43 -0
- data/spec/qualtrics/panel_spec.rb +80 -0
- data/spec/qualtrics/recipient_import_row_spec.rb +62 -0
- data/spec/qualtrics/recipient_spec.rb +185 -0
- data/spec/qualtrics/response_spec.rb +41 -0
- data/spec/qualtrics/survey_import_spec.rb +44 -0
- data/spec/qualtrics/survey_spec.rb +125 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/_dotenv.rb +2 -0
- data/spec/support/qualtrics.rb +5 -0
- data/spec/support/vcr.rb +11 -0
- 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
|