patient_zero 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/lib/patient_zero/analytics/base.rb +35 -0
  4. data/lib/patient_zero/analytics/facebook.rb +25 -0
  5. data/lib/patient_zero/analytics/instagram.rb +13 -0
  6. data/lib/patient_zero/analytics/twitter.rb +13 -0
  7. data/lib/patient_zero/analytics.rb +16 -0
  8. data/lib/patient_zero/authorization.rb +8 -0
  9. data/lib/patient_zero/client.rb +35 -0
  10. data/lib/patient_zero/configurable.rb +21 -0
  11. data/lib/patient_zero/errors.rb +4 -0
  12. data/lib/patient_zero/message/base.rb +11 -0
  13. data/lib/patient_zero/message/facebook.rb +29 -0
  14. data/lib/patient_zero/message/instagram.rb +29 -0
  15. data/lib/patient_zero/message/twitter.rb +21 -0
  16. data/lib/patient_zero/message.rb +16 -0
  17. data/lib/patient_zero/organization.rb +27 -0
  18. data/lib/patient_zero/source.rb +36 -0
  19. data/lib/patient_zero/version.rb +1 -1
  20. data/lib/patient_zero.rb +19 -2
  21. data/patient_zero.gemspec +13 -8
  22. data/spec/patient_zero/analytics/base_spec.rb +54 -0
  23. data/spec/patient_zero/analytics/facebook_spec.rb +40 -0
  24. data/spec/patient_zero/analytics/instagram_spec.rb +35 -0
  25. data/spec/patient_zero/analytics/twitter_spec.rb +34 -0
  26. data/spec/patient_zero/analytics_spec.rb +29 -0
  27. data/spec/patient_zero/authorization_spec.rb +26 -0
  28. data/spec/patient_zero/client_spec.rb +31 -0
  29. data/spec/patient_zero/message/facebook_spec.rb +52 -0
  30. data/spec/patient_zero/message/instagram_spec.rb +53 -0
  31. data/spec/patient_zero/message/twitter_spec.rb +40 -0
  32. data/spec/patient_zero/message_spec.rb +29 -0
  33. data/spec/patient_zero/organization_spec.rb +67 -0
  34. data/spec/patient_zero/source_spec.rb +83 -0
  35. data/spec/spec_helper.rb +14 -0
  36. metadata +106 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1a4a9252ac8b5ea5e0e30a0ecad9474f702d1f8d
4
- data.tar.gz: 7c6721785383d71cc9c733024d654e847fd30e91
3
+ metadata.gz: 44dbcc4b6d6cf236eb77f548a0b764e7816b7962
4
+ data.tar.gz: 33e6b2e9170ebe01eb61ddb7bb3ddd61661e4e16
5
5
  SHA512:
