sales 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/README.md +26 -1
  2. data/VERSION.yml +1 -1
  3. data/bin/sale +131 -123
  4. data/sales.yml +2 -0
  5. metadata +2 -2
data/README.md CHANGED
@@ -18,7 +18,32 @@ open _sales.yml_ and fill in Your iTunes Connect credentials:
18
18
  :username: kitschmaster@gmail.com #iTunes connect username
19
19
  :password: yourpassword #iTunes Connect password
20
20
  :vendorId: 80076733 #iTunes Connect -> Sales and Trends, find the vendorId on the header of the table next to the company name
21
+ :convertTo: EUR #currency to convert the total proceeds into, EUR, USD, MXN, GBP, CHF...
22
+ :beVerbose: YES #verbose output
21
23
 
22
24
  with the credentials in place run _sale_ again, it should now download the latest daily report and present it.
23
25
 
24
- run `sale help` to get a list of all possible commands.
26
+ run `sale help` to get a list of all possible commands.
27
+
28
+ dependencies
29
+ ============
30
+
31
+ OSX + java runtime + ruby1.9.2
32
+
33
+ example output
34
+ ==============
35
+
36
+ ![kitschmaster_90days.jpg](https://github.com/mihael/sales/raw/master/kitschmaster_90days.jpg)
37
+
38
+ idea
39
+ ====
40
+
41
+ ii could not afford a really nice payed Mac App or such to see my poor iPhone Developer sales stats.
42
+ so ii went into the esoteric garage and coded this little script for my self.
43
+
44
+ ii am Your Headless Standup Programmer, ii stand while ii code, and ii can't see my own head.
45
+
46
+ wish
47
+ ====
48
+
49
+ ii wish ii would have a nice remote iPhone development or ruby related job, well payed and about something ii love.
data/VERSION.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  ---
2
2
  :major: 0
3
3
  :minor: 0
4
- :patch: 2
4
+ :patch: 3
5
5
  :build:
data/bin/sale CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- # Sales
3
+ # Sales, still wet, not yet dry
4
4
 
5
5
  #make nice colors for terminal strings
6
6
  class String
@@ -36,12 +36,14 @@ USAGE = <<-EGASU
36
36
 
37
37
  EGASU
38
38
 
39
+ #require all dependent gems
39
40
  require 'rubygems'
40
41
  require 'open-uri'
41
42
  require 'json'
42
43
  require 'date'
43
44
  require 'yaml'
44
45
 
46
+ #load the version file
45
47
  V = YAML.load_file(File.join(File.dirname(__FILE__), %w[.. VERSION.yml]))
46
48
  VERSION = "#{V[:major]}.#{V[:minor]}.#{V[:patch]}"
47
49
 
@@ -50,9 +52,11 @@ example_sales_yml_file = File.join(File.dirname(__FILE__), %w[.. sales.yml])
50
52
  dest_sales_yml_file = "sales.yml"
51
53
  if File.exists? dest_sales_yml_file
52
54
  S = YAML.load_file("sales.yml")
53
- username = S[:username]
54
- password = S[:password]
55
- vendorId = S[:vendorId]
55
+ @username = S[:username]
56
+ @password = S[:password]
57
+ @vendorId = S[:vendorId]
58
+ @convertTo = S[:convertTo]
59
+ @beVerbose = S[:beVerbose]
56
60
  else
57
61
  puts "Please fill out Your iTunes Connect credentials in the".red + " sales.yml ".green + "file in the same dir where You run 'sale'.".red
58
62
  `cp #{example_sales_yml_file} sales.yml; ls -al` unless File.exists? dest_sales_yml_file
@@ -84,86 +88,71 @@ SALE_IDENTS = ["1", "1F", "1T", "F1"]
84
88
  INAPP_SALE_IDENTS = ["IA1", "IA9", "IAY", "FI1"]
85
89
  UPDATE_IDENTS = ["7", "7F", "7T", "F7"]
86
90
 
87
- #the parser was copied from github, https://github.com/siuying/itunes-auto-ingestion/blob/master/lib/itunes_ingestion/sales_report_parser.rb
88
- class SalesReportParser
89
- # Parse sales report
90
- #
91
- # report - text based report form itunesconnect
92
- #
93
- # Returns array of hash, each hash contains one line of sales report
94
- def self.parse(report)
95
- lines = report.split("\n")
96
- #puts "lines: #{lines}"
97
- header = lines.shift # remove first line
98
- lines.collect do |line|
99
- #puts "line: #{line}"
100
- provider, country, sku, developer, title, version, product_type_id, units, developer_proceeds, begin_date, end_date, currency, country_code, currency_of_proceeds, apple_id, customer_price, promo_code, parent_id, subscription, period = line.split("\t")
101
- p = {
102
- :provider => provider.strip,
103
- :country => country.strip,
104
- :sku => sku.strip,
105
- :developer => developer.strip,
106
- :title => title.strip,
107
- :version => version.strip,
108
- :product_type_id => product_type_id.strip,
109
- :units => units.to_i,
110
- :developer_proceeds => developer_proceeds.to_f,
111
- :begin_date => Date.strptime(begin_date.strip, '%m/%d/%Y'),
112
- :end_date => Date.strptime(end_date.strip, '%m/%d/%Y'),
113
- :currency => currency.strip,
114
- :country_code => country_code.strip,
115
- :currency_of_proceeds => currency_of_proceeds.strip,
116
- :apple_id => apple_id.to_i,
117
- :customer_price => customer_price.to_f,
118
- :promo_code => promo_code.strip,
119
- :parent_id => parent_id.strip,
120
- :subscription => subscription.strip,
121
- :period => period
122
- }
123
- puts "parsing failed".red if p==nil
124
- p
125
- end #lines collect
126
- end #self.parse
127
- end #class
128
-
129
- puts "\nSales v#{VERSION}".red + " created by Your Headless Standup Programmer http://kitschmaster.com".dark_blue
130
-
131
- if ARGV[0] == "-h" || ARGV[0] == "help" || ARGV[0] == "h"
132
-
133
- puts USAGE
134
-
135
- elsif ARGV[0] == "daily" || ARGV[0] == "weekly"
136
- #compute alltime stats for daily or weekly reports
137
-
138
- dir_filter = ARGV[0] == "daily" ? "D" : "W"
139
-
140
- alltime_proceeds_per_currency = {} #currency is the key, value is the proceeds
141
- alltime_renewables = 0
142
- alltime_apps = {}
143
- alltime_payed_units = 0
144
- alltime_inapp_units = 0
145
- alltime_free_units = 0
146
- alltime_updated_units = 0
147
- reports = Dir["S_#{dir_filter}_*.txt"].uniq.compact
148
-
149
- if reports.empty?
91
+ # Parse sales report
92
+ # the parser was copied and tweaked from github, https://github.com/siuying/itunes-auto-ingestion/blob/master/lib/itunes_ingestion/sales_report_parser.rb, thanks
93
+ # the class was thrown away though, a simple method is so much cleaner in a simple program like this
94
+ #
95
+ # report - text based report form itunesconnect
96
+ #
97
+ # Returns array of hash, each hash contains one line of sales report
98
+ def parse(report)
99
+ lines = report.split("\n")
100
+ #puts "lines: #{lines}"
101
+ header = lines.shift # remove first line
102
+ lines.collect do |line|
103
+ #puts "line: #{line}"
104
+ provider, country, sku, developer, title, version, product_type_id, units, developer_proceeds, begin_date, end_date, currency, country_code, currency_of_proceeds, apple_id, customer_price, promo_code, parent_id, subscription, period = line.split("\t")
105
+ p = { :provider => provider.strip,
106
+ :country => country.strip,
107
+ :sku => sku.strip,
108
+ :developer => developer.strip,
109
+ :title => title.strip,
110
+ :version => version.strip,
111
+ :product_type_id => product_type_id.strip,
112
+ :units => units.to_i,
113
+ :developer_proceeds => developer_proceeds.to_f,
114
+ :begin_date => Date.strptime(begin_date.strip, '%m/%d/%Y'),
115
+ :end_date => Date.strptime(end_date.strip, '%m/%d/%Y'),
116
+ :currency => currency.strip,
117
+ :country_code => country_code.strip,
118
+ :currency_of_proceeds => currency_of_proceeds.strip,
119
+ :apple_id => apple_id.to_i,
120
+ :customer_price => customer_price.to_f,
121
+ :promo_code => promo_code.strip,
122
+ :parent_id => parent_id.strip,
123
+ :subscription => subscription.strip,
124
+ :period => period
125
+ }
126
+ puts "parsing failed".red if p==nil
127
+ p
128
+ end #lines collect
129
+ end #parse
130
+
131
+ # Computes and presents reports
132
+ #
133
+ # reports - array of report files to compute
134
+ #
135
+ #
136
+ def compute_and_present(reports)
137
+ alltime_proceeds_per_currency = {} #currency is the key, value is the proceeds
138
+ alltime_renewables = 0
139
+ alltime_apps = {}
140
+ alltime_payed_units = 0
141
+ alltime_inapp_units = 0
142
+ alltime_free_units = 0
143
+ alltime_updated_units = 0
150
144
 
151
- puts "\nPlease download reports first.".red
152
- puts "sale get:#{ARGV[0].split(':').last}\n".green
153
-
154
- else
155
-
156
145
  first_date = reports[0].split('_').last.split('.').first
157
146
  reports.each do |alltime_filename|
158
147
 
159
- #puts "Processing #{alltime_filename}".green
148
+ puts "Processing #{alltime_filename}".green if @beVerbose
160
149
 
161
150
  #get the date from the filename
162
151
  date = alltime_filename.split('_').last.split('.').first #filename example: S_D_80076793_20120706.txt
163
152
 
164
153
  report_data = File.open(alltime_filename, "rb").read
165
154
 
166
- report = SalesReportParser.parse(report_data)
155
+ report = parse(report_data)
167
156
  #puts report.class
168
157
  if report #report parsed
169
158
  apps = {}
@@ -229,23 +218,23 @@ elsif ARGV[0] == "daily" || ARGV[0] == "weekly"
229
218
  alltime_app[:updated_units] += apps_app[:updated_units]
230
219
  end
231
220
 
232
- =begin
233
- #report for date
234
- puts "\n\n______________________________________________________________".blue
235
- puts "Report for #{date}"
236
- puts "\n" + "Product".ljust(40).blue + ": " +"Downloads".green + " / " + "Updates".green
237
- puts "______________________________________________________________".yellow
238
- apps.each do |app_sku,apps_app|
239
- puts "#{apps_app[:title].ljust(40).blue}: #{apps_app[:sold_units].to_s.ljust(10).green} / #{apps_app[:updated_units].to_s.rjust(7).dark_green}"
240
- end
241
- puts "______________________________________________________________".yellow
242
- puts "#{'InApp Purchases'.ljust(40).green}: #{total_inapp_units}"
243
- puts "#{'Payed Downloads'.ljust(40).green}: #{total_payed_units}"
244
- puts "#{'Free Downloads'.ljust(40).dark_green}: #{total_free_units}"
245
- puts "#{'Updates'.ljust(40).dark_green}: #{total_updated_units}"
246
- puts "______________________________________________________________".blue
247
- puts "\n\n"
248
- =end
221
+ if @beVerbose && reports.size>1
222
+ #report for date
223
+ puts "\n\n______________________________________________________________".blue
224
+ puts "Report for #{date}"
225
+ puts "\n" + "Product".ljust(40).blue + ": " +"Downloads".green + " / " + "Updates".green
226
+ puts "______________________________________________________________".yellow
227
+ apps.each do |app_sku,apps_app|
228
+ puts "#{apps_app[:title].ljust(40).blue}: #{apps_app[:sold_units].to_s.ljust(10).green} / #{apps_app[:updated_units].to_s.rjust(7).dark_green}"
229
+ end
230
+ puts "______________________________________________________________".yellow
231
+ puts "#{'InApp Purchases'.ljust(40).green}: #{total_inapp_units}"
232
+ puts "#{'Payed Downloads'.ljust(40).green}: #{total_payed_units}"
233
+ puts "#{'Free Downloads'.ljust(40).dark_green}: #{total_free_units}"
234
+ puts "#{'Updates'.ljust(40).dark_green}: #{total_updated_units}"
235
+ puts "______________________________________________________________".blue
236
+ puts "\n\n"
237
+ end #if @beVerbose
249
238
 
250
239
  else
251
240
  puts "null report parsed".red
@@ -258,7 +247,7 @@ elsif ARGV[0] == "daily" || ARGV[0] == "weekly"
258
247
  from = Date.strptime first_date, '%Y%m%d'
259
248
  age = Date.today - from
260
249
  formatted_from = from.strftime("%b %d %Y")
261
- puts "Report for #{ARGV[0]}, from #{formatted_from}, #{age.to_i} days"
250
+ puts "Report" + (ARGV[0]? " #{ARGV[0]}":" daily") + ", from #{formatted_from}, #{age.to_i} days"
262
251
  puts "\n" + "Product".ljust(40).blue + ": " +"Downloads".green + " / " + "Updates".green
263
252
  puts "______________________________________________________________".yellow
264
253
  alltime_apps.each do |app_sku, aapp|
@@ -270,16 +259,16 @@ elsif ARGV[0] == "daily" || ARGV[0] == "weekly"
270
259
  puts "#{'Free Downloads'.ljust(40).dark_green}: #{alltime_free_units}"
271
260
  puts "#{'Updates'.ljust(40).dark_green}: #{alltime_updated_units}"
272
261
  puts "\n#{'Proceeds'.red}:\n\n"
273
- total_eurs = 0.0
262
+ total_proceeds = 0.0
274
263
  alltime_proceeds_per_currency.each do |proceed_key, proceed|
275
264
  formatted_sum = proceed > 0.0 ? "#{proceed}".green : "#{proceed}".red
276
265
  if proceed > 0.0
277
- if proceed_key == "EUR"
278
- total_eurs += proceed
266
+ if proceed_key == @convertTo
267
+ total_proceeds += proceed
279
268
  puts "#{proceed_key} : #{formatted_sum}"
280
269
  else
281
270
  #convert using google
282
- data = open("http://www.google.com/ig/calculator?q=#{proceed}#{proceed_key}=?EUR").read
271
+ data = open("http://www.google.com/ig/calculator?q=#{proceed}#{proceed_key}=?#{@convertTo}").read
283
272
  #fix broken json
284
273
  data.gsub!(/lhs:/, '"lhs":')
285
274
  data.gsub!(/rhs:/, '"rhs":')
@@ -289,32 +278,51 @@ elsif ARGV[0] == "daily" || ARGV[0] == "weekly"
289
278
  #puts data
290
279
  converted = JSON.parse data
291
280
  converted_proceed = converted["rhs"].split(' ').first.to_f
292
- total_eurs += converted_proceed
281
+ total_proceeds += converted_proceed
293
282
  puts "#{proceed_key} : #{formatted_sum} / #{converted['rhs']}"
294
283
  end
295
284
  end
296
285
  end
297
- puts "\n#{'Total'.green}: #{total_eurs} Euros"
286
+ puts "\n#{'Total'.green}: #{total_proceeds} #{@convertTo}"
298
287
  puts "______________________________________________________________".blue
299
- puts "\n\n"
288
+ puts "\n\n"
289
+ end
300
290
 
301
- end #else reports.empty?
302
291
 
292
+ #begin to show visible light of this program by displaying its name, version and creator
293
+ puts "\nSales v#{VERSION}".red + " created by Your Headless Standup Programmer http://kitschmaster.com".dark_blue if @beVerbose
294
+
295
+ if ARGV[0] == "-h" || ARGV[0] == "help" || ARGV[0] == "h"
296
+ #-----------------------------------------------------------------------------------------------------------------------------
297
+ #show help
298
+ puts USAGE
299
+ #-----------------------------------------------------------------------------------------------------------------------------
300
+ elsif ARGV[0] == "daily" || ARGV[0] == "weekly"
301
+ #-----------------------------------------------------------------------------------------------------------------------------
302
+ #compute alltime stats for daily or weekly reports
303
+ #collect the report files
304
+ dir_filter = ARGV[0] == "daily" ? "D" : "W" #the daily or the weekly files
305
+ reports = Dir["S_#{dir_filter}_*.txt"].uniq.compact
306
+ if reports.empty?
307
+ #no reports
308
+ puts "\nPlease download reports first.".red
309
+ puts "sale get:#{ARGV[0].split(':').last}\n".green
310
+ else
311
+ #compute and present the collected reports
312
+ compute_and_present(reports)
313
+ end
314
+ #-----------------------------------------------------------------------------------------------------------------------------
303
315
  elsif ARGV[0] == "get:daily"
304
-
316
+ #-----------------------------------------------------------------------------------------------------------------------------
305
317
  # Daily reports are available only for past 14 days, please enter a date within past 14 days.
306
-
307
- first_date = Date.today
308
-
318
+ first_date = Date.today
309
319
  (1..14).each do |i|
310
-
311
320
  date = (first_date - i).to_s.gsub('-', '')
312
321
  puts "\nGetting Daily Sales Report for #{date}\n"
313
-
314
- filename = "S_D_#{vendorId}_#{date}.txt"
322
+ filename = "S_D_#{@vendorId}_#{date}.txt"
315
323
  unless File.exists? filename #download unless there already is a file
316
324
  #call the java program and fetch the file
317
- e = `java -cp #{classpath} Autoingestion #{username} #{password} #{vendorId} Sales Daily Summary #{date}`
325
+ e = `java -cp #{classpath} Autoingestion #{@username} #{@password} #{@vendorId} Sales Daily Summary #{date}`
318
326
  report_file = e.split("\n").first
319
327
  if File.exists? report_file
320
328
  f = `gzip -df #{report_file}`
@@ -322,13 +330,11 @@ elsif ARGV[0] == "get:daily"
322
330
  puts "#{e}\n".red
323
331
  end
324
332
  end
325
-
326
- end # 91.each
327
-
333
+ end # 1..14.each
334
+ #-----------------------------------------------------------------------------------------------------------------------------
328
335
  elsif ARGV[0] == "get:weekly"
329
-
336
+ #-----------------------------------------------------------------------------------------------------------------------------
330
337
  # Weekly reports are available only for past 13 weeks, please enter a weekend date within past 13 weeks.
331
-
332
338
  first_date = Date.today
333
339
  (1..13).each do |i| #13 weeks
334
340
  date = (first_date - i*7)
@@ -337,10 +343,10 @@ elsif ARGV[0] == "get:weekly"
337
343
  next_sunday = date + day_increment
338
344
  formatted_date = next_sunday.to_s.gsub('-', '')
339
345
  puts "\nGetting Weekly Sales Report for #{formatted_date}\n"
340
- filename = "S_W_#{vendorId}_#{formatted_date}.txt"
346
+ filename = "S_W_#{@vendorId}_#{formatted_date}.txt"
341
347
  unless File.exists? filename #download unless there already is a file
342
348
  #call the java program and fetch the file
343
- e = `java -cp #{classpath} Autoingestion #{username} #{password} #{vendorId} Sales Weekly Summary #{formatted_date}`
349
+ e = `java -cp #{classpath} Autoingestion #{@username} #{@password} #{@vendorId} Sales Weekly Summary #{formatted_date}`
344
350
  report_file = e.split("\n").first
345
351
  if File.exists? report_file
346
352
  f = `gzip -df #{report_file}`
@@ -348,21 +354,20 @@ elsif ARGV[0] == "get:weekly"
348
354
  puts "#{e}\n".red
349
355
  end
350
356
  end
351
- end # 13.each
352
-
357
+ end # 1..13.each
358
+ #-----------------------------------------------------------------------------------------------------------------------------
353
359
  else
360
+ #-----------------------------------------------------------------------------------------------------------------------------
354
361
  # no argument or date in format YYYYMMDD
355
362
  #get sales report for date
356
-
357
363
  @date = ARGV[0]
358
364
  date = (Date.today - 1).to_s.gsub('-', '')
359
365
  date = @date if ARGV[0]
360
366
  puts "\nDaily Sales Report for #{date}\n"
361
-
362
- filename = "S_D_#{vendorId}_#{date}.txt"
367
+ filename = "S_D_#{@vendorId}_#{date}.txt"
363
368
  unless File.exists? filename #download unless there already is a file
364
369
  #call the java program and fetch the file
365
- e = `java -cp #{classpath} Autoingestion #{username} #{password} #{vendorId} Sales Daily Summary #{date}`
370
+ e = `java -cp #{classpath} Autoingestion #{@username} #{@password} #{@vendorId} Sales Daily Summary #{date}`
366
371
  report_file = e.split("\n").first
367
372
  if File.exists? report_file
368
373
  f = `gzip -df #{report_file}`
@@ -370,11 +375,12 @@ else
370
375
  puts "#{e}\n".red
371
376
  end
372
377
  end
373
-
378
+ compute_and_present([filename])
379
+ =begin
374
380
  if File.exists? filename #only if there is data
375
381
  #calculate totals
376
382
  report_data = File.open(filename, "rb").read
377
- report = SalesReportParser.parse(report_data)
383
+ report = parse(report_data)
378
384
  apps = {}
379
385
  total_payed_units = 0
380
386
  total_inapp_units = 0
@@ -418,4 +424,6 @@ else
418
424
  puts "#{'Updates'.ljust(40).dark_green}: #{total_updated_units}"
419
425
  puts "\n"
420
426
  end
427
+ =end
428
+ #-----------------------------------------------------------------------------------------------------------------------------
421
429
  end
data/sales.yml CHANGED
@@ -2,3 +2,5 @@
2
2
  :username: YouriTunesUsername
3
3
  :password: YouriTunesPassword
4
4
  :vendorId: 80076733
5
+ :convertTo: EUR
6
+ :beVerbose: YES
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: sales
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.2
5
+ version: 0.0.3
6
6
  platform: ruby
7
7
  authors:
8
8
  - Mihael
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2012-07-07 00:00:00 Z
13
+ date: 2012-07-08 00:00:00 Z
14
14
  dependencies: []
15
15
 
16
16
  description: iTunes Connect Command Line Autoingestion Script. Computes and presents totals. Uses Autoingestion.class for report downloading.