app_earnings 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.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:
|