6
- metadata.gz: d0fbf5e3550e1284a63ed5654814447f12e3766d80950184c508699cfea8b98d89dc790565eeba83931bb64791b9523924e9c74b5f53304d0897ce6dba827ace
7
- data.tar.gz: 142e969fbc6b4017a9ee83cc9349eaafc6843af035c9fc9a99aa5e1198aba7ea27f7380063adb0c955ecf5b8b9168befa7ba490b3e146732e078402d994707ab
6
+ metadata.gz: 7129206bd4afe74fdb1708a02d6fcfc8040845cd04fec49b67916f8aa928193b27976905a24f0db7dd784d9195c2bc9099c8ce278ffd2cc3897c332bd88d76fe
7
+ data.tar.gz: 10c63b464b5426341cd6393be167ff564ff5dc99a5356fe1493fcd6cc435e52b3fbbe8587ff12761b91437a8b0c7a881c33cd37cb477c51e4809c9d44336f3f2
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --require 'spec_helper'
2
+ --color true
@@ -0,0 +1,35 @@
1
+ module PatientZero
2
+ module Analytics
3
+ class Base < Client
4
+ attr_accessor :token, :source_id, :start_date, :end_date
5
+
6
+ def initialize token:, source_id:, start_date: nil, end_date: nil
7
+ @token = token
8
+ @source_id = source_id
9
+ @end_date = end_date || Date.today.to_s
10
+ @start_date = start_date || Date.today.prev_day(7).to_s
11
+ end
12
+
13
+ def name
14
+ analytical_data['name']
15
+ end
16
+
17
+ def platform
18
+ analytical_data['platform']
19
+ end
20
+
21
+ def messages
22
+ analytical_data['messages'].map { |message| Message.for_platform message['platform'], message }
23
+ end
24
+
25
+ private
26
+
27
+ def analytical_data
28
+ @analytical_data ||= get('/mobile/api/v1/analytics', client_token: token,
29
+ social_object_uid: source_id,
30
+ start_date: start_date,
31
+ end_date: end_date)['stats'].first
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,25 @@
1
+ module PatientZero
2
+ module Analytics
3
+ class Facebook < Base
4
+ def engagements
5
+ @engagements ||= messages.reduce(0) { |sum, message| sum + message.engagements }
6
+ end
7
+
8
+ def impressions
9
+ page_impressions + message_impressions
10
+ end
11
+
12
+ private
13
+
14
+ def message_impressions
15
+ messages.reduce(0) { |sum, message| sum + message.impressions }
16
+ end
17
+
18
+ def page_impressions
19
+ analytical_data['page_impressions'].find do |impressions_hash|
20
+ impressions_hash['key'] == 'Total'
21
+ end['values'].each_value.reduce(:+)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,13 @@
1
+ module PatientZero
2
+ module Analytics
3
+ class Instagram < Base
4
+ def engagements
5
+ @engagements ||= messages.reduce(0) { |sum, message| sum + message.engagements }
6
+ end
7
+
8
+ def impressions
9
+ @impressions ||= messages.reduce(0) { |sum, message| sum + message.impressions }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ module PatientZero
2
+ module Analytics
3
+ class Twitter < Base
4
+ def impressions
5
+ analytical_data['total_impressions']
6
+ end
7
+
8
+ def engagements
9
+ @engagements ||= messages.reduce(0) { |sum, message| sum + message.engagements }
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ require 'patient_zero/analytics/base'
2
+ require 'patient_zero/analytics/twitter'
3
+ require 'patient_zero/analytics/facebook'
4
+ require 'patient_zero/analytics/instagram'
5
+
6
+ module PatientZero
7
+ module Analytics
8
+ SOURCE_TYPES = {'twitter' => Twitter,
9
+ 'facebook' => Facebook,
10
+ 'instagram' => Instagram}
11
+
12
+ def self.for_platform platform, params={}
13
+ SOURCE_TYPES[platform].new params
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,8 @@
1
+ module PatientZero
2
+ class Authorization < Client
3
+ def self.token
4
+ response = post '/mobile/api/v1/user/login', email: PatientZero.email, password: PatientZero.password
5
+ response['user_token']
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,35 @@
1
+ module PatientZero
2
+ class Client
3
+ def self.connection
4
+ @@connection ||= Faraday.new(PatientZero.url) do |faraday|
5
+ faraday.request :url_encoded
6
+ faraday.response :logger
7
+ faraday.adapter Faraday.default_adapter
8
+ end
9
+ end
10
+
11
+ def self.parse request
12
+ response = JSON.parse request.body
13
+ raise Error, response['error'] unless response['error'].nil?
14
+ response
15
+ end
16
+
17
+ def self.get *args
18
+ parse connection.get *args
19
+ end
20
+
21
+ def self.post *args
22
+ parse connection.post *args
23
+ end
24
+
25
+ private
26
+
27
+ def get *args
28
+ self.class.get *args
29
+ end
30
+
31
+ def post *args
32
+ self.class.post *args
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,21 @@
1
+ module PatientZero
2
+ module Configurable
3
+ attr_accessor :url, :api_key, :email, :password
4
+
5
+ def self.extended(base)
6
+ base.reset
7
+ end
8
+
9
+ def configure
10
+ yield self
11
+ self
12
+ end
13
+
14
+ def reset
15
+ self.url = 'https://app.viralheat.com'
16
+ self.api_key = ENV['VIRAL_HEAT_API_KEY']
17
+ self.email = ENV['VIRAL_HEAT_EMAIL']
18
+ self.password = ENV['VIRAL_HEAT_PASSWORD']
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,4 @@
1
+ module PatientZero
2
+ class Error < StandardError; end
3
+ class NotFoundError < Error; end
4
+ end
@@ -0,0 +1,11 @@
1
+ module PatientZero
2
+ module Message
3
+ class Base
4
+ attr_reader :data
5
+
6
+ def initialize data
7
+ @data = data
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+ module PatientZero
2
+ module Message
3
+ class Facebook < Base
4
+ def engagements
5
+ @engagements ||= likes + comments + shares + clicks
6
+ end
7
+
8
+ def impressions
9
+ data.fetch 'impressions'
10
+ end
11
+
12
+ def likes
13
+ data.fetch 'likes'
14
+ end
15
+
16
+ def comments
17
+ data.fetch 'comments'
18
+ end
19
+
20
+ def shares
21
+ data.fetch 'shares'
22
+ end
23
+
24
+ def clicks
25
+ data.fetch 'clicks'
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,29 @@
1
+ module PatientZero
2
+ module Message
3
+ class Instagram < Base
4
+ def engagements
5
+ @engagements ||= likes + comments + clicks + shares
6
+ end
7
+
8
+ def impressions
9
+ data.fetch 'impressions'
10
+ end
11
+
12
+ def likes
13
+ data.fetch 'likes'
14
+ end
15
+
16
+ def comments
17
+ data.fetch 'comments'
18
+ end
19
+
20
+ def clicks
21
+ data.fetch 'clicks'
22
+ end
23
+
24
+ def shares
25
+ data.fetch('shares').to_i
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ module PatientZero
2
+ module Message
3
+ class Twitter < Base
4
+ def engagements
5
+ @engagements ||= retweets + favorites + clicks
6
+ end
7
+
8
+ def retweets
9
+ data.fetch 'retweets'
10
+ end
11
+
12
+ def favorites
13
+ data.fetch 'favorites'
14
+ end
15
+
16
+ def clicks
17
+ data.fetch 'clicks'
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ require 'patient_zero/message/base'
2
+ require 'patient_zero/message/facebook'
3
+ require 'patient_zero/message/twitter'
4
+ require 'patient_zero/message/instagram'
5
+
6
+ module PatientZero
7
+ module Message
8
+ SOURCE_TYPES = {'TW' => Twitter,
9
+ 'FB' => Facebook,
10
+ 'IG' => Instagram}
11
+
12
+ def self.for_platform platform, params={}
13
+ SOURCE_TYPES[platform].new params
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ module PatientZero
2
+ class Organization < Client
3
+ attr_accessor :id, :name, :avatar
4
+
5
+ def initialize attributes
6
+ @id = attributes.fetch 'id'
7
+ @name = attributes.fetch 'name'
8
+ @avatar = attributes.fetch 'avatar'
9
+ end
10
+
11
+ def self.all
12
+ response = get '/mobile/api/v1/organizations', client_token: Authorization.token
13
+ response['organizations'].map do |organization_attributes|
14
+ new organization_attributes
15
+ end
16
+ end
17
+
18
+ def sources
19
+ Source.all token
20
+ end
21
+
22
+ def token
23
+ response = get "/mobile/api/v1/organizations/#{id}/switch", client_token: Authorization.token
24
+ response['user_token']
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ module PatientZero
2
+ class Source < Client
3
+ attr_accessor :id, :name, :platform, :token
4
+
5
+ def initialize attributes, token
6
+ @id = attributes.fetch 'id'
7
+ @name = attributes.fetch 'name'
8
+ @invalid = attributes.fetch 'is_invalid'
9
+ @tracked = attributes.fetch 'is_tracked'
10
+ @platform = attributes.fetch 'platform'
11
+ @token = token
12
+ end
13
+
14
+ def self.all token=Authorization.token
15
+ response = get '/mobile/api/v1/sources/', client_token: token
16
+ response['sources'].map do |source_attributes|
17
+ new source_attributes, token
18
+ end
19
+ end
20
+
21
+ def self.find source_id, token=Authorization.token
22
+ response = get '/mobile/api/v1/sources/show/', id: source_id, client_token: token
23
+ new response['source'], token
24
+ rescue Error => e
25
+ raise NotFoundError, e
26
+ end
27
+
28
+ def profile_id
29
+ id.split('#').last
30
+ end
31
+
32
+ def analytics start_date: nil, end_date: nil
33
+ @analytics ||= Analytics.for_platform platform, token: token, source_id: id, start_date: start_date, end_date: end_date
34
+ end
35
+ end
36
+ end
@@ -1,3 +1,3 @@
1
1
  module PatientZero
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/patient_zero.rb CHANGED
@@ -1,5 +1,22 @@
1
- require "patient_zero/version"
1
+ require 'json'
2
+ require 'faraday'
3
+ require 'patient_zero/version'
4
+ require 'patient_zero/errors'
5
+ require 'patient_zero/client'
6
+ require 'patient_zero/configurable'
7
+ require 'patient_zero/authorization'
8
+ require 'patient_zero/organization'
9
+ require 'patient_zero/analytics'
10
+ require 'patient_zero/source'
11
+ require 'patient_zero/message'
2
12
 
