qualtrics_api 0.0.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.
@@ -0,0 +1,23 @@
1
+ require "faraday"
2
+ require "faraday_middleware"
3
+
4
+ require "qualtrics_api/version"
5
+ require "qualtrics_api/url"
6
+
7
+ require "qualtrics_api/request_error_handler"
8
+
9
+ require "qualtrics_api/client"
10
+ require "qualtrics_api/survey"
11
+ require "qualtrics_api/survey_collection"
12
+ require "qualtrics_api/response_export"
13
+ require "qualtrics_api/response_export_collection"
14
+
15
+ require "qualtrics_api/services/response_export_service"
16
+
17
+ module QualtricsAPI
18
+
19
+ def self.new(token)
20
+ Client.new(api_token: token)
21
+ end
22
+
23
+ end
@@ -0,0 +1,32 @@
1
+ module QualtricsAPI
2
+
3
+ class Client
4
+ attr_reader :api_token
5
+
6
+ def initialize(options = {})
7
+ @api_token = options[:api_token]
8
+ end
9
+
10
+ def surveys(options = {})
11
+ @surveys ||= QualtricsAPI::SurveyCollection.new options.merge({ connection: connection })
12
+ end
13
+
14
+ def response_exports(options = {})
15
+ @response_exports ||= QualtricsAPI::ResponseExportCollection.new options.merge({ connection: connection })
16
+ end
17
+
18
+ def connection
19
+ @conn ||= Faraday.new(url: QualtricsAPI::URL,
20
+ params: { apiToken: @api_token }) do |faraday|
21
+ faraday.request :json
22
+ faraday.response :json, :content_type => /\bjson$/
23
+
24
+ faraday.use FaradayMiddleware::FollowRedirects
25
+ faraday.use QualtricsAPI::RequestErrorHandler
26
+
27
+ faraday.adapter Faraday.default_adapter
28
+ end
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,37 @@
1
+ module QualtricsAPI
2
+
3
+ class RequestErrorHandler < Faraday::Response::Middleware
4
+
5
+ def on_complete(env)
6
+ case env[:status]
7
+ when 404
8
+ raise NotFoundError, "Not Found"
9
+ when 400
10
+ raise BadRequestError, error_message(JSON.parse(env[:body]))
11
+ when 401
12
+ raise UnauthorizedError, error_message(JSON.parse(env[:body]))
13
+ when 500
14
+ raise InternalServerError, error_message(JSON.parse(env[:body]))
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def error_message(response)
21
+ meta = response["meta"]
22
+ [ "[",
23
+ meta["status"], " - ",
24
+ meta["qualtricsErrorCode"] || meta["internalErrorCode"],
25
+ "] ",
26
+ meta["errorMessage"]
27
+ ].join
28
+ end
29
+
30
+ end
31
+
32
+ class NotFoundError < StandardError; end
33
+ class BadRequestError < StandardError; end
34
+ class UnauthorizedError < StandardError; end
35
+ class InternalServerError < StandardError; end
36
+
37
+ end
@@ -0,0 +1,40 @@
1
+ module QualtricsAPI
2
+
3
+ class ResponseExport
4
+
5
+ attr_reader :id
6
+
7
+ def initialize(options = {})
8
+ @conn = options[:connection]
9
+ @id = options[:id]
10
+ end
11
+
12
+ def update_status
13
+ res = @conn.get('surveys/responseExports/' + @id).body["result"]
14
+ @export_progress = res["percentComplete"]
15
+ @file_url = res["fileUrl"]
16
+ @completed = true if @export_progress == 100.0
17
+ self
18
+ end
19
+
20
+ def status
21
+ update_status unless completed?
22
+ "#{@export_progress}%"
23
+ end
24
+
25
+ def percent_completed
26
+ update_status unless completed?
27
+ @export_progress
28
+ end
29
+
30
+ def completed?
31
+ @completed == true
32
+ end
33
+
34
+ def file_url
35
+ update_status unless completed?
36
+ @file_url
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,24 @@
1
+ module QualtricsAPI
2
+ class ResponseExportCollection
3
+ extend Forwardable
4
+ include Enumerable
5
+
6
+ attr_reader :all
7
+
8
+ def_delegator :all, :each
9
+ def_delegator :all, :size
10
+
11
+ def initialize(options = {})
12
+ @conn = options[:connection]
13
+ @all = []
14
+ end
15
+
16
+ def [](export_id); find(export_id); end
17
+ def find(export_id)
18
+ @all.select do |response_export|
19
+ response_export.id == export_id
20
+ end.first || QualtricsAPI::ResponseExport.new(:id => export_id , connection: @conn)
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,90 @@
1
+ module QualtricsAPI
2
+ module Services
3
+ class ResponseExportService
4
+ attr_reader :survey_id,
5
+ :response_set_id,
6
+ :file_type,
7
+ :last_response_id,
8
+ :start_date,
9
+ :end_date,
10
+ :limit,
11
+ :included_question_ids,
12
+ :max_rows,
13
+ :use_labels,
14
+ :decimal_format,
15
+ :seen_unanswered_recode,
16
+ :use_local_time,
17
+ :spss_string_length,
18
+ :result
19
+
20
+ def initialize(options = {})
21
+ @conn = options[:connection]
22
+ @survey_id = options[:survey_id]
23
+ @response_set_id = options[:response_set_id]
24
+ @file_type = options[:file_type] || 'CSV'
25
+ @last_response_id = options[:last_response_id]
26
+ @start_date = options[:start_date]
27
+ @end_date = options[:end_date]
28
+ @limit = options[:limit]
29
+ @included_question_ids = options[:included_question_ids]
30
+ @max_rows = options[:max_rows]
31
+ @use_labels = options.has_key?(:use_labels) ? options[:use_labels] : false
32
+ @decimal_format = options[:decimal_format] || '.'
33
+ @seen_unanswered_recode = options[:seen_unanswered_recode]
34
+ @use_local_time = options.has_key?(:use_local_time) ? options[:use_local_time] : false
35
+ @spss_string_length = options[:spss_string_length]
36
+ @id = options[:id]
37
+ end
38
+
39
+ def start
40
+ response = @conn.get("surveys/#{@survey_id}/responseExports", export_params)
41
+ export_id = response.body["result"]["exportStatus"].split('/').last
42
+ @result = ResponseExport.new(id: export_id, connection: @conn)
43
+ end
44
+
45
+ def export_configurations
46
+ {
47
+ response_set_id: @response_set_id,
48
+ file_type: @file_type,
49
+ last_response_id: @last_response_id,
50
+ start_date: @start_date,
51
+ end_date: @end_date,
52
+ limit: @limit,
53
+ included_question_ids: @included_question_ids,
54
+ max_rows: @max_rows,
55
+ use_labels: @use_labels,
56
+ decimal_format: @decimal_format,
57
+ seen_unanswered_recode: @seen_unanswered_recode,
58
+ use_local_time: @use_local_time,
59
+ spss_string_length: @spss_string_length
60
+ }
61
+ end
62
+
63
+ private
64
+
65
+ def param_mappings
66
+ {
67
+ response_set_id: "responseSetId",
68
+ file_type: "fileType",
69
+ last_response_id: "lastResponseId",
70
+ start_date: "startDate",
71
+ end_date: "endDate",
72
+ limit: "limit",
73
+ included_question_ids: "includedQuestionIds",
74
+ max_rows: "maxRows",
75
+ use_labels: "useLabels",
76
+ decimal_format: "decimalFormat",
77
+ seen_unanswered_recode: "seenUnansweredRecode",
78
+ use_local_time: "useLocalTime",
79
+ spss_string_length: "spssStringLength"
80
+ }
81
+ end
82
+
83
+ def export_params
84
+ export_configurations.map do |config_key, value|
85
+ [param_mappings[config_key], value] unless value.nil? || value.to_s.empty?
86
+ end.compact.to_h
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,29 @@
1
+ module QualtricsAPI
2
+
3
+ class Survey
4
+ attr_accessor :id, :name, :owner_id, :last_modified, :status
5
+
6
+ def initialize(options = {})
7
+ attributes_mappings.each do |key, qualtrics_key|
8
+ instance_variable_set "@#{key}", options[qualtrics_key]
9
+ end
10
+ @conn = options[:connection]
11
+ end
12
+
13
+ def export_responses(export_options = {})
14
+ QualtricsAPI::Services::ResponseExportService.new(export_options.merge(survey_id: id, connection: @conn))
15
+ end
16
+
17
+ private
18
+
19
+ def attributes_mappings
20
+ {
21
+ :id => "id",
22
+ :name => "name",
23
+ :owner_id => "ownerId",
24
+ :last_modified => "lastModified",
25
+ :status => "status"
26
+ }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,64 @@
1
+ module QualtricsAPI
2
+
3
+ class SurveyCollection
4
+ extend Forwardable
5
+ include Enumerable
6
+
7
+ attr_accessor :scope_id
8
+ attr_reader :all
9
+
10
+ def_delegator :all, :each
11
+ def_delegator :all, :size
12
+
13
+ def initialize(options = {})
14
+ @conn = options[:connection]
15
+ @scope_id = options[:scope_id]
16
+ @all = []
17
+ end
18
+
19
+ def fetch(options = {})
20
+ @all = []
21
+ update_query_attributes(options)
22
+ parse_fetch_response(@conn.get('surveys', query_params))
23
+ self
24
+ end
25
+
26
+ def query_attributes
27
+ {
28
+ :scope_id => @scope_id
29
+ }
30
+ end
31
+
32
+ def update_query_attributes(new_attributes = {})
33
+ @scope_id = new_attributes[:scope_id] if new_attributes.has_key? :scope_id
34
+ end
35
+
36
+ def [](survey_id); find(survey_id); end
37
+ def find(survey_id)
38
+ @all.select do |survey|
39
+ survey.id == survey_id
40
+ end.first || QualtricsAPI::Survey.new("id" => survey_id , connection: @conn)
41
+ end
42
+
43
+ private
44
+
45
+ def attributes_mapping
46
+ {
47
+ :scope_id => "scopeId"
48
+ }
49
+ end
50
+
51
+ def query_params
52
+ query_attributes.map do |k, v|
53
+ [attributes_mapping[k], v] unless v.nil? || v.to_s.empty?
54
+ end.compact.to_h
55
+ end
56
+
57
+ def parse_fetch_response(response)
58
+ @all = response.body["result"].map do |result|
59
+ QualtricsAPI::Survey.new result.merge(connection: @conn)
60
+ end
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,3 @@
1
+ module QualtricsAPI
2
+ URL = "https://co1.qualtrics.com:443/API/v1/"
3
+ end
@@ -0,0 +1,3 @@
1
+ module QualtricsAPI
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require 'qualtrics_api/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = "qualtrics_api"
9
+ spec.version = QualtricsAPI::VERSION
10
+ spec.authors = ["Yurui Zhang"]
11
+ spec.email = ["yuruiology@gmail.com"]
12
+ spec.summary = %q{A Ruby wrapper for Qualtrics REST API v3.0}
13
+ spec.description = %q{A Ruby wrapper for Qualtrics REST API version 3.0.
14
+ See https://co1.qualtrics.com/APIDocs/ for API documents.}
15
+ spec.homepage = ""
16
+ spec.license = "MIT"
17
+
18
+ spec.files = `git ls-files -z`.split("\x0")
19
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
20
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_dependency "faraday", "~> 0.9.1"
24
+ spec.add_dependency "faraday_middleware", "~> 0.9.1"
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.7"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rspec", "~> 3.2.0"
29
+ spec.add_development_dependency "vcr", "~> 2.9.3"
30
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+
3
+ describe QualtricsAPI::Client do
4
+
5
+ subject { QualtricsAPI::Client.new(:api_token => "someToken") }
6
+
7
+ it "has an api token" do
8
+ expect(subject.api_token).to eq "someToken"
9
+ end
10
+
11
+ it "does not allow changing the token once initialized" do
12
+ expect(subject).to_not respond_to(:api_token=)
13
+ end
14
+
15
+ describe "#response_exports" do
16
+ it "returns a ResponseExportCollection" do
17
+ expect(subject.response_exports).to be_a QualtricsAPI::ResponseExportCollection
18
+ end
19
+
20
+ it "sets connection" do
21
+ expect(subject.surveys.instance_variable_get(:@conn)).to eq subject.connection
22
+ end
23
+
24
+ it "caches the collection" do
25
+ expect(subject.response_exports.object_id).to eq subject.response_exports.object_id
26
+ end
27
+ end
28
+
29
+ describe "#surveys" do
30
+ it "returns a SurveyCollection" do
31
+ expect(subject.surveys).to be_a QualtricsAPI::SurveyCollection
32
+ end
33
+
34
+ it "sets connection" do
35
+ expect(subject.surveys.instance_variable_get(:@conn)).to eq subject.connection
36
+ end
37
+
38
+ it "assigns scope_id if passed" do
39
+ expect(subject.surveys(:scope_id => "someId").scope_id).to eq "someId"
40
+ end
41
+
42
+ it "caches the surveys" do
43
+ expect(subject.surveys.object_id).to eq subject.surveys.object_id
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe QualtricsAPI::ResponseExportCollection do
4
+ let(:connection) { double('connection') }
5
+
6
+ subject { described_class.new connection: connection }
7
+
8
+ it "has no @all when initialized" do
9
+ expect(subject.all).to eq []
10
+ end
11
+
12
+ it "takes a connection" do
13
+ expect(subject.instance_variable_get(:@conn)).to eq connection
14
+ end
15
+
16
+ describe "#find, #[]" do
17
+ let(:export_1) { QualtricsAPI::ResponseExport.new :id => "export1" }
18
+ let(:export_2) { QualtricsAPI::ResponseExport.new :id => "export2" }
19
+
20
+ it "finds the export by id" do
21
+ subject.instance_variable_set :@all, [export_1, export_2]
22
+ expect(subject.find("export1")).to eq export_1
23
+ expect(subject["export2"]).to eq export_2
24
+ end
25
+
26
+ it "returns a new survey with the id" do
27
+ sut = subject["eee 3"]
28
+ expect(sut).to be_a QualtricsAPI::ResponseExport
29
+ expect(sut.id).to eq "eee 3"
30
+ expect(sut.instance_variable_get(:@conn)).to eq connection
31
+ end
32
+ end
33
+ end