Awight has uploaded a new change for review.

  https://gerrit.wikimedia.org/r/300090

Change subject: Snapshot of pcoombe's campaign statistics tools
......................................................................

Snapshot of pcoombe's campaign statistics tools

Note that this is not currently the authoritative source.  You might find
more recent code in staging:/srv/br/, in which case we should re-sync.

Change-Id: I4de528cf5b7f6cbe391ec9a427b1a3b54a61799a
---
A stats/fr_util.py
A stats/methods.py
2 files changed, 225 insertions(+), 0 deletions(-)


  git pull ssh://gerrit.wikimedia.org:29418/wikimedia/fundraising/tools 
refs/changes/90/300090/1

diff --git a/stats/fr_util.py b/stats/fr_util.py
new file mode 100644
index 0000000..939732d
--- /dev/null
+++ b/stats/fr_util.py
@@ -0,0 +1,58 @@
+"""Fundraising utility belt
+"""
+
+import MySQLdb
+
+FORMAT_CHOICES = ['tsv', 'csv', 'pretty', 'mediawiki']
+
+def get_db_cursor(type='default'):
+    """Get a cursor to the database
+
+    Type can be:
+    - 'default'
+    - 'dict': returns rows as a dictionary
+
+    User must have .my.cnf file configured in their home directory
+    """
+    db = MySQLdb.connect(host='localhost',
+                        unix_socket='/tmp/mysql.sock',
+                        read_default_file="~/.my.cnf")
+    if type == 'dict':
+        cursor = db.cursor(MySQLdb.cursors.DictCursor)
+    else:
+        cursor = db.cursor()
+
+    return cursor
+
+
+def print_table(columns, rows, format):
+    # Put results in a nice table for output
+
+    if format == 'tsv':
+        print '\t'.join(columns)
+        for row in rows:
+            print '\t'.join(row)
+
+    elif format == 'csv':
+        print ', '.join(columns)
+        for row in rows:
+            print ', '.join(row)
+
+    elif format == 'pretty':
+        from prettytable import PrettyTable
+        x = PrettyTable(columns)
+        x.align = 'r'
+        for row in rows:
+            x.add_row(row)
+        print x
+
+    elif format == 'mediawiki':
+        print '{| class="wikitable sortable"'
+        print '|+ Caption'
+        print '|-'
+        print '! ' + ' !! '.join( columns )
+        for row in rows:
+            print '|-'
+            print '! scope="row" | ' + row[0]
+            print '| ' + ' || '.join( row[1:] )
+        print '|}'
diff --git a/stats/methods.py b/stats/methods.py
new file mode 100755
index 0000000..9a87f8b
--- /dev/null
+++ b/stats/methods.py
@@ -0,0 +1,167 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+
+from datetime import datetime
+import dateutil.parser
+import dateutil.relativedelta
+import MySQLdb, sys
+import fr_util
+
+TSFORMAT = '%Y%m%d%H%M%S'
+DATEFORMAT = '%Y-%m-%d %H:%M:%S'
+
+
+def get_options():
+    from argparse import ArgumentParser
+    parser = ArgumentParser(description='Get payment method results')
+
+    # We'll run the date parser on these later
+    parser.add_argument('-s', '--start', dest='start',
+        help='Start time (UTC). If not specified, defaults to one month ago.')
+    parser.add_argument('-e', '--end', dest='end',
+        help='End time (UTC). If not specified, defaults to now.')
+
+    parser.add_argument('--source', dest='source', default='',
+        help='utm_source substring e.g. B1516_0902_en6C_ipd. Can be a regular 
expression.')
+    parser.add_argument('--campaign', dest='campaign', default='',
+        help='utm_campaign substring e.g. C1516_en6C_dsk_FR. Can be a regular 
expression.')
+
+    parser.add_argument('--country', dest='country', default='',
+        help='Filter to country ISO code e.g. US')
+    parser.add_argument('--language', dest='language', default='',
+        help='Filter to language ISO code e.g. en')
+
+    parser.add_argument('--format', dest='format', default='tsv',
+        choices=fr_util.FORMAT_CHOICES,
+        help='Output format. For example "mediawiki" gives a table suitable 
for posting on wiki.')
+
+    parser.add_argument('--sql', action='store_true',
+        help='Show SQL used (for debugging)')
+
+    if len(sys.argv) == 1:
+        # No arguments, show instructions
+        parser.print_help()
+        sys.exit(1)
+
+    args = vars(parser.parse_args())
+    return args
+
+
+def build_query():
+    where = "ts BETWEEN %(start_ts)s AND %(end_ts)s"
+    if args['source']:
+        where += " AND utm_source REGEXP %(source)s"
+    if args['campaign']:
+        where += " AND utm_campaign REGEXP %(campaign)s"
+    if args['country']:
+        where += " AND country=%(country)s"
+    if args['language']:
+        where += " AND language=%(language)s"
+
+    query = """
+SELECT
+    SUBSTRING_INDEX(ct.utm_source, '.', -1) AS method,
+    SUM(cc.id IS NOT NULL) AS donations,
+    SUM(cc.id IS NOT NULL) / total.donations AS donations_pct,
+    COUNT(ct.id) AS clicks,
+    COUNT(ct.id) / total.clicks AS clicks_pct,
+    SUM(cc.id IS NOT NULL) / COUNT(ct.id) AS conversion,
+    SUM(cc.total_amount) AS amount,
+    SUM(cc.total_amount) / total.amount AS amount_pct,
+    SUM(cc.total_amount) / SUM(cc.id IS NOT NULL) AS avg
+FROM
+    drupal.contribution_tracking ct
+    LEFT JOIN civicrm.civicrm_contribution cc on ct.contribution_id = cc.id,
+    (
+        SELECT
+            SUM(cc.id IS NOT NULL) AS donations,
+            COUNT(ct.id) AS clicks,
+            SUM(cc.total_amount) AS amount
+        FROM
+            drupal.contribution_tracking ct
+            LEFT JOIN civicrm.civicrm_contribution cc on ct.contribution_id = 
cc.id
+        WHERE """ + where + """
+    ) AS total
+WHERE
+    """ + where + """
+GROUP BY method;
+    """
+    return query
+
+
+def get_data():
+    # Gets the raw results from the database
+    cursor.execute(query, args)
+    data = cursor.fetchall()
+    return data
+
+
+def format_results(data):
+    # Format results nicely and bundle into lists.
+    num = '{:,}' # number with thousands separator
+    usd = '${:,.2f}'
+    pct = '{:.2%}'
+    columns = ['method', 'donations', 'donations%', 'clicks', 'clicks%', 
'amount', 'amount%', 'avg', 'conversion']
+    results = []
+    for i in data:
+
+        # Change any None to 0 so formatter doesn't choke
+        for x in i:
+            if i[x] == None:
+                i[x] = 0
+
+        row = [
+            str( i['method'] ),
+            num.format( i['donations'] ),
+            pct.format( i['donations_pct'] ),
+            num.format( i['clicks'] ),
+            pct.format( i['clicks_pct'] ),
+            usd.format( i['amount'] ),
+            pct.format( i['amount_pct'] ),
+            usd.format( i['avg'] ),
+            pct.format( i['conversion'] )
+        ]
+        results.append(row)
+    return columns, results
+
+
+if __name__ == '__main__':
+    args = get_options()
+
+    if args['start']:
+        args['start'] = dateutil.parser.parse(args['start'])
+    else:
+        # default to 1 month ago
+        args['start'] = (datetime.now() + 
dateutil.relativedelta.relativedelta(months = -1))
+
+    if args['end']:
+        args['end'] = dateutil.parser.parse(args['end'])
+    else:
+        # default to now
+        args['end'] = datetime.now()
+
+    # Timestamp format for queries
+    args['start_ts'] = args['start'].strftime(TSFORMAT)
+    args['end_ts']   = args['end'].strftime(TSFORMAT)
+
+    cursor = fr_util.get_db_cursor(type='dict')
+    query  = build_query()
+    data   = get_data()
+    if args['sql']:
+        print cursor._last_executed
+
+    print "From: " + args['start'].strftime(DATEFORMAT)
+    print "To: "   + args['end'].strftime(DATEFORMAT)
+
+    print """
+Campaign filter:   {campaign}
+utm_source filter: {source}
+Country filter:    {country}
+Language filter:   {language}
+    """.format(**args)
+    
+    columns, results = format_results(data)
+
+    fr_util.print_table(columns, results, args['format'])
+    print
+

-- 
To view, visit https://gerrit.wikimedia.org/r/300090
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I4de528cf5b7f6cbe391ec9a427b1a3b54a61799a
Gerrit-PatchSet: 1
Gerrit-Project: wikimedia/fundraising/tools
Gerrit-Branch: master
Gerrit-Owner: Awight <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to