http://www.mediawiki.org/wiki/Special:Code/MediaWiki/82951
Revision: 82951
Author: rfaulk
Date: 2011-02-28 20:15:30 +0000 (Mon, 28 Feb 2011)
Log Message:
-----------
Adding reporting functionality to track major gift donors.
Modified Paths:
--------------
trunk/fundraiser-statistics/fundraiser-scripts/fundraiser_reporting.py
trunk/fundraiser-statistics/fundraiser-scripts/test_reporting.py
Added Paths:
-----------
trunk/fundraiser-statistics/bash/report_major_gifts.sh
trunk/fundraiser-statistics/fundraiser-scripts/mine_contacts.py
trunk/fundraiser-statistics/fundraiser-scripts/sql/report_ecomm_by_amount.sql
trunk/fundraiser-statistics/fundraiser-scripts/sql/report_ecomm_by_contact.sql
Added: trunk/fundraiser-statistics/bash/report_major_gifts.sh
===================================================================
--- trunk/fundraiser-statistics/bash/report_major_gifts.sh
(rev 0)
+++ trunk/fundraiser-statistics/bash/report_major_gifts.sh 2011-02-28
20:15:30 UTC (rev 82951)
@@ -0,0 +1,8 @@
+#!/bin/bash
+
+cd /home/rfaulk/fundraiser-statistics/fundraiser-scripts
+
+python mine_contacts.py c
+
+cp
/home/rfaulk/fundraiser-statistics/fundraiser-scripts/html/report_ecomm_by_contact.html
/srv/org.wikimedia.fundraising/stats/
+
Modified: trunk/fundraiser-statistics/fundraiser-scripts/fundraiser_reporting.py
===================================================================
--- trunk/fundraiser-statistics/fundraiser-scripts/fundraiser_reporting.py
2011-02-28 19:47:39 UTC (rev 82950)
+++ trunk/fundraiser-statistics/fundraiser-scripts/fundraiser_reporting.py
2011-02-28 20:15:30 UTC (rev 82951)
@@ -25,43 +25,12 @@
import query_store as qs
import miner_help as mh
-"""
-CLASS :: ^FundraiserReporting^
-
-Base class for reporting fundraiser analytics. Methods that are intended to
be extended in derived classes include:
-
-run_query()
-run()
-gen_plot()
-publish_google_sheet()
-write_to_html_table()
-
-
-"""
-class FundraiserReporting:
-
- # Database and Cursor objects
- db = None
- cur = None
+class TimestampProcesser(object):
- def init_db(self):
- """ Establish connection """
- #db = MySQLdb.connect(host='db10.pmtpa.wmnet', user='rfaulk',
db='faulkner')
- #self.db = MySQLdb.connect(host='127.0.0.1', user='rfaulk',
db='faulkner', port=3307)
- self.db = MySQLdb.connect(host='storage3.pmtpa.wmnet',
user='rfaulk', db='faulkner')
-
- """ Create cursor """
- self.cur = self.db.cursor()
-
- def close_db(self):
- self.cur.close()
- self.db.close()
-
-
"""
- Takes as input and converts it to a set of hours counting back
from 0
+ Takes a list of timestamps as input and converts it to a set of
hours counting back from 0
time_lists - a list of timestamp lists
time_norm - a dictionary of normalized times
@@ -206,6 +175,44 @@
return [start_time, end_time]
+
+
+"""
+
+CLASS :: ^FundraiserReporting^
+
+Base class for reporting fundraiser analytics. Methods that are intended to
be extended in derived classes include:
+
+run_query()
+run()
+gen_plot()
+publish_google_sheet()
+write_to_html_table()
+
+
+"""
+class FundraiserReporting(TimestampProcesser):
+
+ # Database and Cursor objects
+ db = None
+ cur = None
+
+ def init_db(self):
+ """ Establish connection """
+ #db = MySQLdb.connect(host='db10.pmtpa.wmnet', user='rfaulk',
db='faulkner')
+ #self.db = MySQLdb.connect(host='127.0.0.1', user='rfaulk',
db='faulkner', port=3307)
+ self.db = MySQLdb.connect(host='storage3.pmtpa.wmnet',
user='rfaulk', db='faulkner')
+
+ """ Create cursor """
+ self.cur = self.db.cursor()
+
+ def close_db(self):
+ self.cur.close()
+ self.db.close()
+
+
+
+
"""
def smooth::
Added: trunk/fundraiser-statistics/fundraiser-scripts/mine_contacts.py
===================================================================
--- trunk/fundraiser-statistics/fundraiser-scripts/mine_contacts.py
(rev 0)
+++ trunk/fundraiser-statistics/fundraiser-scripts/mine_contacts.py
2011-02-28 20:15:30 UTC (rev 82951)
@@ -0,0 +1,264 @@
+
+"""
+
+mine_contacts.py
+
+wikimediafoundation.org
+Ryan Faulkner
+February 11th, 2010
+
+"""
+
+# =============================================
+# Pulls metrics from database to perform statistical analysis
+# =============================================
+
+import sys
+import getopt
+import re
+import datetime
+
+import MySQLdb
+import HTML
+
+import test_reporting as tr
+import miner_help as mh
+import query_store as qs
+import fundraiser_reporting as fr
+
+
+
+class contact_handler(tr.data_handler, fr.TimestampProcesser):
+
+ __query_obj = qs.query_store()
+ __file_name = './csv/Rebeccas_Contacts_Donation_Alerts_mod.csv'
+ __sql_path = './sql/'
+ __html_path = './html/'
+ __query_handle_contact = 'report_ecomm_by_contact'
+ __query_handle_amount = 'report_ecomm_by_amount'
+
+
+
+ def __init__(self):
+ super(tr.data_handler, self).__init__()
+
+ """
+
+ method :: parse_contacts:
+
+ Constants:
+ =========
+
+ file_name = ./csv/Rebeccas_Contacts_Donation_Alerts.csv
+
+ """
+
+ def parse_contacts(self):
+
+ # Initialization - open the file
+ # FileName = sys.argv[1];
+ if (re.search('\.gz',self.__file_name)):
+ file_obj = gzip.open(self.__file_name, 'r')
+ else:
+ file_obj = open(self.__file_name, 'r')
+
+
+ # Sample from csv file
+ # Internal Contact ID, Sort Name, First Name, Last
Name, Contact Type
+ # 120842, "Abramowicz, David", David, Abramowicz,
Individual
+ index_first_name = 3
+ index_last_name = 4
+ contact_list = list()
+
+
+ # PROCESS LOG FILE
+ # ================
+ line = file_obj.readline()
+ while (line != ''):
+
+ lineArgs = line.split(',')
+ # print lineArgs[index_first_name] + ' ' +
lineArgs[index_last_name]
+ contact_list.append([lineArgs[index_first_name],
lineArgs[index_last_name]])
+ line = file_obj.readline()
+
+ return contact_list
+
+
+ """
+
+ Civi Reporting - Create a table from a list of contacts
+
+ This method looks at
+
+ Constants:
+ =========
+
+ file_name = ./sql/Rebeccas_Contacts_Donation_Alerts.csv
+
+
+ """
+ def build_html_table_by_contact(self):
+
+
+ # get the contacts from the list
+ list = self.parse_contacts()
+
+ table_data = []
+ sql_stmnt = mh.read_sql(self.__sql_path +
self.__query_handle_contact + '.sql');
+
+ # open the html files for writing
+ f = open(self.__html_path + self.__query_handle_contact +
'.html', 'w')
+
+ # construct contact where string
+ # iterate through the list
+ where_str = 'where '
+ first_item = 0
+ for i in list[1:]:
+
+ # only process People now - not organizations or
Households
+ if not(re.search('Organization', i[0]) or
re.search('Organization', i[1]) or re.search('Household', i[0]) or
re.search('Household', i[1])):
+ if not(first_item):
+ first_item = 1
+ else:
+ where_str = where_str + ' or '
+
+ where_str = where_str + '(first_name = \'' +
i[0].strip() + '\' and last_name = \'' + i[1].strip() + '\')'
+
+ # Formats the statement according to query type
+ select_stmnt =
self.__query_obj.format_query(self.__query_handle_contact, sql_stmnt,
[where_str])
+ print select_stmnt
+
+ # initialize the db and execute the query
+ self.init_db()
+
+ try:
+
+ # execute statement gathering amounts
+ err_msg = select_stmnt
+ self._cur.execute(select_stmnt)
+ results = self._cur.fetchall()
+
+ for row in results:
+ cpRow = self.listify(row)
+ table_data.append(cpRow)
+
+ except:
+ self._db.rollback()
+ sys.exit("Database Interface Exception:\n" + err_msg)
+
+ self.close_db()
+
+ # Construct the html table
+ header =
self.__query_obj.get_query_header(self.__query_handle_contact)
+ t = HTML.table(table_data, header_row=header)
+ htmlcode = str(t)
+
+ f.write(htmlcode)
+ f.close()
+
+ return htmlcode
+
+
+
+ """
+
+ Civi Reporting - Create a table from a list of contacts
+ Creates two html tables
+
+ """
+ def build_html_table_by_amount(self):
+
+
+ # Initialize times
+ now = datetime.datetime.now()
+ hours_back = 24 * 7
+
+ start_time, end_time = self.gen_date_strings_hr(now, hours_back)
+
+ # Prepare SQL statements and tables
+ table_data = []
+ sql_stmnt = mh.read_sql(self.__sql_path +
self.__query_handle_amount + '.sql');
+
+ # open the html files for writing
+ f = open(self.__html_path + self.__query_handle_amount +
'.html', 'w')
+
+ # Formats the statement according to query type
+ select_stmnt =
self.__query_obj.format_query(self.__query_handle_amount, sql_stmnt,
[start_time, end_time])
+
+ # initialize the db and execute the query
+ self.init_db()
+
+ try:
+
+ # execute statement gathering amounts
+ err_msg = select_stmnt
+ self._cur.execute(select_stmnt)
+ results = self._cur.fetchall()
+
+ for row in results:
+ cpRow = self.listify(row)
+ table_data.append(cpRow)
+
+
+ except:
+ self._db.rollback()
+ sys.exit("Database Interface Exception:\n" + err_msg)
+
+ self.close_db()
+
+ # Construct the html table
+ header =
self.__query_obj.get_query_header(self.__query_handle_amount)
+ t = HTML.table(table_data, header_row=header)
+ htmlcode = str(t)
+
+ f.write(htmlcode)
+ f.close()
+
+ return htmlcode
+
+
+
+"""
+
+method :: main
+
+to parse contact names invoke:
+
+rfaulkner@wmf128:~/trunk/projects/fundraiser-statistics/fundraiser-scripts$
python mine_contacts.py
/home/rfaulkner/trunk/docs/Rebeccas_Contacts_Donation_Alerts.csv
+
+"""
+
+class Usage(Exception):
+ def __init__(self, msg):
+ self.msg = msg
+
+def main(argv=None):
+ if argv is None:
+ argv = sys.argv
+ try:
+ try:
+ opts, args = getopt.getopt(argv[1:], "h", ["help"])
+ except getopt.error, msg:
+ raise Usage(msg)
+
+ # more code, unchanged
+ except Usage, err:
+ print >>sys.stderr, err.msg
+ print >>sys.stderr, "for help use --help"
+ return 2
+
+ contact_handler_obj = contact_handler()
+
+ # Construct HTML table - conditioned on input
+ if len(args) > 0:
+ if args[0] == 'c':
+ contact_handler_obj.build_html_table_by_contact()
+ elif args[0] == 'a':
+ contact_handler_obj.build_html_table_by_amount()
+ else:
+ print 'Invalid option: enter "c" for civi contacts or
"a" for amounts'
+ else:
+ print 'Invalid args: need at least one argument; enter "c" for
civi contacts or "a" for amounts'
+
+if __name__ == "__main__":
+ sys.exit(main())
Added:
trunk/fundraiser-statistics/fundraiser-scripts/sql/report_ecomm_by_amount.sql
===================================================================
---
trunk/fundraiser-statistics/fundraiser-scripts/sql/report_ecomm_by_amount.sql
(rev 0)
+++
trunk/fundraiser-statistics/fundraiser-scripts/sql/report_ecomm_by_amount.sql
2011-02-28 20:15:30 UTC (rev 82951)
@@ -0,0 +1,30 @@
+
+
+select
+
+ecomm.stamp as stamp,
+civicrm.civicrm_contact.first_name as first_name,
+civicrm.civicrm_contact.last_name as last_name,
+civicrm.civicrm_country.name as name,
+civicrm.civicrm_country.iso_code as iso_code,
+converted_amount as amount
+
+
+from
+(
+select
+DATE_FORMAT(ts, '%sY-%sm-%sd %sH') as stamp,
+contribution_tracking.contribution_id,
+converted_amount,
+contact_id
+
+from drupal.contribution_tracking left join civicrm.public_reporting on
contribution_tracking.contribution_id = civicrm.public_reporting.contribution_id
+where ts > '%s' and ts <= '%s'
+) as ecomm
+
+join civicrm.civicrm_contact on ecomm.contact_id = civicrm.civicrm_contact.id
+join civicrm.civicrm_address on civicrm.civicrm_contact.id =
civicrm.civicrm_address.contact_id
+join civicrm.civicrm_country on civicrm.civicrm_address.country_id =
civicrm.civicrm_country.id
+
+where ecomm.converted_amount >= 100
+group by 1;
Added:
trunk/fundraiser-statistics/fundraiser-scripts/sql/report_ecomm_by_contact.sql
===================================================================
---
trunk/fundraiser-statistics/fundraiser-scripts/sql/report_ecomm_by_contact.sql
(rev 0)
+++
trunk/fundraiser-statistics/fundraiser-scripts/sql/report_ecomm_by_contact.sql
2011-02-28 20:15:30 UTC (rev 82951)
@@ -0,0 +1,29 @@
+
+
+select
+
+ecomm.stamp as stamp,
+civicrm.civicrm_contact.first_name as first_name,
+civicrm.civicrm_contact.last_name as last_name,
+civicrm.civicrm_country.name as name,
+civicrm.civicrm_country.iso_code as iso_code,
+converted_amount as amount
+
+
+from
+(
+select
+DATE_FORMAT(ts, '%sY-%sm-%sd %sH') as stamp,
+contribution_tracking.contribution_id,
+converted_amount,
+contact_id
+
+from drupal.contribution_tracking left join civicrm.public_reporting on
contribution_tracking.contribution_id = civicrm.public_reporting.contribution_id
+) as ecomm
+
+join civicrm.civicrm_contact on ecomm.contact_id = civicrm.civicrm_contact.id
+join civicrm.civicrm_address on civicrm.civicrm_contact.id =
civicrm.civicrm_address.contact_id
+join civicrm.civicrm_country on civicrm.civicrm_address.country_id =
civicrm.civicrm_country.id
+
+%s group by 1;
+
Modified: trunk/fundraiser-statistics/fundraiser-scripts/test_reporting.py
===================================================================
--- trunk/fundraiser-statistics/fundraiser-scripts/test_reporting.py
2011-02-28 19:47:39 UTC (rev 82950)
+++ trunk/fundraiser-statistics/fundraiser-scripts/test_reporting.py
2011-02-28 20:15:30 UTC (rev 82951)
@@ -22,7 +22,7 @@
import query_store as qs
-
+
def build_html_table_from_list(db, cur, header, sql_path, html_path,
query_type, start_time, end_time):
query_obj = qs.query_store()
@@ -88,12 +88,52 @@
return htmlcode
-# workaround for issue with tuple objects in HTML.py
-# MySQLdb returns unfamiliar tuple elements from its fetchall method
-# this is probably a version problem since the issue popped up in 2.5 but not
2.6
-def listify(row):
- l = []
- for i in row:
- l.append(i)
- return l
+
+class data_handler(object):
+
+ _db = None
+ _cur = None
+
+ def __init__(self):
+ pass
+
+ """
+
+ INITIALIZE DB ACCESS
+
+ """
+ def init_db(self):
+
+ """ Establish connection """
+ # self._db = MySQLdb.connect(host='db10.pmtpa.wmnet',
user='rfaulk', db='faulkner')
+ # self._db = MySQLdb.connect(host='127.0.0.1', user='rfaulk',
db='faulkner', port=3307)
+ self._db = MySQLdb.connect(host='storage3.pmtpa.wmnet',
user='rfaulk', db='faulkner')
+
+ """ Create cursor """
+ self._cur = self._db.cursor()
+
+
+ """
+
+ Close the db connection
+
+ """
+ def close_db(self):
+ self._cur.close()
+ self._db.close()
+
+ """
+
+ workaround for issue with tuple objects in HTML.py
+ MySQLdb returns unfamiliar tuple elements from its fetchall method
+ this is probably a version problem since the issue popped up in 2.5 but
not 2.6
+
+ """
+ def listify(self, row):
+ l = []
+ for i in row:
+ l.append(i)
+ return l
+
+
_______________________________________________
MediaWiki-CVS mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-cvs