voicebase-client-ruby 1.0.14

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3dff5518b93777f4361ef5ae09534b24f26b81fd
4
+ data.tar.gz: a7b02a83c9ec5b4bca5da34213544f7049e45395
5
+ SHA512:
6
+ metadata.gz: 722c6172937548128010286413d6a684a1ab6507812a6213091246fe2af74d45fb5fe8eaf9468d4865e1e3510365eea5049931b90a91de21f1635dc4faf58c6f
7
+ data.tar.gz: 4472a71f64fd1bdd655c2396161da6b0afbfa0046cc6a03ad105a4ee99cbfe15ffd62fe67324d6f8b9ac88b0eab6cb3e6033c69de4c075d505221f0b11d72ed0
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.gem
11
+ /.idea/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ voicebase-client-ruby
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.4
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.4
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in voicebase-client-ruby.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,17 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) 2016 User Testing, Inc.
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software
5
+ and associated documentation files (the "Software"), to deal in the Software without restriction,
6
+ including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
7
+ and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all copies or substantial
11
+ portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
14
+ LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
15
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
16
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
17
+ OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,91 @@
1
+ # Voicebase Client Ruby
2
+
3
+ This is a Ruby client to the VoiceBase API Version [1.x](http://www.voicebase.com/developers/), see [API documentation](https://s3.amazonaws.com/vb-developers/VB-api-devguide-v1.1.5.pdf), and [2.x](https://apis.voicebase.com). Some portions of this gem were derived from [voicebase-client-ruby](https://github.com/popuparchive/voicebase-client-ruby).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'voicebase-client-ruby', github: "usertesting/voicebase-client-ruby"
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ ## Usage
18
+
19
+ ### VoiceBase API V1.x:
20
+
21
+ An example to authenticate with v1 and upload a video file.
22
+
23
+ ```ruby
24
+ require 'voicebase'
25
+
26
+ client = VoiceBase::Client.new({
27
+ api_version: "1.1",
28
+ auth_key: "my-voicebase-key",
29
+ auth_secret: "my-voicebase-secret",
30
+ })
31
+
32
+ client.upload_media({
33
+ media_url: "http://my.media-example.com/media1.mp4",
34
+ title: "My fancy media",
35
+ transcription_type: 'machine',
36
+ external_id: 'abcd1234',
37
+ machine_ready_callback: "http://my.example.com/success",
38
+ error_callback: "http://my.example.com/error"
39
+ })
40
+
41
+ response = get_transcript(external_id: 'abcd1234' format: "json")
42
+ if response.success?
43
+ transcript_json = JSON.parse(response.transcript)
44
+ end
45
+
46
+ ```
47
+
48
+ For VoiceBase API V2.x:
49
+
50
+ ```ruby
51
+ require 'voicebase'
52
+
53
+ client = VoiceBase::Client.new({
54
+ api_version: "2.0.beta",
55
+ auth_key: "my-voicebase-key",
56
+ auth_secret: "my-voicebase-secret",
57
+ })
58
+
59
+ client.upload_media({
60
+ media_url: "http://my.media-example.com/media1.mp4",
61
+ configuration: {
62
+ transcripts: {
63
+ engine: "premium"
64
+ },
65
+ publish: {
66
+ callbacks: [{
67
+ url: "https://example.org/callback",
68
+ method: "POST",
69
+ include: ["transcripts", "keywords", "topics", "metadata"]
70
+ }]
71
+ }
72
+ }
73
+ })
74
+
75
+ client.get_transcript({
76
+ media_id: "3b5c78e2-868c-4ce7-a0db-087a02db4042"
77
+ }, {'Accept' => 'text/srt'})
78
+
79
+ ...
80
+ ```
81
+
82
+ ## Development
83
+
84
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
85
+
86
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
87
+
88
+ ## Contributing
89
+
90
+ Bug reports and pull requests are welcome on GitHub at https://github.com/usertesting/voicebase-client-ruby.
91
+
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "voicebase/client/ruby"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/voicebase.rb ADDED
@@ -0,0 +1,22 @@
1
+ require 'json'
2
+ require 'uri'
3
+ require 'httparty'
4
+ require 'active_support/core_ext/module'
5
+
6
+ require "voicebase/version"
7
+ require "voicebase/helpers"
8
+
9
+ require "voicebase/v1"
10
+ require "voicebase/v2"
11
+
12
+ require "voicebase/client"
13
+ require "voicebase/client/token"
14
+ require "voicebase/response"
15
+
16
+ require "voicebase/json"
17
+ require "voicebase/json/word"
18
+
19
+ module VoiceBase
20
+ class AuthenticationError < StandardError; end
21
+ class ArgumentError < StandardError; end
22
+ end
@@ -0,0 +1,41 @@
1
+ module VoiceBase
2
+ class Client
3
+ include HTTParty
4
+
5
+ attr_accessor :args
6
+ attr_accessor :api_host
7
+ attr_accessor :api_endpoint
8
+ attr_accessor :api_version
9
+ attr_accessor :debug
10
+ attr_accessor :user_agent
11
+ attr_accessor :cookies
12
+ attr_accessor :locale
13
+ attr_accessor :token
14
+
15
+ # E.g. "request_status" -> "requestStatus"
16
+ def self.camelize_name(snake_cased_name)
17
+ snake_cased_name.to_s.camelize(:lower)
18
+ end
19
+
20
+ def initialize(args = {})
21
+ @args = args
22
+ @api_version = args[:api_version] || ENV.fetch('VOICEBASE_API_VERSION', '1.1')
23
+ @auth_key = args[:auth_key] || ENV['VOICEBASE_API_KEY']
24
+ @auth_secret = args[:auth_secret] || ENV['VOICEBASE_API_SECRET']
25
+ @debug = !!args[:debug]
26
+ @user_agent = args[:user_agent] || "usertesting-client/#{VoiceBase::version}"
27
+ @locale = args[:locale] || 'en' # US English
28
+
29
+ if @api_version.to_f < 2.0
30
+ self.extend(VoiceBase::V1::Client)
31
+ else
32
+ self.extend(VoiceBase::V2::Client)
33
+ end
34
+ end
35
+
36
+ def uri
37
+ @api_host + @api_endpoint
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,16 @@
1
+ module VoiceBase
2
+ class Client::Token
3
+ attr_accessor :token, :created_at, :timeout
4
+
5
+ def initialize(token, timeout = Float::INFINITY)
6
+ raise VoiceBase::AuthenticationError, "Authentication token cannot be empty" unless token
7
+ @token = token
8
+ @created_at = Time.now
9
+ @timeout = timeout
10
+ end
11
+
12
+ def expired?
13
+ Time.now > created_at + (timeout / 1000.to_f)
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ module VoiceBase
2
+ module Helpers
3
+ def self.included(base)
4
+ base.send :extend, ClassMethods
5
+ base.send :include, InstanceMethods
6
+ end
7
+
8
+ module ClassMethods
9
+
10
+ # E.g. "request_status" -> "requestStatus"
11
+ def camelize_name(snake_cased_name)
12
+ snake_cased_name.to_s.camelize(:lower)
13
+ end
14
+
15
+ end
16
+
17
+ module InstanceMethods
18
+
19
+ def camelize_name(snake_cased_name)
20
+ self.class.camelize_name(snake_cased_name)
21
+ end
22
+
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,89 @@
1
+ module VoiceBase
2
+ class JSON
3
+ include Enumerable
4
+
5
+ class ParseError < StandardError; end
6
+
7
+ class << self
8
+
9
+ def parse(input, options = {})
10
+ @debug = options.fetch(:debug, false)
11
+ if input.is_a?(String)
12
+ parse_string(input)
13
+ elsif input.is_a?(::File)
14
+ parse_file(input)
15
+ else
16
+ raise "Invalid input. Expected a String or File, got #{input.class.name}."
17
+ end
18
+ end
19
+
20
+ private
21
+
22
+ def parse_file(json_file)
23
+ parse_string ::File.open(json_file, 'rb') { |f| json_file.read }
24
+ end
25
+
26
+ def parse_string(json_string_data)
27
+ result = new
28
+
29
+ json_hash_data = ::JSON.parse(json_string_data)
30
+ raise ParseError, "Invalid format" unless json_hash_data.is_a?(Array)
31
+ json_hash_data.each_with_index do |word_hash, index|
32
+ word = Word.new(word_hash)
33
+ result.words << word unless word.empty?
34
+
35
+ %w(p c s e w).each do |field|
36
+ if word.send(field).nil?
37
+ word.error = "#{index}, Invalid formatting of #{field}, [#{word_hash[field]}]"
38
+ $stderr.puts word.error if @debug
39
+ end
40
+ end
41
+ end
42
+ result
43
+ end
44
+ end
45
+
46
+ attr_writer :words
47
+
48
+ def initialize(word_array = nil)
49
+ raise StandardError, "Must be initialized with words." if word_array.is_a?(Array) && !words.all? {|w| w.is_a?(VoiceBase::JSON::Word)}
50
+ @words = word_array || []
51
+ end
52
+
53
+ def words
54
+ @words ||= []
55
+ end
56
+
57
+ def errors
58
+ @words.map {|w| w.error if w.error}.compact
59
+ end
60
+
61
+ def each(&block)
62
+ @words.each {|word| block.call(word)}
63
+ end
64
+
65
+ def gt(start_time)
66
+ VoiceBase::JSON.new(select {|w| w.start_time > start_time})
67
+ end
68
+
69
+ def gteq(start_time)
70
+ VoiceBase::JSON.new(select {|w| w.start_time >= start_time})
71
+ end
72
+
73
+ def lt(start_time)
74
+ VoiceBase::JSON.new(select {|w| w.start_time < start_time})
75
+ end
76
+
77
+ def lteq(start_time)
78
+ VoiceBase::JSON.new(select {|w| w.start_time <= start_time})
79
+ end
80
+
81
+ def to_a
82
+ @words
83
+ end
84
+
85
+ def to_json
86
+ map {|w| w.to_hash}.to_json
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,63 @@
1
+ module VoiceBase
2
+ class JSON::Word
3
+ attr_accessor :sequence
4
+ attr_accessor :start_time
5
+ attr_accessor :end_time
6
+ attr_accessor :confidence
7
+ attr_accessor :word
8
+ attr_accessor :error
9
+ attr_accessor :metadata
10
+
11
+ alias_method :p, :sequence
12
+ alias_method :p=, :sequence=
13
+ alias_method :c, :confidence
14
+ alias_method :c=, :confidence=
15
+ alias_method :s, :start_time
16
+ alias_method :s=, :start_time=
17
+ alias_method :e, :end_time
18
+ alias_method :e=, :end_time=
19
+ alias_method :w, :word
20
+ alias_method :w=, :word=
21
+ alias_method :m, :metadata
22
+ alias_method :m=, :metadata=
23
+
24
+ def initialize(options={})
25
+ options.each do |k,v|
26
+ self.send("#{k}=",v)
27
+ end
28
+ end
29
+
30
+ def clone
31
+ clone = VoiceBase::JSON::Word.new
32
+ clone.sequence = sequence
33
+ clone.start_time = start_time
34
+ clone.end_time = end_time
35
+ clone.confidence = confidence
36
+ clone.error = error
37
+ clone.word = word
38
+ clone.metadata = metadata
39
+ clone
40
+ end
41
+
42
+ def ==(word)
43
+ self.sequence == word.sequence &&
44
+ self.start_time == word.start_time &&
45
+ self.end_time == word.end_time &&
46
+ self.confidence == word.confidence &&
47
+ self.word == word.word &&
48
+ self.metadata == word.metadata
49
+ end
50
+
51
+ def empty?
52
+ sequence.nil? && start_time.nil? && end_time.nil? && (word.nil? || word.empty?)
53
+ end
54
+
55
+ def to_hash
56
+ {"p": sequence, "c": confidence, "s": start_time, "e": end_time, "w": word}
57
+ end
58
+
59
+ def to_json
60
+ {"p": sequence, "c": confidence, "s": start_time, "e": end_time, "w": word}.to_json
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,35 @@
1
+ module VoiceBase
2
+ class Response
3
+ include Helpers
4
+ attr_accessor :http_response
5
+
6
+ delegate :code, :body, :headers, :message, to: :http_response, allow_nil: true
7
+
8
+ def initialize(http_response, api_version = "1.1")
9
+ @http_response = http_response
10
+ if api_version.to_f < 2
11
+ self.extend(VoiceBase::V1::Response)
12
+ else
13
+ self.extend(VoiceBase::V2::Response)
14
+ end
15
+ end
16
+
17
+ def ok?
18
+ http_response.code && http_response.code >= 200 && http_response.code < 300
19
+ end
20
+
21
+ # E.g.
22
+ #
23
+ # @response.request_status is derived from the
24
+ # response hash 'statusMessage' key, or
25
+ # @response.status_message from 'statusMessage'
26
+ #
27
+ def method_missing(method, *args, &block)
28
+ if result = http_response.parsed_response[camelize_name(method)]
29
+ result
30
+ else
31
+ super
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,6 @@
1
+ module VoiceBase
2
+ module V1; end
3
+ end
4
+
5
+ require "voicebase/v1/client"
6
+ require "voicebase/v1/response"
@@ -0,0 +1,92 @@
1
+ module VoiceBase
2
+ module V1
3
+ module Client
4
+ include Helpers
5
+
6
+ TOKEN_TIMEOUT_IN_MS = 1440
7
+ PARAM_NORMALIZATION = {"Url" => "URL", "Id" => "ID", "Callback" => "CallBack"}
8
+ ACTIONS = ['uploadMedia', 'getTranscript', 'deleteFile', 'getFileStatus']
9
+
10
+ def self.extended(client, args = {})
11
+ client.api_host = client.args[:api_host] || ENV.fetch('VOICEBASE_V1_API_HOST', 'https://api.voicebase.com')
12
+ client.api_endpoint = client.args[:api_endpoint] || ENV.fetch('VOICEBASE_V1_API_ENDPOINT', '/services')
13
+ end
14
+
15
+ def authenticate!
16
+ response = VoiceBase::Response.new(
17
+ self.class.post(uri,
18
+ query: {
19
+ version: @api_version, apiKey: @auth_key,
20
+ password: @auth_secret, action: 'getToken',
21
+ timeout: TOKEN_TIMEOUT_IN_MS
22
+ }), api_version)
23
+ @token = Token.new(response.token, TOKEN_TIMEOUT_IN_MS)
24
+ rescue NoMethodError => ex
25
+ raise VoiceBase::AuthenticationError, response.status_message
26
+ end
27
+
28
+ # E.g. @client.upload_media media_url: "https://ut.aws.amazon.com/..."
29
+ def method_missing(method, args, &block)
30
+ if actions.include?(camelize_name(method)) && args.size > 0
31
+ post camelize_keys(args).merge({action: camelize_name(method)})
32
+ else
33
+ super
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def post(query_params, headers = {})
40
+ query = default_query(query_params)
41
+
42
+ puts "post #{uri} #{query.inspect}, #{default_headers(headers).inspect}" if debug
43
+ VoiceBase::Response.new(self.class.post(uri,
44
+ query: query, headers: default_headers(headers)), api_version)
45
+ end
46
+
47
+ def actions
48
+ ACTIONS
49
+ end
50
+
51
+ def default_query(params = {})
52
+ params = params.reverse_merge({version: @api_version,
53
+ apiKey: @auth_key, password: @auth_secret,
54
+ lang: locale})
55
+
56
+ # authenticate using token or key/password?
57
+ if token && !token.expired?
58
+ params.merge!({token: token.token})
59
+ else
60
+ params.merge!({apiKey: @auth_key, password: @auth_secret})
61
+ end
62
+
63
+ params
64
+ end
65
+
66
+ def default_headers(headers = {})
67
+ {'User-Agent' => @user_agent, 'Accept' => 'application/json',
68
+ 'Cookie' => @cookies}.reject {|k, v| v.blank?}.merge(headers)
69
+ end
70
+
71
+ def camelize_keys(params)
72
+ params.inject({}) {|r, e| r[camelize_and_normalize_name(e.first)] = e.last; r }
73
+ end
74
+
75
+ # Parameters are camelized and normalized
76
+ # according to VoiceBase API.
77
+ #
78
+ # E.g.
79
+ #
80
+ # :media_url -> "mediaURL"
81
+ # :external_id -> "externalID"
82
+ # :error_callback -> "errorCallBack"
83
+ #
84
+ def camelize_and_normalize_name(snake_cased_name)
85
+ result = Client.camelize_name(snake_cased_name.to_s)
86
+ PARAM_NORMALIZATION.each {|k, v| result.gsub!(/#{k}/, v) }
87
+ result
88
+ end
89
+
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,24 @@
1
+ module VoiceBase
2
+ module V1
3
+ module Response
4
+ def self.extended(response)
5
+ end
6
+
7
+ def success?
8
+ ok? && request_status == "SUCCESS"
9
+ end
10
+
11
+
12
+ def transcript_ready?
13
+
14
+ # this was added because with the V1 API, a value in the returned JSON indicates both a
15
+ # successful HTTP request, but also a ready transcript. With V2, there's no JSON value
16
+ # to indicate status. Instead, the HTTP status code indicates request status, and
17
+ # the state becoming "finished" indicates the transcript it ready.
18
+
19
+ success?
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,6 @@
1
+ module VoiceBase
2
+ module V2; end
3
+ end
4
+
5
+ require "voicebase/v2/client"
6
+ require "voicebase/v2/response"
@@ -0,0 +1,233 @@
1
+ module VoiceBase
2
+ module V2
3
+ module Client
4
+ BOUNDARY = "0123456789ABLEWASIEREISAWELBA9876543210"
5
+ MULTIPART_CONTENT_TYPE = "multipart/form-data; boundary=#{BOUNDARY}"
6
+
7
+ def self.extended(client, args = {})
8
+ client.api_host = client.args[:host] || ENV.fetch('VOICEBASE_V2_API_HOST', 'https://apis.voicebase.com')
9
+ client.api_endpoint = client.args[:api_endpoint] || ENV.fetch('VOICEBASE_V2_API_ENDPOINT', '/v2-beta')
10
+ end
11
+
12
+ def authenticate!
13
+ auth = {:username => @auth_key, :password => @auth_secret}
14
+ response = VoiceBase::Response.new(
15
+ self.class.get(
16
+ uri + '/access/users/admin/tokens',
17
+ basic_auth: auth,
18
+ headers: {
19
+ 'User-Agent' => @user_agent,
20
+ 'Accept' => 'application/json'
21
+ }
22
+ ), api_version)
23
+ @token = VoiceBase::Client::Token.new(response.tokens.try(:first).try(:[], 'token'))
24
+ rescue NoMethodError => ex
25
+ raise VoiceBase::AuthenticationError, response.status_message
26
+ end
27
+
28
+ def upload_media(args = {}, headers = {})
29
+
30
+ media_url = require_media_file_or_url(args)
31
+
32
+ form_args = {
33
+ 'media' => media_url,
34
+ 'configuration' => {
35
+ 'configuration' => {
36
+ 'executor' => 'v2'
37
+ }
38
+ }
39
+ }
40
+
41
+ # external ID is only partially supported in the V2 API (can't get plain text transcripts or delete media)
42
+ if args[:external_id]
43
+ form_args.merge!({
44
+ 'metadata' => {
45
+ 'metadata' => {
46
+ 'external' => {
47
+ 'id' => "#{args[:external_id]}"
48
+ }
49
+ }
50
+ }
51
+ })
52
+ end
53
+
54
+ response = self.class.post(
55
+ uri + '/media',
56
+ headers: multipart_headers(headers),
57
+ body: multipart_query(form_args)
58
+ )
59
+
60
+ VoiceBase::Response.new(response, api_version)
61
+ end
62
+
63
+ # I presume this method exists for parity with the V1 API however it is not used by the Orders app
64
+ def get_media(args = {}, headers = {})
65
+ raise ArgumentError, "Missing argument :media_id" unless args[:media_id]
66
+ url = if args[:media_id]
67
+ uri + "/media/#{args[:media_id]}"
68
+ elsif args[:external_id]
69
+ uri + "/media?externalID=#{args[:external_id]}"
70
+ else
71
+ raise ArgumentError, "Missing argument :media_url or :media_file"
72
+ end
73
+ if args[:external_id]
74
+ uri + "/media?externalID=#{args[:external_id]}"
75
+ else
76
+ raise ArgumentError, "Missing argument :external_id"
77
+ end
78
+
79
+ VoiceBase::Response.new(self.class.get(
80
+ url, headers: default_headers(headers)
81
+ ), api_version)
82
+ end
83
+
84
+ def get_json_transcript(args, headers)
85
+ url = if args[:media_id]
86
+ uri + "/media/#{args[:media_id]}"
87
+ else
88
+ raise ArgumentError, "Missing argument :media_id"
89
+ end
90
+
91
+ response = self.class.get(
92
+ url,
93
+ headers: default_headers(headers)
94
+ )
95
+
96
+ VoiceBase::Response.new(response, api_version)
97
+ end
98
+
99
+ def get_text_transcript(args, headers)
100
+ url = if args[:media_id]
101
+ uri + "/media/#{args[:media_id]}/transcripts/latest"
102
+ else
103
+ raise ArgumentError, "Missing argument :media_id"
104
+ end
105
+
106
+ headers.merge!({ 'Accept' => 'text/plain' })
107
+
108
+ response = self.class.get(
109
+ url,
110
+ headers: default_headers(headers)
111
+ )
112
+
113
+ response.parsed_response
114
+ end
115
+
116
+ def get_transcript(args = {}, headers = {})
117
+ if args[:format] == "txt"
118
+ get_text_transcript(args, headers)
119
+ else
120
+ get_json_transcript(args, headers)
121
+ end
122
+ end
123
+
124
+ # is this used?
125
+ def get_media_progress(args = {}, headers = {})
126
+ raise ArgumentError, "Missing argument :media_id" unless args[:media_id]
127
+ VoiceBase::Response.new(self.class.get(
128
+ uri + "/media/#{args[:media_id]}/progress",
129
+ headers: default_headers(headers)
130
+ ), api_version)
131
+ end
132
+
133
+ def delete_file(args = {}, headers = {})
134
+ url = if args[:media_id]
135
+ uri + "/media/#{args[:media_id]}"
136
+ else
137
+ raise ArgumentError, "Missing argument :media_id"
138
+ end
139
+
140
+ response = self.class.delete(
141
+ url,
142
+ headers: default_headers(headers)
143
+ )
144
+
145
+ VoiceBase::Response.new(response, api_version)
146
+ end
147
+
148
+ private
149
+
150
+ def default_headers(headers = {})
151
+ authenticate! unless token
152
+ headers = {'Authorization' => "Bearer #{token.token}",
153
+ 'User-Agent' => user_agent}.reject {|k, v| v.blank?}.merge(headers)
154
+ puts "> headers\n> #{headers}" if debug
155
+ headers
156
+ end
157
+
158
+ def multipart_headers(headers = {})
159
+ default_headers(headers.merge({'Content-Type' => MULTIPART_CONTENT_TYPE}))
160
+ end
161
+
162
+ def multipart_query(params)
163
+ fp = []
164
+
165
+ params.each do |k, v|
166
+ if v.respond_to?(:path) and v.respond_to?(:read) then
167
+ fp.push(FileParam.new(k, v.path, v.read))
168
+ elsif v.is_a?(Hash)
169
+ fp.push(HashParam.new(k, v))
170
+ else
171
+ fp.push(StringParam.new(k, v))
172
+ end
173
+ end
174
+
175
+ query = fp.map {|p| "--" + BOUNDARY + "\r\n" + p.to_multipart }.join("") + "--" + BOUNDARY + "--"
176
+ puts "> multipart-query\n> #{query}" if debug
177
+ query
178
+ end
179
+
180
+ def require_media_file_or_url(args = {})
181
+ media = if args[:media_url]
182
+ args[:media_url]
183
+ elsif args[:media_file]
184
+ args[:media_file]
185
+ else
186
+ raise ArgumentError, "Missing argument :media_url or :media_file"
187
+ end
188
+ end
189
+
190
+ class StringParam
191
+ attr_accessor :k, :v
192
+
193
+ def initialize(k, v)
194
+ @k, @v = k, v
195
+ end
196
+
197
+ def to_multipart
198
+ return "Content-Disposition: form-data; name=\"#{CGI::escape(k.to_s)}\"\r\n\r\n#{v}\r\n"
199
+ end
200
+ end
201
+
202
+ class HashParam
203
+ attr_accessor :k, :v
204
+
205
+ def initialize(k, v)
206
+ @k, @v = k, v
207
+ end
208
+
209
+ def to_multipart
210
+ return "Content-Disposition: form-data; name=\"#{CGI::escape(k.to_s)}\"\r\n\r\n#{v.to_json}\r\n"
211
+ end
212
+ end
213
+
214
+ # Formats the contents of a file or string for inclusion with a multipart
215
+ # form post
216
+ class FileParam
217
+ attr_accessor :k, :filename, :content
218
+
219
+ def initialize(k, filename, content)
220
+ @k, @filename, @content = k, filename, content
221
+ end
222
+
223
+ def to_multipart
224
+ # If we can tell the possible mime-type from the filename, use the
225
+ # first in the list; otherwise, use "application/octet-stream"
226
+ mime_type = MIME::Types.type_for(filename)[0] || MIME::Types["application/octet-stream"][0]
227
+ return "Content-Disposition: form-data; name=\"#{CGI::escape(k.to_s)}\"; filename=\"#{ filename }\"\r\n" +
228
+ "Content-Type: #{ mime_type.simplified }\r\n\r\n#{ content }\r\n"
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,38 @@
1
+ module VoiceBase
2
+ module V2
3
+ module Response
4
+
5
+ TRANSCRIPT_READY_STATUS = "finished".freeze
6
+
7
+ def success?
8
+
9
+ # for the V1 API this indicates both a successful HTTP status code and a values of "SUCCESS" in the
10
+ # returned JSON. with V2, there is no "SUCCESS" value. The combined use was split, adding
11
+ # #transcript_ready? to both interfaces.
12
+
13
+ ok?
14
+ end
15
+
16
+ def media_id
17
+ voicebase_response['mediaId']
18
+ end
19
+
20
+ def transcript_ready?
21
+ voicebase_response['media']['status'].casecmp(TRANSCRIPT_READY_STATUS) == 0
22
+ end
23
+
24
+ def transcript
25
+ # this retrieves the JSON transcript only
26
+ # the plain text transcript is a plain text non-JSON response
27
+ voicebase_response['media']['transcripts']['latest']['words']
28
+ end
29
+
30
+ private
31
+
32
+ def voicebase_response
33
+ http_response.parsed_response
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,5 @@
1
+ module VoiceBase
2
+ VERSION = "1.0.14"
3
+
4
+ def self.version; VERSION; end
5
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'voicebase/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "voicebase-client-ruby"
8
+ spec.version = VoiceBase::version
9
+ spec.authors = ["Jerry Hogsett"]
10
+ spec.email = ["jerry@usertesting.com"]
11
+
12
+ spec.summary = %q{Ruby client for VoiceBase API Version 1.x and 2.x.}
13
+ spec.description = %q{Ruby client for VoiceBase API Version 1.x and 2.x that will make both API versions available at the same time.}
14
+ spec.homepage = "https://github.com/usertesting/voicebase-client-ruby"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.11"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec", "~> 3.0"
25
+ spec.add_development_dependency "timecop", "~> 0.8"
26
+
27
+ spec.add_dependency "httparty"
28
+ spec.add_dependency "activesupport"
29
+ end
metadata ADDED
@@ -0,0 +1,155 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: voicebase-client-ruby
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.14
5
+ platform: ruby
6
+ authors:
7
+ - Jerry Hogsett
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-05-04 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: timecop
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.8'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.8'
69
+ - !ruby/object:Gem::Dependency
70
+ name: httparty
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: activesupport
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Ruby client for VoiceBase API Version 1.x and 2.x that will make both
98
+ API versions available at the same time.
99
+ email:
100
+ - jerry@usertesting.com
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - ".rspec"
107
+ - ".ruby-gemset"
108
+ - ".ruby-version"
109
+ - ".travis.yml"
110
+ - Gemfile
111
+ - LICENSE
112
+ - README.md
113
+ - Rakefile
114
+ - bin/console
115
+ - bin/setup
116
+ - lib/voicebase.rb
117
+ - lib/voicebase/client.rb
118
+ - lib/voicebase/client/token.rb
119
+ - lib/voicebase/helpers.rb
120
+ - lib/voicebase/json.rb
121
+ - lib/voicebase/json/word.rb
122
+ - lib/voicebase/response.rb
123
+ - lib/voicebase/v1.rb
124
+ - lib/voicebase/v1/client.rb
125
+ - lib/voicebase/v1/response.rb
126
+ - lib/voicebase/v2.rb
127
+ - lib/voicebase/v2/client.rb
128
+ - lib/voicebase/v2/response.rb
129
+ - lib/voicebase/version.rb
130
+ - voicebase-client-ruby.gemspec
131
+ homepage: https://github.com/usertesting/voicebase-client-ruby
132
+ licenses:
133
+ - MIT
134
+ metadata: {}
135
+ post_install_message:
136
+ rdoc_options: []
137
+ require_paths:
138
+ - lib
139
+ required_ruby_version: !ruby/object:Gem::Requirement
140
+ requirements:
141
+ - - ">="
142
+ - !ruby/object:Gem::Version
143
+ version: '0'
144
+ required_rubygems_version: !ruby/object:Gem::Requirement
145
+ requirements:
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: '0'
149
+ requirements: []
150
+ rubyforge_project:
151
+ rubygems_version: 2.4.8
152
+ signing_key:
153
+ specification_version: 4
154
+ summary: Ruby client for VoiceBase API Version 1.x and 2.x.
155
+ test_files: []