3
13
  module PatientZero
4
- # Your code goes here...
14
+ extend PatientZero::Configurable
5
15
  end
16
+
17
+ # Configuration Example
18
+ # PatientZero.configure do |config|
19
+ # config.api_key = 'somethingsomethingdangerzone'
20
+ # config.email = 'duchess@cia.gov'
21
+ # config.password = 'password'
22
+ # end
data/patient_zero.gemspec CHANGED
@@ -4,20 +4,25 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'patient_zero/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "patient_zero"
7
+ spec.name = 'patient_zero'
8
8
  spec.version = PatientZero::VERSION
9
- spec.authors = ["Adam Zaninovich"]
10
- spec.email = ["adam.zaninovich@gmail.com"]
9
+ spec.authors = ['Adam Zaninovich','Devin Clark']
10
+ spec.email = ['adam.zaninovich@gmail.com', 'notdevinclark@gmail.com']
11
11
  spec.summary = %q{A gem to use the Viral Heat API}
12
12
  spec.description = %q{A gem to use the Viral Heat API}
13
- spec.homepage = ""
14
- spec.license = "MIT"
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0")
17
17
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
19
+ spec.require_paths = ['lib']
20
20
 
21
- spec.add_development_dependency "bundler", "~> 1.7"
22
- spec.add_development_dependency "rake", "~> 10.0"
21
+ spec.add_development_dependency 'bundler', '~> 1.7'
22
+ spec.add_development_dependency 'rake', '~> 10.0'
23
+ spec.add_development_dependency 'rspec'
24
+ spec.add_development_dependency 'pry'
25
+
26
+ spec.add_dependency 'faraday'
27
+ spec.add_dependency 'json'
23
28
  end
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ module Analytics
5
+ describe Base do
6
+ let(:source_id) { "12345##{platform}#1234567890" }
7
+ let(:name) { 'account_name' }
8
+ let(:platform) { 'account_type' }
9
+ let(:messages) { [ { 'platform' => 'FB' } ] }
10
+ let(:token) { 'token-shmoken' }
11
+ let(:analytical_data) { { 'stats' => [ { 'id' => source_id, 'platform' => platform, 'name' => name, 'messages' => messages } ] } }
12
+ subject(:analytics_base) { Base.new token: token, source_id: source_id }
13
+ before { allow(Client).to receive(:get).with('/mobile/api/v1/analytics', anything).and_return analytical_data }
14
+
15
+ describe '#name' do
16
+ it 'calls the analytics API endpoint' do
17
+ expect(Client).to receive(:get).with('/mobile/api/v1/analytics', anything)
18
+ analytics_base.name
19
+ end
20
+ it 'pulls the name from the analytical_data hash' do
21
+ expect(analytics_base.name).to be name
22
+ end
23
+ end
24
+
25
+ describe '#platform' do
26
+ it 'calls the analytics API endpoint' do
27
+ expect(Client).to receive(:get).with('/mobile/api/v1/analytics', anything)
28
+ analytics_base.name
29
+ end
30
+ it 'pulls the platform from the analytical_data hash' do
31
+ expect(analytics_base.platform).to be platform
32
+ end
33
+ end
34
+
35
+ describe '#messages' do
36
+ it 'calls the analytics API endpoint' do
37
+ expect(Client).to receive(:get).with('/mobile/api/v1/analytics', anything)
38
+ analytics_base.name
39
+ end
40
+ context 'when there are messages' do
41
+ it 'returns an array of messages' do
42
+ expect(analytics_base.messages.first).to be_a Message::Facebook
43
+ end
44
+ end
45
+ context 'when there are no messages' do
46
+ let(:messages) { [] }
47
+ it 'returns an empty array' do
48
+ expect(analytics_base.messages).to be_empty
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ module Analytics
5
+ describe Facebook do
6
+ let(:source_id) { "12345##{platform}#1234567890" }
7
+ let(:platform) { 'facebook' }
8
+ let(:message) do
9
+ { 'platform' => 'FB',
10
+ 'likes' => 93,
11
+ 'comments' => 7,
12
+ 'impressions' => 345,
13
+ 'shares' => 22,
14
+ 'clicks' => 38 }
15
+ end
16
+ let(:messages) { [ message ] }
17
+ let(:page_impressions) do
18
+ [ { 'key'=>'Total',
19
+ 'values'=>
20
+ { '2010-10-10'=> 99 } } ]
21
+ end
22
+ let(:token) { 'token-shmoken' }
23
+ let(:analytical_data) { { 'messages' => messages, 'page_impressions' => page_impressions } }
24
+ let(:facebook_analytics) { Facebook.new token: token, source_id: source_id }
25
+ before{ allow(facebook_analytics).to receive(:analytical_data).and_return analytical_data }
26
+
27
+ describe '#impressions' do
28
+ it 'returns the total for message and page impressions' do
29
+ expect(facebook_analytics.impressions).to eq 444
30
+ end
31
+ end
32
+
33
+ describe '#engagements' do
34
+ it 'returns the sum of likes, comments, shares, and clicks' do
35
+ expect(facebook_analytics.engagements).to eq 160
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ module Analytics
5
+ describe Instagram do
6
+ let(:source_id) { "12345##{platform}#1234567890" }
7
+ let(:platform) { 'instagram' }
8
+ let(:message) do
9
+ { 'platform' => 'IG',
10
+ 'impressions' => 10101,
11
+ 'shares'=>'3',
12
+ 'likes'=>52,
13
+ 'comments'=>3,
14
+ 'clicks'=>14 }
15
+ end
16
+ let(:messages) { [ message ] }
17
+ let(:token) { 'token-shmoken' }
18
+ let(:analytical_data) { { 'messages' => messages } }
19
+ let(:instagram_analytics) { Instagram.new token: token, source_id: source_id }
20
+ before{ allow(instagram_analytics).to receive(:analytical_data).and_return analytical_data }
21
+
22
+ describe '#impressions' do
23
+ it 'returns the sum of impressions for all messages' do
24
+ expect(instagram_analytics.impressions).to eq 10101
25
+ end
26
+ end
27
+
28
+ describe '#engagements' do
29
+ it 'returns the sum of retweets, favorites, and clicks for all messages' do
30
+ expect(instagram_analytics.engagements).to eq 72
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ module Analytics
5
+ describe Twitter do
6
+ let(:source_id) { "12345##{platform}#1234567890" }
7
+ let(:platform) { 'twitter' }
8
+ let(:message) do
9
+ { 'platform' => 'TW',
10
+ 'retweets' => 6,
11
+ 'impressions' => 13,
12
+ 'favorites' => 2,
13
+ 'clicks' => 12 }
14
+ end
15
+ let(:messages) { [ message ] }
16
+ let(:token) { 'token-shmoken' }
17
+ let(:analytical_data) { { 'messages' => messages, 'total_impressions' => 13 } }
18
+ let(:twitter_analytics) { Twitter.new token: token, source_id: source_id }
19
+ before{ allow(twitter_analytics).to receive(:analytical_data).and_return analytical_data }
20
+
21
+ describe '#impressions' do
22
+ it 'returns the total_impressions value from the analytical_data hash' do
23
+ expect(twitter_analytics.impressions).to eq 13
24
+ end
25
+ end
26
+
27
+ describe '#engagements' do
28
+ it 'returns the sum of retweets, favorites, and clicks for all messages' do
29
+ expect(twitter_analytics.engagements).to eq 20
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ describe Analytics do
5
+ describe '.for_platform' do
6
+ let(:data_hash) { { token: "", source_id: "" } }
7
+ context 'when platform is Facebook' do
8
+ let(:platform) { 'facebook' }
9
+ it 'news up a Analytics::Facebook object' do
10
+ expect(Analytics.for_platform(platform, data_hash)).to be_a Analytics::Facebook
11
+ end
12
+ end
13
+
14
+ context 'when platform is Twitter' do
15
+ let(:platform) { 'twitter' }
16
+ it 'news up a Analytics::Twitter object' do
17
+ expect(Analytics.for_platform(platform, data_hash)).to be_a Analytics::Twitter
18
+ end
19
+ end
20
+
21
+ context 'when platform is Instagram' do
22
+ let(:platform) { 'instagram' }
23
+ it 'news up a Analytics::Instagram object' do
24
+ expect(Analytics.for_platform(platform, data_hash)).to be_a Analytics::Instagram
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ describe Authorization do
5
+ describe '.token' do
6
+ let(:token) { 'token-shmoken' }
7
+ let(:authorization_response) { response_with_body user_token: token }
8
+ before{ allow(Client.connection).to receive(:post).with('/mobile/api/v1/user/login', anything).and_return authorization_response }
9
+ it 'calls the login api endpoint' do
10
+ expect(Client.connection).to receive(:post).with('/mobile/api/v1/user/login', anything)
11
+ Authorization.token
12
+ end
13
+ context 'when email and password are correct' do
14
+ it 'returns a user_token' do
15
+ expect(Authorization.token).to eq token
16
+ end
17
+ end
18
+ context 'when email and password are incorrect' do
19
+ let(:authorization_response) { response_with_body error: 'Invalid login/password' }
20
+ it 'raises a PatientZero::Error error' do
21
+ expect{ Authorization.token }.to raise_error PatientZero::Error
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ describe Client do
5
+ let(:token) { 'token-shmoken' }
6
+ let(:body) { "{}" }
7
+ let(:response) { double body: body }
8
+
9
+ describe '.connection' do
10
+ it 'news up a connection to the Viral Heat API' do
11
+ expect(Client.connection).to be_a Faraday::Connection
12
+ end
13
+ end
14
+
15
+ describe '.parse' do
16
+ subject(:parse) { Client.parse response }
17
+ context 'when the response contains an error key with nil' do
18
+ let(:body) { "{\"status\":200,\"error\":null,\"user_token\":\"#{token}\"}" }
19
+ it 'returns a hash of data' do
20
+ expect(parse).to be_a Hash
21
+ end
22
+ end
23
+ context 'when the response contains an error key that is not nil' do
24
+ let(:body) { "{\"status\":403,\"error\":\"Invalid login/password\",\"user_token\":null}" }
25
+ it 'raises an error' do
26
+ expect { parse }.to raise_error Error
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ module Message
5
+ describe Facebook do
6
+ let(:data) do
7
+ { 'platform' => 'FB',
8
+ 'likes' => 93,
9
+ 'comments' => 7,
10
+ 'impressions' => 345,
11
+ 'shares' => 22,
12
+ 'clicks' => 38 }
13
+ end
14
+ subject(:facebook_message) { Facebook.new data }
15
+ describe '#engagements' do
16
+ it 'returns the sum of likes, comments, shares, and clicks' do
17
+ expect(facebook_message.engagements).to eq 160
18
+ end
19
+ end
20
+
21
+ describe '#impressions' do
22
+ it 'returns the impression variable from the data hash' do
23
+ expect(facebook_message.impressions).to eq 345
24
+ end
25
+ end
26
+
27
+ describe '#likes' do
28
+ it 'returns the likes variable from the data hash' do
29
+ expect(facebook_message.likes).to eq 93
30
+ end
31
+ end
32
+
33
+ describe '#comments' do
34
+ it 'returns the comments variable from the data hash' do
35
+ expect(facebook_message.comments).to eq 7
36
+ end
37
+ end
38
+
39
+ describe '#shares' do
40
+ it 'returns the shares variable from the data hash' do
41
+ expect(facebook_message.shares).to eq 22
42
+ end
43
+ end
44
+
45
+ describe '#clicks' do
46
+ it 'returns the clicks variable from the data hash' do
47
+ expect(facebook_message.clicks).to eq 38
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ module Message
5
+ describe Instagram do
6
+ let(:data) do
7
+ { 'platform' => 'IG',
8
+ 'likes' => 93,
9
+ 'comments' => 7,
10
+ 'impressions' => 345,
11
+ 'shares' => '22',
12
+ 'clicks' => 38 }
13
+ end
14
+ subject(:instagram_message) { Instagram.new data }
15
+
16
+ describe '#engagements' do
17
+ it 'returns the sum of likes, comments, shares, and clicks' do
18
+ expect(instagram_message.engagements).to eq 160
19
+ end
20
+ end
21
+
22
+ describe '#impressions' do
23
+ it 'returns the impression variable from the data hash' do
24
+ expect(instagram_message.impressions).to eq 345
25
+ end
26
+ end
27
+
28
+ describe '#likes' do
29
+ it 'returns the likes variable from the data hash' do
30
+ expect(instagram_message.likes).to eq 93
31
+ end
32
+ end
33
+
34
+ describe '#comments' do
35
+ it 'returns the comments variable from the data hash' do
36
+ expect(instagram_message.comments).to eq 7
37
+ end
38
+ end
39
+
40
+ describe '#shares' do
41
+ it 'returns the shares variable from the data hash' do
42
+ expect(instagram_message.shares).to eq 22
43
+ end
44
+ end
45
+
46
+ describe '#clicks' do
47
+ it 'returns the clicks variable from the data hash' do
48
+ expect(instagram_message.clicks).to eq 38
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ module Message
5
+ describe Twitter do
6
+ let(:data) do
7
+ { 'platform' => 'TW',
8
+ 'favorites' => 7,
9
+ 'impressions' => 345,
10
+ 'retweets' => 22,
11
+ 'clicks' => 38 }
12
+ end
13
+ subject(:twitter_message) { Twitter.new data }
14
+
15
+ describe '#engagements' do
16
+ it 'returns the sum of retweets, favorites, and clicks' do
17
+ expect(twitter_message.engagements).to eq 67
18
+ end
19
+ end
20
+
21
+ describe '#retweets' do
22
+ it 'returns the retweets variable from the data hash' do
23
+ expect(twitter_message.retweets).to eq 22
24
+ end
25
+ end
26
+
27
+ describe '#favorites' do
28
+ it 'returns the favorites variable from the data hash' do
29
+ expect(twitter_message.favorites).to eq 7
30
+ end
31
+ end
32
+
33
+ describe '#clicks' do
34
+ it 'returns the clicks variable from the data hash' do
35
+ expect(twitter_message.clicks).to eq 38
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ describe Message do
5
+ describe '.for_platform' do
6
+ let(:data_hash) { { } }
7
+ context 'when platform is Facebook' do
8
+ let(:platform) { 'FB' }
9
+ it 'news up a Message::Facebook object' do
10
+ expect(Message.for_platform(platform, data_hash)).to be_a Message::Facebook
11
+ end
12
+ end
13
+
14
+ context 'when platform is Twitter' do
15
+ let(:platform) { 'TW' }
16
+ it 'news up a Message::Twitter object' do
17
+ expect(Message.for_platform(platform, data_hash)).to be_a Message::Twitter
18
+ end
19
+ end
20
+
21
+ context 'when platform is Instagram' do
22
+ let(:platform) { 'IG' }
23
+ it 'news up a Message::Instagram object' do
24
+ expect(Message.for_platform(platform, data_hash)).to be_a Message::Instagram
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,67 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ describe Organization do
5
+ let(:id) { 12345 }
6
+ let(:token) { 'token-shmoken' }
7
+ let(:organization) { Organization.new organization_response_data }
8
+ let(:organizations_response_data) { [organization_response_data] }
9
+ let(:organization_response_data) do
10
+ { "id"=>id,
11
+ "name"=>"Organization One",
12
+ "avatar"=>
13
+ { "thumbnail"=>"https://s3.amazonaws.com/viralheat/avatars/organizations/12345/thumb/fake_img_1.png",
14
+ "medium"=>"https://s3.amazonaws.com/viralheat/avatars/organizations/12345/medium/fake_img_1.png",
15
+ "large"=>"https://s3.amazonaws.com/viralheat/avatars/organizations/12345/large/fake_img_1.png" } }
16
+ end
17
+
18
+ before { allow(Authorization).to receive(:token).and_return token }
19
+
20
+ describe '.all' do
21
+ let(:organizations) { [organization] }
22
+ let(:organizations_response) { response_with_body organizations: organizations_response_data }
23
+ before { allow(Client.connection).to receive(:get).with('/mobile/api/v1/organizations', anything) { organizations_response } }
24
+
25
+ it 'calls the organizations api endpoint' do
26
+ expect(Client.connection).to receive(:get).with('/mobile/api/v1/organizations', anything)
27
+ Organization.all
28
+ end
29
+
30
+ context 'if the user has organizations' do
31
+ it 'returns an array of all organizations available' do
32
+ expect(Organization.all.map(&:id)).to eq organizations.map(&:id)
33
+ end
34
+ end
35
+
36
+ context 'if the user has no organizations' do
37
+ let(:organizations_response_data) { [] }
38
+ it 'returns an empty array' do
39
+ expect(Organization.all).to be_empty
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '#sources' do
45
+ it 'calls Source.all and passes its own organization user_token' do
46
+ allow(organization).to receive(:token).and_return token
47
+ expect(Source).to receive(:all).with(token)
48
+ organization.sources
49
+ end
50
+ end
51
+
52
+ describe '#token' do
53
+ let(:organization_specific_token) { 'organization-token' }
54
+ let(:switch_response) { response_with_body user_token: organization_specific_token }
55
+ before { allow(Client.connection).to receive(:get).with("/mobile/api/v1/organizations/#{id}/switch", anything).and_return switch_response }
56
+
57
+ it 'calls the organization switch api endpoint' do
58
+ expect(Client.connection).to receive(:get).with("/mobile/api/v1/organizations/#{id}/switch", anything)
59
+ organization.token
60
+ end
61
+
62
+ it 'returns the organization specific user_token' do
63
+ expect(organization.token).to eq organization_specific_token
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,83 @@
1
+ require 'spec_helper'
2
+
3
+ module PatientZero
4
+ describe Source do
5
+ let(:id) { '12345#instagram_account#1234567890' }
6
+ let(:token) { 'token-shmoken' }
7
+ let(:sources_response_data) { [source_response_data] }
8
+ let(:source_response_data) do
9
+ {'id'=> id,
10
+ 'name' => 'account_name',
11
+ 'is_invalid' => false,
12
+ 'is_tracked' => true,
13
+ 'platform' => 'instagram',
14
+ 'delete_id' => id }
15
+ end
16
+ let(:source) { Source.new source_response_data, token }
17
+
18
+ describe '.all' do
19
+ before do
20
+ allow(Authorization).to receive(:token).and_return token
21
+ allow(Client.connection).to receive(:get).with('/mobile/api/v1/sources/', anything).and_return response_with_body sources: sources_response_data
22
+ end
23
+ it 'calls the sources index api' do
24
+ expect(Client.connection).to receive(:get).with('/mobile/api/v1/sources/', anything)
25
+ Source.all
26
+ end
27
+ it 'calls Authorization.token if no token is passed in' do
28
+ expect(Authorization).to receive(:token)
29
+ Source.all
30
+ end
31
+ context 'when the associated organization token has sources' do
32
+ it 'returns an array of sources' do
33
+ expect(Source.all.first.id).to eq source.id
34
+ end
35
+ end
36
+ context 'when the associated organization token has no sources' do
37
+ it 'returns an empty array' do
38
+ allow(Client.connection).to receive(:get).with('/mobile/api/v1/sources/', anything).and_return response_with_body sources: []
39
+ expect(Source.all).to be_empty
40
+ end
41
+ end
42
+ end
43
+
44
+ describe '.find' do
45
+ before do
46
+ allow(Authorization).to receive(:token).and_return token
47
+ allow(Client.connection).to receive(:get).with('/mobile/api/v1/sources/show/', anything).and_return response_with_body source: source_response_data
48
+ end
49
+ it 'calls the sources show api' do
50
+ expect(Client.connection).to receive(:get).with('/mobile/api/v1/sources/show/', anything)
51
+ Source.find id
52
+ end
53
+ it 'calls Authorization.token if no token is passed in' do
54
+ expect(Authorization).to receive(:token)
55
+ Source.find id
56
+ end
57
+ context 'when the source exists' do
58
+ it 'returns a source' do
59
+ expect(Source.find(id).id).to eq source.id
60
+ end
61
+ end
62
+ context 'when no source exists' do
63
+ it 'throws an PatientZero::Error' do
64
+ allow(Client).to receive(:get).and_raise Error, 'The source could not be found. Please check your inputs.'
65
+ expect{ Source.find id }.to raise_error PatientZero::NotFoundError
66
+ end
67
+ end
68
+ end
69
+
70
+ describe '#profile_id' do
71
+ it 'returns the number at the end of the id' do
72
+ expect(source.profile_id).to eq '1234567890'
73
+ end
74
+ end
75
+
76
+ describe '#analytics' do
77
+ it 'calls Analytics.for_platorm to create an analytics object' do
78
+ expect(Analytics).to receive(:for_platform).with(source.platform, { token: source.token, source_id: source.id, start_date: nil, end_date: nil })
79
+ source.analytics
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,14 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'patient_zero'
5
+
6
+ module Helpers
7
+ def response_with_body body
8
+ double :response, body: body.to_json
9
+ end
10
+ end
11
+
12
+ RSpec.configure do |config|
13
+ config.include Helpers
14
+ end
metadata CHANGED
@@ -1,14 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: patient_zero
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adam Zaninovich
8
+ - Devin Clark
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2015-03-30 00:00:00.000000000 Z
12
+ date: 2015-04-16 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: bundler
@@ -38,21 +39,109 @@ dependencies:
38
39
  - - "~>"
