Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

fixed chart, added chart handler, allow for downloading older reports

  • Loading branch information...
commit e9436ffb36423643a9f0d30d6054180dd96235a3 1 parent 7bf68b5
Greg Allen authored Basil Shkara committed
View
141 chart.py
@@ -0,0 +1,141 @@
+import sys
+import settings
+import models.data
+import datetime
+from google.appengine.ext import db
+sys.path.insert(0, settings.APP_ROOT_DIR + '/lib')
+from graphy.backends import google_chart_api
+from graphy import formatters
+from graphy import line_chart
+
+class SalesChart(object):
+ def units_chart(self, pid):
+ overall_chart = google_chart_api.LineChart()
+
+ sales_query = db.Query(models.data.Sale)
+ sales_query.filter('pid =', pid)
+ sales_query.order('report_date')
+ sales = []
+ for sale in sales_query:
+ sales.append([sale.income_units, sale.report_date])
+
+ if len(sales) == 0: return (None, None)
+ sales, dates = zip(*sales)
+
+ # Make dates readable
+ dates = [date.strftime('%d %b') for date in dates]
+
+ # Add sales line
+ overall_chart.AddLine(sales, width=line_chart.LineStyle.THICK, label='Sales')
+
+ # Determine if an upgrades line needs to be drawn
+ sales_start = sales_query.get().report_date
+ # Use settings file as the definitive source of upgrade start date because iTunes Connect sometimes reports false upgrade numbers
+ versions = settings.PRODUCTS[pid]['versions']
+ upgrades = []
+ if len(versions) > 1:
+ # Convert to datetime to allow for timedelta calculation
+ upgrades_start = datetime.datetime.combine(versions[1]['date'], datetime.time(sales_start.hour, sales_start.minute))
+ difference_in_days = (upgrades_start - sales_start).days
+
+ upgrades_query = db.Query(models.data.Upgrade)
+ upgrades_query.filter('pid =', pid)
+ upgrades_query.order('report_date')
+ upgrades_query.filter('report_date >', upgrades_start)
+
+ # Pad upgrades list with time before upgrade commenced
+ for i in range(0, difference_in_days):
+ upgrades.append(0)
+ for upgrade in upgrades_query:
+ upgrades.append(upgrade.income_units)
+ # Add upgrades line
+ overall_chart.AddLine(upgrades, width=line_chart.LineStyle.THICK, label='Upgrades')
+
+ # Add horizontal labels
+ max_num_horizontal_labels = 15
+ segment_gap = 1
+ if len(dates) > max_num_horizontal_labels:
+ segment_gap = len(dates) / max_num_horizontal_labels
+
+ overall_chart.bottom.min = 0
+ overall_chart.bottom.max = max_num_horizontal_labels
+ overall_chart.bottom.labels = dates
+ overall_chart.bottom.labels = dates[::segment_gap]
+
+ # Add vertical labels
+ max_num_vertical_labels = 15
+
+ max_sales = 0
+ min_sales = 0
+ max_upgrades = 0
+ min_upgrades = 0
+ if sales:
+ max_sales = max(sales)
+ min_sales = min(sales)
+ if upgrades:
+ max_upgrades = max(upgrades)
+ min_upgrades = min(upgrades)
+
+ overall_chart.left.max = max_upgrades if max_upgrades > max_sales else max_sales
+ overall_chart.left.min = min_upgrades if min_upgrades < min_sales else min_sales
+ vertical_labels = []
+ segment_gap = overall_chart.left.max / max_num_vertical_labels
+ for i in range(0, max_num_vertical_labels + 1):
+ vertical_labels.append(i * segment_gap)
+ if len(vertical_labels) == max_num_vertical_labels + 1: break
+
+ overall_chart.left.labels = vertical_labels
+ overall_chart.bottom.label_gridlines = True
+
+ # Build concentrated chart if there is enough data for one
+ concentrated_chart = self.concentrated_units_chart(sales, upgrades, dates)
+ if concentrated_chart != None:
+ concentrated_chart = concentrated_chart.display.Url(1000, 300)
+
+ return (overall_chart.display.Url(1000, 300), concentrated_chart)
+
+ def concentrated_units_chart(self, sales, upgrades, dates):
+ # Want results for the last 2 weeks
+ concentrated_result_set_num = 14
+ concentrated_chart = None
+ if len(sales) > concentrated_result_set_num:
+ concentrated_chart = google_chart_api.LineChart()
+ # Slice to create the line for the concentrated chart
+ calc_concentrated_result_set = lambda x: x[len(sales) - concentrated_result_set_num :len(sales)]
+ sales_concentrated = calc_concentrated_result_set(sales)
+ dates_concentrated = calc_concentrated_result_set(dates)
+ upgrades_concentrated = calc_concentrated_result_set(upgrades)
+
+ concentrated_chart.AddLine(sales_concentrated, width=line_chart.LineStyle.THICK, label='Sales')
+ if len(upgrades_concentrated) == concentrated_result_set_num - 1:
+ concentrated_chart.AddLine(upgrades_concentrated, width=line_chart.LineStyle.THICK, label='Upgrades')
+
+ concentrated_chart.left.min = 0
+ max_upgrades_concentrated = 0
+ max_sales_concentrated = 0
+ if upgrades_concentrated:
+ max_upgrades_concentrated = max(upgrades_concentrated)
+ if sales_concentrated:
+ max_sales_concentrated = max(sales_concentrated)
+
+ concentrated_chart.left.max = max_upgrades_concentrated if max_upgrades_concentrated > max_sales_concentrated else max_sales_concentrated
+ segment_gap = concentrated_chart.left.max / concentrated_result_set_num
+ concentrated_vertical_labels = []
+
+ for i in range(0, concentrated_result_set_num + 1):
+ concentrated_vertical_labels.append(i * segment_gap)
+ if len(concentrated_vertical_labels) == concentrated_result_set_num + 1: break
+ if concentrated_vertical_labels[-1] < concentrated_chart.left.max:
+ new_max = concentrated_vertical_labels[-1] + segment_gap
+ concentrated_vertical_labels.append(new_max)
+ concentrated_chart.left.max = new_max
+
+ concentrated_chart.left.labels = concentrated_vertical_labels
+ concentrated_chart.bottom.labels = dates_concentrated
+ concentrated_chart.left.label_gridlines = True
+ concentrated_chart.bottom.label_gridlines = True
+ return concentrated_chart
+ else:
+ return None
+
+
View
11 handlers/admin.py
@@ -7,7 +7,7 @@
import tarfile
import settings
from processors import report_persister
-
+from chart import SalesChart
PAGE_NAME = 'Admin'
TEMPLATE_PATH = os.path.join(settings.SETTINGS['template_path'], 'admin.html')
@@ -46,3 +46,12 @@ def post(self):
}
self.response.out.write(template.render(TEMPLATE_PATH, template_values))
+
+class ChartHandler(webapp.RequestHandler):
+ def get(self):
+ pid = self.request.get("pid", None)
+ if not pid:
+ return
+ overall_chart_url, concentrated_chart_url = SalesChart().units_chart(pid)
+
+ self.response.out.write("<img src='%s'/>" % overall_chart_url)
View
137 jobs/email_report.py
@@ -13,13 +13,7 @@
import settings
import models.data
-
-# Append lib path to sys.path for Graphy
-sys.path.insert(0, settings.APP_ROOT_DIR + '/lib')
-from graphy.backends import google_chart_api
-from graphy import formatters
-from graphy import line_chart
-
+from chart import SalesChart
class EmailReport(webapp.RequestHandler):
@@ -104,7 +98,7 @@ def get(self):
rankings.append(dict)
rankings = sorted(rankings, key=lambda k: k['country'])
- overall_chart_url, concentrated_chart_url = self.units_chart(pid)
+ overall_chart_url, concentrated_chart_url = SalesChart().units_chart(pid)
product = {
'name': product_name,
'last_reported_sales_total': last_reported_sales_total,
@@ -146,133 +140,6 @@ def _date_string(self, date):
def _format_number(self, number):
locale.setlocale(locale.LC_ALL,"")
return locale.format('%d', number, True)
-
- def units_chart(self, pid):
- overall_chart = google_chart_api.LineChart()
-
- sales_query = db.Query(models.data.Sale)
- sales_query.filter('pid =', pid)
- sales_query.order('report_date')
- sales = []
- for sale in sales_query:
- sales.append([sale.income_units, sale.report_date])
-
- if len(sales) == 0: return (None, None)
- sales, dates = zip(*sales)
-
- # Make dates readable
- dates = [date.strftime('%d %b') for date in dates]
-
- # Add sales line
- overall_chart.AddLine(sales, width=line_chart.LineStyle.THICK, label='Sales')
-
- # Determine if an upgrades line needs to be drawn
- sales_start = sales_query.get().report_date
- # Use settings file as the definitive source of upgrade start date because iTunes Connect sometimes reports false upgrade numbers
- versions = settings.PRODUCTS[pid]['versions']
- upgrades = []
- if len(versions) > 1:
- # Convert to datetime to allow for timedelta calculation
- upgrades_start = datetime.datetime.combine(versions[1]['date'], datetime.time(sales_start.hour, sales_start.minute))
- difference_in_days = (upgrades_start - sales_start).days
-
- upgrades_query = db.Query(models.data.Upgrade)
- upgrades_query.filter('pid =', pid)
- upgrades_query.order('report_date')
- upgrades_query.filter('report_date >', upgrades_start)
-
- # Pad upgrades list with time before upgrade commenced
- for i in range(0, difference_in_days):
- upgrades.append(0)
- for upgrade in upgrades_query:
- upgrades.append(upgrade.income_units)
-
- # Add upgrades line
- overall_chart.AddLine(upgrades, width=line_chart.LineStyle.THICK, label='Upgrades')
-
- # Add horizontal labels
- max_num_horizontal_labels = 15
- segment_gap = 1
- if len(dates) > max_num_horizontal_labels:
- segment_gap = len(dates) / max_num_horizontal_labels
-
- overall_chart.bottom.min = 0
- overall_chart.bottom.max = max_num_horizontal_labels
- overall_chart.bottom.labels = dates
- overall_chart.bottom.labels = dates[::segment_gap]
-
- # Add vertical labels
- max_num_vertical_labels = 15
- overall_chart.left.min = 0
-
- max_sales = 0
- max_upgrades = 0
- if sales:
- max_sales = max(sales)
- if upgrades:
- max_upgrades = max(upgrades)
-
- overall_chart.left.max = max_upgrades if max_upgrades > max_sales else max_sales
- vertical_labels = []
- segment_gap = overall_chart.left.max / max_num_vertical_labels
- for i in range(0, max_num_vertical_labels + 1):
- vertical_labels.append(i * segment_gap)
- if len(vertical_labels) == max_num_vertical_labels + 1: break
-
- overall_chart.left.labels = vertical_labels
- overall_chart.bottom.label_gridlines = True
-
- # Build concentrated chart if there is enough data for one
- concentrated_chart = self.concentrated_units_chart(sales, upgrades, dates)
- if concentrated_chart != None:
- concentrated_chart = concentrated_chart.display.Url(1000, 300)
-
- return (overall_chart.display.Url(1000, 300), concentrated_chart)
-
- def concentrated_units_chart(self, sales, upgrades, dates):
- # Want results for the last 2 weeks
- concentrated_result_set_num = 14
- concentrated_chart = None
- if len(sales) > concentrated_result_set_num:
- concentrated_chart = google_chart_api.LineChart()
- # Slice to create the line for the concentrated chart
- calc_concentrated_result_set = lambda x: x[len(sales) - concentrated_result_set_num :len(sales)]
- sales_concentrated = calc_concentrated_result_set(sales)
- dates_concentrated = calc_concentrated_result_set(dates)
- upgrades_concentrated = calc_concentrated_result_set(upgrades)
-
- concentrated_chart.AddLine(sales_concentrated, width=line_chart.LineStyle.THICK, label='Sales')
- if len(upgrades_concentrated) == concentrated_result_set_num - 1:
- concentrated_chart.AddLine(upgrades_concentrated, width=line_chart.LineStyle.THICK, label='Upgrades')
-
- concentrated_chart.left.min = 0
- max_upgrades_concentrated = 0
- max_sales_concentrated = 0
- if upgrades_concentrated:
- max_upgrades_concentrated = max(upgrades_concentrated)
- if sales_concentrated:
- max_sales_concentrated = max(sales_concentrated)
-
- concentrated_chart.left.max = max_upgrades_concentrated if max_upgrades_concentrated > max_sales_concentrated else max_sales_concentrated
- segment_gap = concentrated_chart.left.max / concentrated_result_set_num
- concentrated_vertical_labels = []
-
- for i in range(0, concentrated_result_set_num + 1):
- concentrated_vertical_labels.append(i * segment_gap)
- if len(concentrated_vertical_labels) == concentrated_result_set_num + 1: break
- if concentrated_vertical_labels[-1] < concentrated_chart.left.max:
- new_max = concentrated_vertical_labels[-1] + segment_gap
- concentrated_vertical_labels.append(new_max)
- concentrated_chart.left.max = new_max
-
- concentrated_chart.left.labels = concentrated_vertical_labels
- concentrated_chart.bottom.labels = dates_concentrated
- concentrated_chart.left.label_gridlines = True
- concentrated_chart.bottom.label_gridlines = True
- return concentrated_chart
- else:
- return None
-
def send_email(self, pid, subject, email_body):
message = mail.EmailMessage(sender=settings.SETTINGS['admin_email_address'],
subject=subject)
View
38 jobs/pull_report.py
@@ -15,28 +15,32 @@
class ReportJob(webapp.RequestHandler):
def get(self):
+ date = self.request.get("date", None)
# iTunes Connect stores reports with a 1 day delay
- now = datetime.date.today()
- one_day = datetime.timedelta(days=1)
- yesterday = now - one_day
- yesterday = yesterday.strftime('%m/%d/%Y')
-
+ if not date:
+ now = datetime.date.today()
+ one_day = datetime.timedelta(days=1)
+ yesterday = now - one_day
+ date = yesterday.strftime('%m/%d/%Y')
+ print date
# Fetch for all available accounts
for account_name in settings.ACCOUNTS:
- try:
- latest_report = itcscrape.getLastDayReport(settings.ACCOUNTS[account_name]['itunesconnect_username'], settings.ACCOUNTS[account_name]['itunesconnect_password'], yesterday)
- report_persister.persist(latest_report['filename'], latest_report['content'])
- except:
- # Download failed (timeout or report not available yet)
- # Send email to administrator
- message = mail.EmailMessage(sender=settings.SETTINGS['admin_email_address'],
- subject='[ASM] Report job failed for account: ' + account_name)
- message.to = settings.SETTINGS['admin_email_address']
- message.body = 'Failed to download the iTunes Connect sales report for: ' + yesterday
- message.send()
+ #try:
+ latest_report = itcscrape.getLastDayReport(settings.ACCOUNTS[account_name]['itunesconnect_username'], settings.ACCOUNTS[account_name]['itunesconnect_password'], date)
+ report_persister.persist(latest_report['filename'], latest_report['content'])
+ #except:
+ ## Download failed (timeout or report not available yet)
+ ## Send email to administrator
+ #message = mail.EmailMessage(sender=settings.SETTINGS['admin_email_address'],
+ #subject='[ASM] Report job failed for account: ' + account_name)
+ #message.to = settings.SETTINGS['admin_email_address']
+ #message.body = 'Failed to download the iTunes Connect sales report for: ' + date
+ #message.send()
def main():
- application = webapp.WSGIApplication([('/jobs/pull_report', ReportJob)], debug=True)
+ application = webapp.WSGIApplication([
+ ('/jobs/pull_report', ReportJob)
+ ], debug=True)
wsgiref.handlers.CGIHandler().run(application)
if __name__ == '__main__':
View
1  main.py 100755 → 100644
@@ -16,6 +16,7 @@ def main():
application = webapp.WSGIApplication([('/', admin.RootHandler),
('/admin', admin.RootHandler),
('/admin/upload', admin.UploadHandler),
+ ('/admin/chart', admin.ChartHandler),
], debug=True)
wsgiref.handlers.CGIHandler().run(application)
View
8 templates/admin.html
@@ -13,4 +13,12 @@
<input type="submit" value="Upload" />
</form>
+ {% if imported_date %}
+ <h3>Sucessfully imported: {{ imported_date}}</h3>
+ {% endif %}
+ <form action="/jobs/pull_report" method="get">
+ <input type="text" name="date"/>
+ <input type="submit" value="Import"/>
+ </form>
+
{% endblock %}
Please sign in to comment.
Something went wrong with that request. Please try again.