app_earnings 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +71 -0
- data/Rakefile +24 -0
- data/app_earnings.gemspec +32 -0
- data/bin/app_earnings +14 -0
- data/lib/app_earnings.rb +21 -0
- data/lib/app_earnings/amazon.rb +3 -0
- data/lib/app_earnings/amazon/amazon_report.rb +37 -0
- data/lib/app_earnings/amazon/parser.rb +42 -0
- data/lib/app_earnings/amazon/reporter.rb +89 -0
- data/lib/app_earnings/cli.rb +31 -0
- data/lib/app_earnings/google_play.rb +3 -0
- data/lib/app_earnings/google_play/parser.rb +22 -0
- data/lib/app_earnings/google_play/play_report.rb +29 -0
- data/lib/app_earnings/google_play/reporter.rb +65 -0
- data/lib/app_earnings/report.rb +94 -0
- data/spec/app_earnings/amazon/amazon_reporter_spec.rb +28 -0
- data/spec/app_earnings/amazon/parser_spec.rb +24 -0
- data/spec/app_earnings/app_earnings_spec.rb +15 -0
- data/spec/app_earnings/google_play/parser_spec.rb +17 -0
- data/spec/app_earnings/google_play/reporter_spec.rb +18 -0
- data/spec/app_earnings/report_spec.rb +44 -0
- data/spec/fixtures/amazon_earnings.csv +32 -0
- data/spec/fixtures/amazon_payments.csv +18 -0
- data/spec/fixtures/play_transactions.csv +4 -0
- data/spec/spec_helper.rb +17 -0
- metadata +187 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 487fedffadd25385824a6975622541ccfc1ed345
|
4
|
+
data.tar.gz: 90512a69c5fba64f7b9076335e5014b292d43e2c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7bbd1156a220de74b2131579141f193dcb405f4de9c293768f32b822cc75bf1d6ce934af67459297ace932de3cd949537eec5d9d5526d2e85a14d08126af7dcc
|
7
|
+
data.tar.gz: 9cdc32f3ba8a84db4573e3e5fd3253061a7ce6c8d83c5bddcd0ef9b56e1ecbcab389ffb6a1f9bf1d1d6a4f1590285dc9306ad91a4fd75419fd36680711ad3902
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Egghead Games LLC
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
app_earnings
|
2
|
+
============
|
3
|
+
|
4
|
+
Process App Store monthly "raw" CSV files into a report by Application and IAP.
|
5
|
+
Currently supports Google Play and Amazon Android marketplace data.
|
6
|
+
|
7
|
+
## Background
|
8
|
+
|
9
|
+
Egghead Games is an independent app developer who has to provide monthly royalties to various licensors based on sales.
|
10
|
+
None of the existing tools like Distimo or App Annie provided data that was sufficiently accurate to make payments.
|
11
|
+
The stores provide data files (usually CSV) with the raw data, but processing these by hand or spreadsheet is painful.
|
12
|
+
This command utility takes the CSV files and outputs sales by app and in-app purchase within the app.
|
13
|
+
It takes into account refunds and currency conversions, so that the amounts given should exactly correspond to the payments
|
14
|
+
made by the app store.
|
15
|
+
|
16
|
+
## Description
|
17
|
+
|
18
|
+
The Google Play and Amazon Android Marketplaces provide monthly earning reports in the form of CSV files.
|
19
|
+
|
20
|
+
This command line utility processes the transactions, groups them by app and in-app purchase (IAP), and produces the net dollar amount for each app. It includes the following features:
|
21
|
+
|
22
|
+
* handles returns
|
23
|
+
* provides sub-totals for IAPs, so you can see how much individual IAPs are earning.
|
24
|
+
* uses Amazon exchange rates to ensure that all amounts are in your final currency
|
25
|
+
* should handle non-USD Google Wallet accounts (though this is untested)
|
26
|
+
* JSON output is available, to make further processing simpler
|
27
|
+
|
28
|
+
By default, it provides simple text output.
|
29
|
+
|
30
|
+
## Installation
|
31
|
+
|
32
|
+
Install it yourself as:
|
33
|
+
|
34
|
+
$ gem install app_earnings
|
35
|
+
|
36
|
+
## Usage
|
37
|
+
|
38
|
+
This will show the full set of current commands and options:
|
39
|
+
|
40
|
+
app_earnings help
|
41
|
+
|
42
|
+
### Google Play Marketplace
|
43
|
+
|
44
|
+
To process a Google Play monthly earnings report csv file into a text sales report:
|
45
|
+
|
46
|
+
app_earnings play PlayApps_201401.csv
|
47
|
+
|
48
|
+
Alternatively, you can get a JSON version of the data with:
|
49
|
+
|
50
|
+
app_earnings play PlayApps_201401.csv --format json
|
51
|
+
|
52
|
+
### Amazon Android Marketplace
|
53
|
+
|
54
|
+
To process Amazon monthly earnings report files into a text sales report:
|
55
|
+
|
56
|
+
app_earnings amazon EarningsReport-Jan-1-2014-Jan-31-2014.csv PaymentReport-Feb-1-2014-Mar-1-2014.csv
|
57
|
+
|
58
|
+
Note that the payment report must be for the month after, as Amazon payments happen a month in arrears.
|
59
|
+
|
60
|
+
Alternatively, you can get a JSON version of the data with:
|
61
|
+
|
62
|
+
app_earnings amazon EarningsReport-Jan-1-2014-Jan-31-2014.csv PaymentReport-Feb-1-2014-Mar-1-2014.csv --format json
|
63
|
+
|
64
|
+
|
65
|
+
## Contributing
|
66
|
+
|
67
|
+
1. Fork it ( http://github.com/eggheadgames/app_earnings/fork )
|
68
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
69
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
70
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
71
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rubocop/rake_task'
|
3
|
+
require 'ruby-lint/rake_task'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
|
6
|
+
RSpec::Core::RakeTask.new
|
7
|
+
|
8
|
+
task :default => :spec
|
9
|
+
task :test => :spec
|
10
|
+
|
11
|
+
desc 'Run RuboCop on the lib directory'
|
12
|
+
Rubocop::RakeTask.new(:rubocop) do |task|
|
13
|
+
task.patterns = ['lib/**/*.rb']
|
14
|
+
# don't abort rake on failure
|
15
|
+
task.fail_on_error = false
|
16
|
+
end
|
17
|
+
|
18
|
+
RubyLint::RakeTask.new do |task|
|
19
|
+
task.name = 'lint'
|
20
|
+
task.files = ['lib']
|
21
|
+
end
|
22
|
+
|
23
|
+
desc 'Runs specs/Lint/Style checks'
|
24
|
+
task :all => [ :lint, :rubocop, :spec ]
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "app_earnings"
|
7
|
+
spec.version = "1.0.0"
|
8
|
+
spec.authors = ["Egghead Games", "André Luis Leal Cardoso Junior", "Michael Mee"]
|
9
|
+
spec.email = ["support@eggheadgames.com"]
|
10
|
+
spec.summary = %q{Process app store csv report files into a text/json summary by application and iap}
|
11
|
+
spec.description = <<-EOF
|
12
|
+
Allows easy calculation of revenue sharing by app and in-app purchases.
|
13
|
+
Munges the monthly CSV reports from Google Play or Amazon Android app stores.
|
14
|
+
Does Amazon currency conversions and verifies against payment amounts.
|
15
|
+
EOF
|
16
|
+
spec.homepage = "http://github.com/eggheadgames/app_earnings"
|
17
|
+
spec.license = "MIT"
|
18
|
+
|
19
|
+
spec.files = `git ls-files -z`.split("\x0")
|
20
|
+
spec.executables << 'app_earnings'
|
21
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
22
|
+
spec.require_paths = ["lib"]
|
23
|
+
|
24
|
+
spec.add_dependency "thor", "~> 0.18.0"
|
25
|
+
spec.add_dependency "currencies", "~> 0.4.2"
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
28
|
+
spec.add_development_dependency "rake"
|
29
|
+
spec.add_development_dependency "rubocop", "~> 0.18.1"
|
30
|
+
spec.add_development_dependency "ruby-lint", "~> 1.1.0"
|
31
|
+
spec.add_development_dependency "rspec", "~> 2.14"
|
32
|
+
end
|
data/bin/app_earnings
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'app_earnings'
|
7
|
+
rescue LoadError
|
8
|
+
lib = File.expand_path('../../lib', __FILE__)
|
9
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
10
|
+
|
11
|
+
require 'app_earnings'
|
12
|
+
end
|
13
|
+
|
14
|
+
AppEarnings::Cli.start(ARGV)
|
data/lib/app_earnings.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'app_earnings/cli'
|
2
|
+
require 'app_earnings/report'
|
3
|
+
require 'app_earnings/amazon'
|
4
|
+
require 'app_earnings/google_play'
|
5
|
+
|
6
|
+
# Process Monthly Earnings report
|
7
|
+
# From GoogleApps or Amazon
|
8
|
+
# transaction CSV file into a report by Application and IAP
|
9
|
+
module AppEarnings
|
10
|
+
def self.play_report(name, format = 'text')
|
11
|
+
parsed = GooglePlay::Parser.new(name).extract
|
12
|
+
GooglePlay::Reporter.new(parsed).report_as(format)
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.amazon_report(payments, earnings, format = 'text')
|
16
|
+
parsed = []
|
17
|
+
parsed << Amazon::Parser.new(payments).extract
|
18
|
+
parsed << Amazon::Parser.new(earnings).extract
|
19
|
+
Amazon::Reporter.new(parsed).report_as(format)
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'iso4217'
|
3
|
+
|
4
|
+
module AppEarnings::Amazon
|
5
|
+
# Converts a csv file to a hash.
|
6
|
+
class AmazonReport
|
7
|
+
include AppEarnings::Report
|
8
|
+
|
9
|
+
attr_accessor :exchange_info
|
10
|
+
|
11
|
+
def initialize(name, transactions, exchange_info)
|
12
|
+
@exchange_info = exchange_info
|
13
|
+
extract_amount(name, transactions)
|
14
|
+
end
|
15
|
+
|
16
|
+
def convert_amounts(amounts)
|
17
|
+
amounts.reduce(0.0) do |sum, (marketplace, amount)|
|
18
|
+
amount = amount * @exchange_info[marketplace].to_f
|
19
|
+
sum + amount
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def amount_from_transactions(transactions)
|
24
|
+
amounts = transactions.reduce({}) do |sum, transaction|
|
25
|
+
marketplace = transaction[:marketplace]
|
26
|
+
sum[marketplace] ||= 0.0
|
27
|
+
sum[marketplace] += transaction[:gross_earnings_or_refunds].to_f
|
28
|
+
sum
|
29
|
+
end
|
30
|
+
|
31
|
+
{
|
32
|
+
currency: 'USD',
|
33
|
+
amount: convert_amounts(amounts).round(2)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module AppEarnings::Amazon
|
4
|
+
# Converts a csv file to a hash.
|
5
|
+
class Parser
|
6
|
+
attr_accessor :file_name
|
7
|
+
|
8
|
+
def initialize(file_name)
|
9
|
+
@file_name = file_name
|
10
|
+
@contents = File.read(@file_name)
|
11
|
+
@header, @summary, @details = @contents.split(/Summary|Detail/)
|
12
|
+
|
13
|
+
if @header =~ /Payment Report/
|
14
|
+
@report_type = :payments
|
15
|
+
else
|
16
|
+
@report_type = :earnings
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def extract
|
21
|
+
{
|
22
|
+
report_type: @report_type,
|
23
|
+
header: @header,
|
24
|
+
summary: parse(@summary.strip),
|
25
|
+
details: parse((@details || '').strip)
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def parse(content)
|
32
|
+
return nil if content.nil?
|
33
|
+
extracted_data = []
|
34
|
+
options = { headers: true, header_converters: :symbol }
|
35
|
+
|
36
|
+
CSV.parse(content, options) do |row|
|
37
|
+
extracted_data << row.to_hash
|
38
|
+
end
|
39
|
+
extracted_data
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module AppEarnings::Amazon
|
2
|
+
# Generates a report based on the data provided
|
3
|
+
class Reporter
|
4
|
+
AVAILABLE_FORMATS = %w(json text)
|
5
|
+
attr_accessor :data
|
6
|
+
|
7
|
+
def initialize(data)
|
8
|
+
@data = data
|
9
|
+
@payments_data = @data.find { |r| r[:report_type] == :payments }
|
10
|
+
@earnings_data = (@data - [@payments_data]).first
|
11
|
+
@exchange_info = fetch_exchange_info
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch_exchange_info
|
15
|
+
@payments_full_amount = 0.0
|
16
|
+
@payments_data[:summary].reduce({}) do |all_info, data|
|
17
|
+
all_info[data[:marketplace]] = data[:fx_rate]
|
18
|
+
@payments_full_amount += data[:total_payment].gsub(/,/, '').to_f
|
19
|
+
all_info
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def full_amount
|
24
|
+
total = @reports.reduce(0.0) { |a, e| a + e.amount.to_f }
|
25
|
+
total - refunds
|
26
|
+
end
|
27
|
+
|
28
|
+
def refunds
|
29
|
+
@earnings_data[:summary].reduce(0.0) do |sum, marketplace|
|
30
|
+
currency = marketplace[:refunds_currency_code]
|
31
|
+
amount = marketplace[:refunds].gsub(/\(|\)/, '').to_f
|
32
|
+
amount = amount * @exchange_info[currency].to_f if currency != 'USD'
|
33
|
+
sum + amount
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def generate
|
38
|
+
@reports = []
|
39
|
+
@data.each do |raw_data|
|
40
|
+
if raw_data[:report_type] == :earnings
|
41
|
+
by_apps = raw_data[:details].group_by { |element| element[:app] }
|
42
|
+
.sort_by { |app| app }
|
43
|
+
|
44
|
+
by_apps.each do |key, application|
|
45
|
+
@reports << AmazonReport.new(key, application, @exchange_info)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def report_as(format = 'text')
|
52
|
+
unless AVAILABLE_FORMATS.include?(format)
|
53
|
+
fail "#{format} Not supported. Available formats are: " +
|
54
|
+
" #{AVAILABLE_FORMATS.join(", ")}"
|
55
|
+
end
|
56
|
+
|
57
|
+
generate
|
58
|
+
render_as(format)
|
59
|
+
end
|
60
|
+
|
61
|
+
def render_as(format = 'text')
|
62
|
+
case format
|
63
|
+
when 'text'
|
64
|
+
as_text
|
65
|
+
when 'json'
|
66
|
+
as_json
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def as_text
|
71
|
+
amount = AppEarnings::Report.formatted_amount('USD', full_amount)
|
72
|
+
refund = AppEarnings::Report.formatted_amount('USD', refunds)
|
73
|
+
puts @reports
|
74
|
+
puts "Total of refunds: #{refund}"
|
75
|
+
puts "Total of all transactions: #{amount}"
|
76
|
+
|
77
|
+
if @payments_full_amount.round(2) != full_amount
|
78
|
+
puts "Total from Payment Report: #{@payments_full_amount.round(2)}"
|
79
|
+
end
|
80
|
+
@reports
|
81
|
+
end
|
82
|
+
|
83
|
+
def as_json
|
84
|
+
puts JSON.generate(apps: @reports.map(&:to_json),
|
85
|
+
currency: 'USD',
|
86
|
+
total: full_amount)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'thor'
|
2
|
+
|
3
|
+
module AppEarnings
|
4
|
+
# Command-line utility: Retrieves CSV file
|
5
|
+
# and generates a report based into it.
|
6
|
+
class Cli < Thor
|
7
|
+
desc 'play PlayApps.csv', 'Generates a detailed report'\
|
8
|
+
' for the provided Google Play transaction file'
|
9
|
+
method_option :format, type: :string, default: 'text',
|
10
|
+
aliases: '-f', enum: %w(text json)
|
11
|
+
def play(name)
|
12
|
+
if File.exists?(name)
|
13
|
+
AppEarnings.play_report(name, options[:format])
|
14
|
+
else
|
15
|
+
puts "File '#{name}' not found!"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
desc 'amazon PaymentReport.csv EarningsReport.csv', 'Generates a '\
|
20
|
+
'detailed report for the Amazon report files'
|
21
|
+
method_option :format, type: :string, default: 'text',
|
22
|
+
aliases: '-f', enum: %w(text json)
|
23
|
+
def amazon(payments, earnings)
|
24
|
+
if File.exists?(payments) && File.exists?(earnings)
|
25
|
+
AppEarnings.amazon_report(payments, earnings, options[:format])
|
26
|
+
else
|
27
|
+
puts "Files '#{payments}' and '#{earnings}' were not found!"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'csv'
|
2
|
+
|
3
|
+
module AppEarnings::GooglePlay
|
4
|
+
# Converts a csv file to a hash.
|
5
|
+
class Parser
|
6
|
+
attr_accessor :file_name
|
7
|
+
|
8
|
+
def initialize(file_name)
|
9
|
+
@file_name = file_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def extract
|
13
|
+
@extracted_data = []
|
14
|
+
options = { headers: true, header_converters: :symbol }
|
15
|
+
|
16
|
+
CSV.foreach(@file_name, options) do |row|
|
17
|
+
@extracted_data << row.to_hash
|
18
|
+
end
|
19
|
+
@extracted_data
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module AppEarnings::GooglePlay
|
2
|
+
# Represents the report.
|
3
|
+
class PlayReport
|
4
|
+
include AppEarnings::Report
|
5
|
+
|
6
|
+
def initialize(name, transactions)
|
7
|
+
extract_amount(name, transactions)
|
8
|
+
unless transactions_from_product.first.nil?
|
9
|
+
@description = transactions_from_product.first[:product_title]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# It sums up to all available amounts, but it takes just the first one.
|
14
|
+
# As it's usually just one.
|
15
|
+
def amount_from_transactions(transactions)
|
16
|
+
all_currencies = transactions.reduce({}) do |sum, transaction|
|
17
|
+
currency = transaction[:merchant_currency]
|
18
|
+
sum[currency] ||= 0.0
|
19
|
+
sum[currency] += transaction[:amount_merchant_currency].to_f
|
20
|
+
sum
|
21
|
+
end.first
|
22
|
+
|
23
|
+
{
|
24
|
+
currency: all_currencies.first,
|
25
|
+
amount: all_currencies.last.round(2)
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'json'
|
3
|
+
require 'iso4217'
|
4
|
+
|
5
|
+
module AppEarnings::GooglePlay
|
6
|
+
# Generates a report based on the data provided
|
7
|
+
class Reporter
|
8
|
+
AVAILABLE_FORMATS = %w(json text)
|
9
|
+
attr_accessor :raw_data
|
10
|
+
|
11
|
+
def initialize(raw_data)
|
12
|
+
@raw_data = raw_data
|
13
|
+
end
|
14
|
+
|
15
|
+
def generate
|
16
|
+
by_apps = @raw_data.group_by { |element| element[:product_id] }
|
17
|
+
.sort_by { |app| app }
|
18
|
+
|
19
|
+
@reports = []
|
20
|
+
by_apps.each do |key, application|
|
21
|
+
@reports << PlayReport.new(key, application)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def full_amount
|
26
|
+
total = @reports.reduce(0.0) { |a, e| a + e.amount.to_f }
|
27
|
+
currency = @reports.first.currency
|
28
|
+
[currency, total]
|
29
|
+
end
|
30
|
+
|
31
|
+
def report_as(format = 'text')
|
32
|
+
unless AVAILABLE_FORMATS.include?(format)
|
33
|
+
fail "#{format} Not supported. Available formats are: " +
|
34
|
+
" #{AVAILABLE_FORMATS.join(", ")}"
|
35
|
+
end
|
36
|
+
|
37
|
+
generate
|
38
|
+
render_as(format)
|
39
|
+
end
|
40
|
+
|
41
|
+
def render_as(format = 'text')
|
42
|
+
case format
|
43
|
+
when 'text'
|
44
|
+
as_text
|
45
|
+
when 'json'
|
46
|
+
as_json
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def as_text
|
51
|
+
currency, total = full_amount
|
52
|
+
formatted_amount = AppEarnings::Report.formatted_amount(currency, total)
|
53
|
+
puts @reports
|
54
|
+
puts "Total of all transactions: #{formatted_amount}"
|
55
|
+
@reports
|
56
|
+
end
|
57
|
+
|
58
|
+
def as_json
|
59
|
+
currency, total = full_amount
|
60
|
+
puts JSON.generate(apps: @reports.map(&:to_json),
|
61
|
+
currency: currency,
|
62
|
+
total: total)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module AppEarnings
|
2
|
+
# Base class for reports
|
3
|
+
module Report
|
4
|
+
attr_accessor :name, :transactions, :amount, :currency
|
5
|
+
|
6
|
+
def extract_amount(name, transactions)
|
7
|
+
@name = name
|
8
|
+
@transactions = transactions
|
9
|
+
@total_amount = amount_from_transactions(@transactions)
|
10
|
+
@currency = @total_amount[:currency]
|
11
|
+
@amount = @total_amount[:amount]
|
12
|
+
end
|
13
|
+
|
14
|
+
def transactions_by_type
|
15
|
+
transactions.group_by { |transaction| transaction[:transaction_type] }
|
16
|
+
end
|
17
|
+
|
18
|
+
def transactions_from_in_app_purchases
|
19
|
+
transactions.reject do |tr|
|
20
|
+
tr[:sku_id].nil? && tr[:vendor_sku].nil?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def transactions_from_in_app_purchases_by_id_and_name
|
25
|
+
transactions_from_in_app_purchases.group_by do |tr|
|
26
|
+
[tr[:sku_id] || tr[:vendor_sku],
|
27
|
+
tr[:item_name] || tr[:product_title]]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def transactions_count_by_type
|
32
|
+
transaction_count = {}
|
33
|
+
transactions_by_type.each do |type, transactions|
|
34
|
+
transaction_count[type] = transactions.length
|
35
|
+
end
|
36
|
+
transaction_count
|
37
|
+
end
|
38
|
+
|
39
|
+
def transactions_from_product
|
40
|
+
transactions - transactions_from_in_app_purchases
|
41
|
+
end
|
42
|
+
|
43
|
+
def total_from_in_app_purchases
|
44
|
+
transactions_from_in_app_purchases_by_id_and_name.map do |iap, tr|
|
45
|
+
{
|
46
|
+
id: iap.first,
|
47
|
+
name: iap.last
|
48
|
+
}.merge(amount_from_transactions(tr))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def formatted_transactions_count_by_type
|
53
|
+
transactions_count_by_type.sort_by { |name, _| name }.map do |tr|
|
54
|
+
tr.join(': ')
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def formatted_total_by_products
|
59
|
+
total_from_in_app_purchases.sort_by { |product| product[:id] }
|
60
|
+
.map do |app|
|
61
|
+
total_amount = Report.formatted_amount(app[:currency], app[:amount])
|
62
|
+
"#{app[:id]} - #{app[:name]}: #{total_amount}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.formatted_amount(currency, amount)
|
67
|
+
symbol = ISO4217::Currency.from_code(currency).symbol
|
68
|
+
"#{currency} #{symbol}#{sprintf('%.2f', amount)}"
|
69
|
+
end
|
70
|
+
|
71
|
+
def to_json
|
72
|
+
{
|
73
|
+
id: @name,
|
74
|
+
name: @description,
|
75
|
+
transactions_types: transactions_count_by_type,
|
76
|
+
total: @amount.round(2),
|
77
|
+
currency: @currency,
|
78
|
+
subtotals: total_from_in_app_purchases
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def to_s
|
83
|
+
%Q(#{@name} #{@description}
|
84
|
+
Transactions: #{formatted_transactions_count_by_type.join(", ")}
|
85
|
+
Total:
|
86
|
+
#{Report.formatted_amount(@currency, @amount)}
|
87
|
+
|
88
|
+
Sub totals by IAP:
|
89
|
+
#{formatted_total_by_products.join("\n")}
|
90
|
+
|
91
|
+
)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AppEarnings::Amazon::Reporter do
|
4
|
+
let(:payments_data) { AppEarnings::Amazon::Parser.new(file("amazon_payments.csv")).extract }
|
5
|
+
let(:earnings_data) { AppEarnings::Amazon::Parser.new(file("amazon_earnings.csv")).extract }
|
6
|
+
let(:dummy_data) { [ { merchant_currency: "USD", amount_merchant_currency: 10.0 },
|
7
|
+
{ merchant_currency: "USD", amount_merchant_currency: 5.0 } ] }
|
8
|
+
let(:reporter) { AppEarnings::Amazon::Reporter.new([payments_data, earnings_data]) }
|
9
|
+
|
10
|
+
it "should return an array of report objects" do
|
11
|
+
data = reporter.report_as('text')
|
12
|
+
expect(data).to be_a(Array)
|
13
|
+
expect(data.first).to be_a(AppEarnings::Amazon::AmazonReport)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should fail when trying to use an unsupported format" do
|
17
|
+
expect { reporter.report_as('unknown') }.to raise_error
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should sum refunds" do
|
21
|
+
reporter.refunds.should eql(4.18)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should return total amount" do
|
25
|
+
reporter.generate
|
26
|
+
reporter.full_amount.should eql(10.3)
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AppEarnings::Amazon::Parser do
|
4
|
+
context "Validation" do
|
5
|
+
it "should fail when an invalid csv is provided" do
|
6
|
+
expect { AppEarnings::Amazon::Parser.new("non_existant.csv").extract }.to raise_error(Errno::ENOENT)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
context "Extracted data" do
|
11
|
+
let(:extracted_data_earnings) { AppEarnings::Amazon::Parser.new(file("amazon_earnings.csv")).extract }
|
12
|
+
let(:extracted_data_payments) { AppEarnings::Amazon::Parser.new(file("amazon_payments.csv")).extract }
|
13
|
+
|
14
|
+
it "should retrieve data from the provided csv and find out the type" do
|
15
|
+
expect(extracted_data_earnings[:report_type]).to eql(:earnings)
|
16
|
+
expect(extracted_data_earnings).to_not be_empty
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should retrieve data from the provided csv and find out the type" do
|
20
|
+
expect(extracted_data_payments[:report_type]).to eql(:payments)
|
21
|
+
expect(extracted_data_payments).to_not be_empty
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AppEarnings do
|
4
|
+
it "should generate a report by application based on a provided csv file for googple play" do
|
5
|
+
expect_any_instance_of(AppEarnings::GooglePlay::Parser).to receive(:extract)
|
6
|
+
expect_any_instance_of(AppEarnings::GooglePlay::Reporter).to receive(:report_as).with('text')
|
7
|
+
|
8
|
+
AppEarnings.play_report(file("play_transactions.csv"))
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should generate a report by application based on a provided csv file for amazon" do
|
12
|
+
expect_any_instance_of(AppEarnings::Amazon::Reporter).to receive(:report_as).with('text')
|
13
|
+
AppEarnings.amazon_report(file("amazon_earnings.csv"), file("amazon_payments.csv"))
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AppEarnings::GooglePlay::Parser do
|
4
|
+
context "Validation" do
|
5
|
+
it "should fail when an invalid csv is provided" do
|
6
|
+
expect { AppEarnings::GooglePlay::Parser.new("non_existant.csv").extract }.to raise_error(Errno::ENOENT)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
context "Extracted data" do
|
11
|
+
let(:extracted_data) { AppEarnings::GooglePlay::Parser.new(file("play_transactions.csv")).extract }
|
12
|
+
|
13
|
+
it "should retrieve data from the provided csv" do
|
14
|
+
expect(extracted_data).to_not be_empty
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AppEarnings::GooglePlay::Reporter do
|
4
|
+
let(:extracted_data) { AppEarnings::GooglePlay::Parser.new(file("play_transactions.csv")).extract }
|
5
|
+
let(:dummy_data) { [ { merchant_currency: "USD", amount_merchant_currency: 10.0 },
|
6
|
+
{ merchant_currency: "USD", amount_merchant_currency: 5.0 } ] }
|
7
|
+
let(:reporter) { AppEarnings::GooglePlay::Reporter.new(extracted_data) }
|
8
|
+
|
9
|
+
it "should return an array of report objects" do
|
10
|
+
data = reporter.report_as('text')
|
11
|
+
expect(data).to be_a(Array)
|
12
|
+
expect(data.first).to be_a(AppEarnings::GooglePlay::PlayReport)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should fail when trying to use an unsupported format" do
|
16
|
+
expect { reporter.report_as('unknown') }.to raise_error
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AppEarnings::Report do
|
4
|
+
let(:google_fee) { { product_title: "one", transaction_type: "Google Fee", merchant_currency: "USD", amount_merchant_currency: 10.0, sku_id: '1' } }
|
5
|
+
let(:charge) { { product_title: "two", transaction_type: "Charge", merchant_currency: "USD", amount_merchant_currency: 20.0 } }
|
6
|
+
let(:charge_eur) { { product_title: "two", transaction_type: "Charge", merchant_currency: "EUR", amount_merchant_currency: 20.0 } }
|
7
|
+
let(:transactions) { [ google_fee, charge, google_fee, charge_eur ] }
|
8
|
+
|
9
|
+
before(:each) do
|
10
|
+
@report = AppEarnings::GooglePlay::PlayReport.new("Product one", transactions)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return count of transactions by type" do
|
14
|
+
expect(@report.transactions_count_by_type).to include( { "Google Fee" => 2 }, { "Charge" => 2 })
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should group transactions by type" do
|
18
|
+
expect(@report.transactions_by_type.keys).to include("Google Fee", "Charge")
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should not include transactions made for IAP" do
|
22
|
+
expect(@report.transactions_from_product.all? { |tr| tr[:sku_id].nil? }).to be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should not include transactions made from product" do
|
26
|
+
expect(@report.transactions_from_in_app_purchases.all? { |tr| !tr[:sku_id].nil? }).to be_true
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should group transactions by product title (IAP)" do
|
30
|
+
transactions_by_product = @report.transactions_from_in_app_purchases_by_id_and_name
|
31
|
+
counter = transactions_by_product.map { |product, transactions| [ product, transactions.length] }
|
32
|
+
expect(counter).to include([["1", "one"], 2])
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should retrieve amount from a list of transactions" do
|
36
|
+
amount = @report.amount_from_transactions(transactions)
|
37
|
+
expect(amount).to include({ currency: "USD", amount: 40.00 })
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should retrieve totals by product title" do
|
41
|
+
amounts = @report.total_from_in_app_purchases
|
42
|
+
expect(amounts).to include({ id: "1", name: "one", currency: "USD", amount: 20.00})
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
Amazon Mobile App Distribution
|
2
|
+
Earnings Report
|
3
|
+
1/1/2014 - 1/31/2014
|
4
|
+
Mobile Apps
|
5
|
+
|
6
|
+
Notes
|
7
|
+
Gross Earnings - Gross royalties and/or commissions earned by the developer during the period.
|
8
|
+
Refunds - The royalty and/or commission portion of the total amount refunded to customers who returned their purchase.
|
9
|
+
"Adjustments - Sum of all adjustments that occurred on your account during the specified month, such as pricing adjustments for marketing promotions. Please contact us with questions about a specific adjustment."
|
10
|
+
Earnings Before Tax - May not reflect the actual disbursement values when royalties are subject to a withholding or payment thresholds have not been met. Please see the FAQ on the developer portal or your developer agreement for more detail.
|
11
|
+
|
12
|
+
Summary
|
13
|
+
Marketplace,From Date,To Date,Charge Units,Refund Units,Gross Earnings,Gross Earnings Currency Code,Refunds,Refunds Currency Code,Adjustments,Adjustments Currency Code,Earnings Before Tax,Earnings Before Tax Currency Code
|
14
|
+
Amazon.ca,1/1/2014,1/31/2014,7,0,2.09,CAD,0.00,CAD,0.00,CAD,1.00,CAD
|
15
|
+
Amazon.cn,1/1/2014,1/31/2014,0,0,0.00,CNY,0.00,CNY,0.00,CNY,0.00,CNY
|
16
|
+
Amazon.co.jp,1/1/2014,1/31/2014,0,0,0,JPY,0,JPY,0,JPY,0,JPY
|
17
|
+
Amazon.co.uk,1/1/2014,1/31/2014,244,0,73.61,GBP,0.00,GBP,0.00,GBP,20.00,GBP
|
18
|
+
Amazon.com,1/1/2014,1/31/2014,"3,121",-2,"2,146.67",USD,(4.18),USD,0.00,USD,"2,000.12",USD
|
19
|
+
Amazon.com.au,1/1/2014,1/31/2014,0,0,0.00,AUD,0.00,AUD,0.00,AUD,0.00,AUD
|
20
|
+
Amazon.com.br,1/1/2014,1/31/2014,0,0,0.00,BRL,0.00,BRL,0.00,BRL,0.00,BRL
|
21
|
+
Amazon.de,1/1/2014,1/31/2014,0,0,0.00,EUR,0.00,EUR,0.00,EUR,0.00,EUR
|
22
|
+
Amazon.es,1/1/2014,1/31/2014,0,0,0.00,EUR,0.00,EUR,0.00,EUR,0.00,EUR
|
23
|
+
Amazon.fr,1/1/2014,1/31/2014,0,0,0.00,EUR,0.00,EUR,0.00,EUR,0.00,EUR
|
24
|
+
Amazon.it,1/1/2014,1/31/2014,0,0,0.00,EUR,0.00,EUR,0.00,EUR,0.00,EUR
|
25
|
+
|
26
|
+
Detail
|
27
|
+
Marketplace,Date,App,Item Name,Period,Vendor SKU,Item Type,Transaction Type,List Price,List Price Currency Code,Sales Price,Sales Price Currency Code,units,Gross Earnings or Refunds,Gross Earnings or Refunds Currency Code
|
28
|
+
Amazon.com,1/1/2014,App,,,,Apps,CHARGE,2.99,USD,2.99,USD,4,8.36,USD
|
29
|
+
Amazon.co.uk,1/1/2014,Metal Gear,,,,Apps,CHARGE,1.09,GBP,0.51,GBP,1,0.36,GBP
|
30
|
+
Amazon.com,1/1/2014,Metal Gear,,,,Apps,CHARGE,1.99,USD,0.99,USD,8,5.52,USD
|
31
|
+
Amazon.co.uk,1/1/2014,Metal Gear FREE Trial,,,,Apps,CHARGE,0.00,GBP,0.00,GBP,1,0.00,GBP
|
32
|
+
Amazon.com,1/1/2014,Metal Gear FREE Trial,,,,Apps,CHARGE,0.00,USD,0.00,USD,14,0.00,USD
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Amazon Mobile App Distribution
|
2
|
+
Payment Report
|
3
|
+
1/23/2014 - 2/10/2014
|
4
|
+
Mobile Apps
|
5
|
+
|
6
|
+
Notes
|
7
|
+
Payment Cost is the cost of disbursing your earnings in your home currency
|
8
|
+
Tax Witheld is the taxes Amazon withholds from your earnings due to US tax withholding requirements
|
9
|
+
A more detailed calculation of earnings can be found in the Earnings Report.
|
10
|
+
|
11
|
+
Summary
|
12
|
+
Payment Date,Marketplace,Earnings Period,Earnings Before Tax,Earnings Before Tax Currency Code,Tax Withheld,Tax Withheld Currency Code,Payment Cost,Payment Cost Currency Code,Payment Method,FX Rate,Total Payment,Total Payment Currency Code,Payment Comment
|
13
|
+
1/30/2014,Amazon.ca,12/1/2013 - 12/31/2013,2.09,CAD,0.00,CAD,0.00,CAD,EFT,0.894737,1.0,USD,
|
14
|
+
1/30/2014,Amazon.co.uk,12/1/2013 - 12/31/2013,115.24,GBP,0.00,GBP,0.00,GBP,EFT,1.655675,100.10,USD,
|
15
|
+
1/30/2014,Amazon.com,12/1/2013 - 12/31/2013,"2,221.57",USD,0.00,USD,0.00,USD,EFT,1.000000,"2,200.12",USD,
|
16
|
+
1/30/2014,Amazon.com.au,12/1/2013 - 12/31/2013,4.54,AUD,0.00,AUD,0.00,AUD,EFT,0.876652,2.00,USD,
|
17
|
+
1/30/2014,Amazon.de,12/1/2013 - 12/31/2013,1.52,EUR,0.00,EUR,0.00,EUR,EFT,1.361842,2.07,USD,
|
18
|
+
|
@@ -0,0 +1,4 @@
|
|
1
|
+
Description,Transaction Date,Transaction Time,Tax Type,Transaction Type,Refund Type,Product Title,Product id,Product Type,Sku Id,Hardware,Buyer Country,Buyer State,Buyer Postal Code,Buyer Currency,Amount (Buyer Currency),Currency Conversion Rate,Merchant Currency,Amount (Merchant Currency)
|
2
|
+
1376321714625257,"Jan 29, 2014",7:33:12 AM PST,,Charge,,Flappy Bird,com.example.flappy,0,,m0,AU,QLD,4520,AUD,2.99,0.874200,USD,2.61
|
3
|
+
1376321714625257,"Jan 29, 2014",7:33:12 AM PST,,Google fee,,Flappy Bird,com.example.flappy,0,,m0,AU,QLD,4520,AUD,-0.90,0.874200,USD,-0.79
|
4
|
+
1346161764058234,"Jan 29, 2014",8:03:14 AM PST,,Charge,,Flappy Clone,example.clone,0,,espresso10rf,AU,,3165,AUD,2.99,0.875700,USD,2.62
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require './lib/app_earnings'
|
2
|
+
|
3
|
+
RSpec.configure do |config|
|
4
|
+
config.mock_with :rspec
|
5
|
+
config.color_enabled = true
|
6
|
+
config.tty = true
|
7
|
+
|
8
|
+
config.formatter = :documentation # :progress, :html, :textmate
|
9
|
+
config.before do
|
10
|
+
AppEarnings::Amazon::Reporter.any_instance.stub(:puts)
|
11
|
+
AppEarnings::GooglePlay::Reporter.any_instance.stub(:puts)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def file(name)
|
16
|
+
File.expand_path(File.dirname(__FILE__) + '/fixtures/' + name)
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,187 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: app_earnings
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Egghead Games
|
8
|
+
- André Luis Leal Cardoso Junior
|
9
|
+
- Michael Mee
|
10
|
+
autorequire:
|
11
|
+
bindir: bin
|
12
|
+
cert_chain: []
|
13
|
+
date: 2014-04-10 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: thor
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 0.18.0
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
version: 0.18.0
|
29
|
+
- !ruby/object:Gem::Dependency
|
30
|
+
name: currencies
|
31
|
+
requirement: !ruby/object:Gem::Requirement
|
32
|
+
requirements:
|
33
|
+
- - ~>
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: 0.4.2
|
36
|
+
type: :runtime
|
37
|
+
prerelease: false
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
39
|
+
requirements:
|
40
|
+
- - ~>
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: 0.4.2
|
43
|
+
- !ruby/object:Gem::Dependency
|
44
|
+
name: bundler
|
45
|
+
requirement: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ~>
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: '1.5'
|
50
|
+
type: :development
|
51
|
+
prerelease: false
|
52
|
+
version_requirements: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
54
|
+
- - ~>
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '1.5'
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: rake
|
59
|
+
requirement: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
type: :development
|
65
|
+
prerelease: false
|
66
|
+
version_requirements: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - '>='
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: '0'
|
71
|
+
- !ruby/object:Gem::Dependency
|
72
|
+
name: rubocop
|
73
|
+
requirement: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.18.1
|
78
|
+
type: :development
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ~>
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: 0.18.1
|
85
|
+
- !ruby/object:Gem::Dependency
|
86
|
+
name: ruby-lint
|
87
|
+
requirement: !ruby/object:Gem::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ~>
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: 1.1.0
|
92
|
+
type: :development
|
93
|
+
prerelease: false
|
94
|
+
version_requirements: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ~>
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: 1.1.0
|
99
|
+
- !ruby/object:Gem::Dependency
|
100
|
+
name: rspec
|
101
|
+
requirement: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ~>
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '2.14'
|
106
|
+
type: :development
|
107
|
+
prerelease: false
|
108
|
+
version_requirements: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ~>
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '2.14'
|
113
|
+
description: "Allows easy calculation of revenue sharing by app and in-app purchases.
|
114
|
+
\nMunges the monthly CSV reports from Google Play or Amazon Android app stores.
|
115
|
+
\nDoes Amazon currency conversions and verifies against payment amounts.\n"
|
116
|
+
email:
|
117
|
+
- support@eggheadgames.com
|
118
|
+
executables:
|
119
|
+
- app_earnings
|
120
|
+
extensions: []
|
121
|
+
extra_rdoc_files: []
|
122
|
+
files:
|
123
|
+
- .gitignore
|
124
|
+
- Gemfile
|
125
|
+
- LICENSE.txt
|
126
|
+
- README.md
|
127
|
+
- Rakefile
|
128
|
+
- app_earnings.gemspec
|
129
|
+
- lib/app_earnings.rb
|
130
|
+
- lib/app_earnings/amazon.rb
|
131
|
+
- lib/app_earnings/amazon/amazon_report.rb
|
132
|
+
- lib/app_earnings/amazon/parser.rb
|
133
|
+
- lib/app_earnings/amazon/reporter.rb
|
134
|
+
- lib/app_earnings/cli.rb
|
135
|
+
- lib/app_earnings/google_play.rb
|
136
|
+
- lib/app_earnings/google_play/parser.rb
|
137
|
+
- lib/app_earnings/google_play/play_report.rb
|
138
|
+
- lib/app_earnings/google_play/reporter.rb
|
139
|
+
- lib/app_earnings/report.rb
|
140
|
+
- spec/app_earnings/amazon/amazon_reporter_spec.rb
|
141
|
+
- spec/app_earnings/amazon/parser_spec.rb
|
142
|
+
- spec/app_earnings/app_earnings_spec.rb
|
143
|
+
- spec/app_earnings/google_play/parser_spec.rb
|
144
|
+
- spec/app_earnings/google_play/reporter_spec.rb
|
145
|
+
- spec/app_earnings/report_spec.rb
|
146
|
+
- spec/fixtures/amazon_earnings.csv
|
147
|
+
- spec/fixtures/amazon_payments.csv
|
148
|
+
- spec/fixtures/play_transactions.csv
|
149
|
+
- spec/spec_helper.rb
|
150
|
+
- bin/app_earnings
|
151
|
+
homepage: http://github.com/eggheadgames/app_earnings
|
152
|
+
licenses:
|
153
|
+
- MIT
|
154
|
+
metadata: {}
|
155
|
+
post_install_message:
|
156
|
+
rdoc_options: []
|
157
|
+
require_paths:
|
158
|
+
- lib
|
159
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
160
|
+
requirements:
|
161
|
+
- - '>='
|
162
|
+
- !ruby/object:Gem::Version
|
163
|
+
version: '0'
|
164
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
165
|
+
requirements:
|
166
|
+
- - '>='
|
167
|
+
- !ruby/object:Gem::Version
|
168
|
+
version: '0'
|
169
|
+
requirements: []
|
170
|
+
rubyforge_project:
|
171
|
+
rubygems_version: 2.0.3
|
172
|
+
signing_key:
|
173
|
+
specification_version: 4
|
174
|
+
summary: Process app store csv report files into a text/json summary by application
|
175
|
+
and iap
|
176
|
+
test_files:
|
177
|
+
- spec/app_earnings/amazon/amazon_reporter_spec.rb
|
178
|
+
- spec/app_earnings/amazon/parser_spec.rb
|
179
|
+
- spec/app_earnings/app_earnings_spec.rb
|
180
|
+
- spec/app_earnings/google_play/parser_spec.rb
|
181
|
+
- spec/app_earnings/google_play/reporter_spec.rb
|
182
|
+
- spec/app_earnings/report_spec.rb
|
183
|
+
- spec/fixtures/amazon_earnings.csv
|
184
|
+
- spec/fixtures/amazon_payments.csv
|
185
|
+
- spec/fixtures/play_transactions.csv
|
186
|
+
- spec/spec_helper.rb
|
187
|
+
has_rdoc:
|