39
40
  - !ruby/object:Gem::Version
40
41
  version: '10.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: pry
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: faraday
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: json
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ type: :runtime
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
41
98
  description: A gem to use the Viral Heat API
42
99
  email:
43
100
  - adam.zaninovich@gmail.com
101
+ - notdevinclark@gmail.com
44
102
  executables: []
45
103
  extensions: []
46
104
  extra_rdoc_files: []
47
105
  files:
48
106
  - ".gitignore"
107
+ - ".rspec"
49
108
  - Gemfile
50
109
  - LICENSE.txt
51
110
  - README.md
52
111
  - Rakefile
53
112
  - lib/patient_zero.rb
113
+ - lib/patient_zero/analytics.rb
114
+ - lib/patient_zero/analytics/base.rb
115
+ - lib/patient_zero/analytics/facebook.rb
116
+ - lib/patient_zero/analytics/instagram.rb
117
+ - lib/patient_zero/analytics/twitter.rb
118
+ - lib/patient_zero/authorization.rb
119
+ - lib/patient_zero/client.rb
120
+ - lib/patient_zero/configurable.rb
121
+ - lib/patient_zero/errors.rb
122
+ - lib/patient_zero/message.rb
123
+ - lib/patient_zero/message/base.rb
124
+ - lib/patient_zero/message/facebook.rb
125
+ - lib/patient_zero/message/instagram.rb
126
+ - lib/patient_zero/message/twitter.rb
127
+ - lib/patient_zero/organization.rb
128
+ - lib/patient_zero/source.rb
54
129
  - lib/patient_zero/version.rb
