sales 0.0.2 → 0.0.3

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.
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.