Who likes getting a daily report on the number of downloads they’ve had from iTunes? Me too! Thats why I recently wrote a python script to download those numbers and email me a copy of the results, every day! Woot woot! This tutorial is going to show you how to do it.
iTunesConnect and Reporter
If you go into your iTunesConnect account, and tap on the Sales and Trends icon, you will see the sales your applications have had over the last week or so. You can also see how many downloads were done on a given day. While this is all good, I don’t like to need to log into iTunesConnect every day to view these numbers. I would really just like them to be sent to me… (Lazy! right?). Luckily for me (and us, if you are still reading this post), Apple has provided a way for us to automate this process!
The first thing we need to do is to download their Reporter files (here). This will download a zip file, containing a folder called Reporter. This folder contains two files, Reporter.jar, and Reporter.properties. These files allow us to access and download a report from iTunesConnect. So we need to move this folder to wherever you would like these files to be saved. I simply put the folder in my documents folder… Seemed logical…
Great! Now, the Reporter.jar file requires you to have Java 1.6 or later installed. So make sure that you are up to date on that front. (If you need to download Java, you can do so here). With that bit of house keeping done, we can start having fun!
If you open up your Reporter.properties it should look something like this:
AccessToken= Mode=Normal SalesUrl=https://reportingitc-reporter.apple.com/reportservice/sales/v1 FinanceUrl=https://reportingitc-reporter.apple.com/reportservice/finance/v1
So we see that we need to provide an AccessToken… The rest doesn’t really concern us. But we do need to find that access token.
There are two ways to do this. Perhaps the easiest is via iTunesConnect. To do so, follow these steps:
1) From the homepage, click Sales and Trends. 2) In the upper-left corner, click the pop-up menu and choose Reports. 3) In the upper-right corner, click on the tooltip next to About Reports. 4) Click Generate Access Token. 5) Copy the access token to your clipboard. 6) Paste the access token to the RHS of the equals: AccessToken=#YourTokenHere#
Much of the above instructions comes form Apples help document on using reporter. You can read it here for more information (like how to get your token via command line).
At this point, you should be able to start using Reporter. The first thing we need to do is to get the account number your want to download your numbers from. To do so, open your terminal app of choice, navigate to the Reporter folder, and run the following command:
java -jar Reporter.jar p=Reporter.properties Sales.getAccounts
This should print out a list of companies, or iTunesConnect accounts your have access to, followed by a number:
Company1 LLC, 123456 Company2 LLC, 234567 ...
That account number is what we need!
Next we need to get the vendor number we need. Run the following command in terminal:
java -jar Reporter.jar p=Reporter.properties a=YOUR_ACCOUNT_NUMBER Sales.getVendors
This should print out a list of vender numbers. Now, for me it only showed one number, so I’m not really sure what the difference each vendor number makes… So, if you had more than one vendor number, you may need alter the script to iterate over the vendor numbers… But now we have everything we need to write our python script!
Python Script
Open up your favorite python IDE and create a new file called Reporter.py. Copy the following code into this new file and change out the base_file_path, account_number, and vendor_number values to the values you collected above:
# set current directory to import os base_file_path = "Path To your Reporter file" os.system("cd " + base_file_path) # get a string version of yesterdays date from datetime import date, timedelta yesterday = date.today() - timedelta(1) yesterday_str = yesterday.strftime("%Y%m%d") # request report from itc import subprocess account_number = "Your Account Number" vendor_number = "Your Vender Number" output = subprocess.check_output("java -jar Reporter.jar p=Reporter.properties a=" + account_number + " Sales.getReport " + vendor_number + ", Sales, Summary, Daily, " + yesterday_str, shell=True)
These steps are all pretty clear, we set the current directory to the directory containing the Reporter.jar and Reporter.properties files. Next, we calculate yesterdays date and transform that date to a string. Finally we request from iTunesConnect a report containing the sales information for yesterday.
If you run this code, you should get a .gz file downloaded into your Reporter folder. Double click this and you will get a .txt file. This .txt file contains all the sales information from yesterday. Looking over this information, you might notice multiple lines for the same app. Not quite what we wanted (though I do like summing things up in my head…). So our next step is to parse this document and count the different types if sales each application had. You see, the totals listed are divided by sales type (download, redownload, update, etc) as well as the country it was sold in. As I was only interested in the type of sale, not the country, the following script combines by country, but separates by type. Lets have a look, shall we? Copy the following code below your last line in your Reporter.py file:
# open downloaded itc report file and unzip it. import gzip zip_filename = str(output.decode('UTF-8').split()[-1]) zip_ref = gzip.open(base_file_path + zip_filename, 'r') file_contents = zip_ref.read().decode("UTF-8").split('\n') zip_ref.close() # parse contents # get header row, find desired indices headers = file_contents[0].split('\t') title_index = headers.index('Title') units_index = headers.index('Units') download_type_index = headers.index('Product Type Identifier') # iterate through data rows, getting desired data. new_download_types = ["1", "1E", "1EP", "1EU", "1F", "1T", "F1"] redownload_types = ["3", "3F", "3T", "F3"] update_types = ["7", "7F", "F7"] iap_types = ["IA1-M", "IA1", "IA9", "IA9-M", "IAC", "IAC-M", "IAY", "IAY-M", "FI1"] # drop header row file_contents = file_contents[1:]
So, the first thing we do, is unzip the file and open it. Next, we find the indices for the columns we are interested in. In our case we are only interested in the Title (product name), Units (number of sales), and Product Type Identifier. The Product Type Identifier is an identification code that indicates what type of sale happened. As the next section shows, for example, 1, 1E, 1EP, 1EU, 1F, 1T, and F1 all indicate a new download. Each number indicates a different type of new download, such as Mac, iPad, iPhone, Universal, etc. But again, I’m only interested in a total, so I am throwing them into the same basket. Note that iap_types stands for in-app-purchas types. Next, we drop the header file, since we are done with it.
Awesome! Now paste the following code below the above code (Warning! this should be simplified… it was late and I wanted to get this over with, so I was a little too… copy-paste happy…), :
new_downloads = dict() redownloads = dict() updates = dict() iaps = dict() for row in file_contents: row_array = row.split("\t") if len(row_array) < max(download_type_index, title_index, units_index): break product_title = row_array[title_index] units = int(row_array[units_index]) download_type = row_array[download_type_index] # Downloads if download_type in new_download_types: if product_title in new_downloads: previous_total = new_downloads[product_title] new_downloads[product_title] = previous_total + units else: new_downloads[product_title] = units # redownloads if download_type in redownload_types: if product_title in redownloads: previous_total = redownloads[product_title] redownloads[product_title] = previous_total + units else: redownloads[product_title] = units # Downloads if download_type in update_types: if product_title in new_downloads: previous_total = updates[product_title] updates[product_title] = previous_total + units else: updates[product_title] = units # Downloads if download_type in new_download_types: if product_title in iap_types: previous_total = iaps[product_title] iaps[product_title] = previous_total + units else: iaps[product_title] = units
Basically, we are iterating over each row of the downloaded report and checking its type and adding the unit to the total being stored in the dictionaries created at the top of the code snippet… Thus we will have a dictionary containing the ProductName as the keys, and the total sales for that type (new downloads, updates, etc) as the value.
Tubular, now we have all the information condensed in the format we want. Next we need to email it to ourselves. First we create the message text in HTML from the dictionaries we just created. Again, I was lazy and simply copy and pasted my way through this:
# create email message = '' # "<strong>New Downloads</strong><b />" message += '<p><table style="width:100%"><caption>New Downloads</caption><tr><th>Name</th><th>Units</th></tr>' for key, value in new_downloads.items(): message += '<tr><td>' + key + "</td><td>" + str(value) + '</td></tr>' message += "</table></p><b /><b />" # message += "<strong>New In App Purchases</strong><b />" message += '<p><table style="width:100%"><caption>New In App Purchases</caption><tr><th>Name</th><th>Units</th></tr>' for key, value in iaps.items(): message += '<tr><td>' + key + "</td><td>" + str(value) + '</td></tr>' message += "</table></p><b /><b />" # message += "<strong>Updates</strong><b />" message += '<p><table style="width:100%"><caption>Updates</caption><tr><th>Name</th><th>Units</th></tr>' for key, value in updates.items(): message += '<tr><td>' + key + "</td><td>" + str(value) + '</td></tr>' message += "</table></p><b /><b />" # message += "<strong>Redownloads</strong><b />" message += '<p><table style="width:100%"><caption>Redownloads</caption><tr><th>Name</th><th>Units</th></tr>' for key, value in redownloads.items(): message += '<tr><td>' + key + "</td><td>" + str(value) + '</td></tr>' message += "</table></p><b /><b />"
Now that we have the message text created, lets create the email:
# skipped your comments for readability import smtplib from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText me = "YOUR EMAIL" my_password = r"YOUR EMAIL PASSWORD" you = "YOUR EMAIL... AGAIN" msg = MIMEMultipart('alternative') msg['Subject'] = "Yesterday's Downloads!" msg['From'] = me msg['To'] = you html = '<html><head><style>table { font-family: arial, sans-serif; border-collapse: collapse; width: 100%;}' \ 'td, th {border: 1px solid #dddddd;text-align: left;padding: 8px;}' \ 'tr:nth-child(even) {background-color: #dddddd;}' \ '</style></head><body>' + message + '</body></html>' part2 = MIMEText(html, 'html') msg.attach(part2) # Send the message via gmail's regular server, over SSL - passwords are being sent, afterall s = smtplib.SMTP_SSL('smtp.gmail.com', 465) # uncomment if interested in the actual smtp conversation # s.set_debuglevel(1) # do the smtp auth; sends ehlo if it hasn't been sent already s.login(me, my_password) s.sendmail(me, you, msg.as_string()) s.quit()
First, this is configured to send an email via a gmail account. It may differ depending on your client. Next, I got this after exploring several different SO answers on sending emails from python, but I can’t seem to find it again. (If you know where this is, comment and I’ll link it…).
OK, now that I’ve said that, if you add the info that you need to (email and password info) and run the code you will probably get an error indicating that google will not allow the request. You will probably also get an email from google stating that they blocked a log-in. To get it to work, you need to click on the link in the email to allow less-secure apps to log into your account.
Once you’ve done that, you should be able to run the code and get an email with all your numbers in a pretty HTML table! Woot woot!
Fun, huh? The only thing left to do, is to automate this script so that it runs every day. I used an app called Plisterine. Simply download and install this application. Then, to run our python script, set it to the file selected in the Application/script to launch section, and configure the rest of the options to your liking. I have my launching on a schedule, every day at 11:00 a.m. Hit continue, follow the prompts and, you have got daily download emails coming your way!
TL;DR
- Download and install reporter.
- Download Reporter.py file, and move into your Reporter folder.
- Fill out the missing information in the Reporter.py file.
- (Optional) Automate the running of the Reporter.py file using Plisterine.
Sources
http://help.apple.com/itc/contentreporterguide/en.lproj/static.html#apda86f89da5