55
130
  - patient_zero.gemspec
131
+ - spec/patient_zero/analytics/base_spec.rb
132
+ - spec/patient_zero/analytics/facebook_spec.rb
133
+ - spec/patient_zero/analytics/instagram_spec.rb
134
+ - spec/patient_zero/analytics/twitter_spec.rb
135
+ - spec/patient_zero/analytics_spec.rb
136
+ - spec/patient_zero/authorization_spec.rb
137
+ - spec/patient_zero/client_spec.rb
138
+ - spec/patient_zero/message/facebook_spec.rb
139
+ - spec/patient_zero/message/instagram_spec.rb
140
+ - spec/patient_zero/message/twitter_spec.rb
141
+ - spec/patient_zero/message_spec.rb
142
+ - spec/patient_zero/organization_spec.rb
143
+ - spec/patient_zero/source_spec.rb
144
+ - spec/spec_helper.rb
56
145
  homepage: ''
57
146
  licenses:
58
147
  - MIT
@@ -77,4 +166,18 @@ rubygems_version: 2.4.5
77
166
  signing_key:
78
167
  specification_version: 4
79
168
  summary: A gem to use the Viral Heat API
80
- test_files: []
169
+ test_files:
170
+ - spec/patient_zero/analytics/base_spec.rb
171
+ - spec/patient_zero/analytics/facebook_spec.rb
172
+ - spec/patient_zero/analytics/instagram_spec.rb
173
+ - spec/patient_zero/analytics/twitter_spec.rb
174
+ - spec/patient_zero/analytics_spec.rb
175
+ - spec/patient_zero/authorization_spec.rb
176
+ - spec/patient_zero/client_spec.rb
177
+ - spec/patient_zero/message/facebook_spec.rb
178
+ - spec/patient_zero/message/instagram_spec.rb
179
+ - spec/patient_zero/message/twitter_spec.rb
180
+ - spec/patient_zero/message_spec.rb
181
+ - spec/patient_zero/organization_spec.rb
182
+ - spec/patient_zero/source_spec.rb
183
+ - spec/spec_helper.rb