Skip to content

Instantly share code, notes, and snippets.

@MelissaCole
Created April 29, 2015 19:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MelissaCole/eff526fc165891630fc1 to your computer and use it in GitHub Desktop.
Save MelissaCole/eff526fc165891630fc1 to your computer and use it in GitHub Desktop.
original gold accounting script
# before running script, setup SSH tunnel with below bash line
# ssh -f -i /home/scott/Desktop/sf_VMshare/st_privatekey.ssh -p 31337 scott@107.22.176.232 -L 6543:pg-99s1:5432 -N
from collections import deque, defaultdict
from datetime import datetime
import csv
import psycopg2
from sqlalchemy import create_engine
from sqlalchemy import Column, String, DateTime, Integer
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('postgresql://scott@127.0.0.1:6543/authorize', connect_args={'sslmode':'require'})
Base = declarative_base()
Session = sessionmaker(bind=engine)
session = Session()
class GoldTransaction(Base):
__tablename__ = 'reddit_gold'
_charter_end = datetime(2010, 7, 20, 8, 6, 10, 365317, tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=-420, name=None))
# database columns
trans_id = Column(String, primary_key=True)
status = Column(String)
date = Column(DateTime)
payer_email = Column(String)
paying_id = Column(String)
pennies = Column(Integer)
secret = Column(String)
account_id = Column(String)
days = Column(Integer)
subscr_id = Column(String)
DESCRIPTIONS = {
"onetime-self": "One-time subscription purchase for self",
"subscription": "Auto-renewing subscription for self (unknown if initial setup or automatic payment)",
"subscription-new": "New auto-renewing subscription for self",
"subscription-auto": "Automatic recurring payment for subscription",
"creddits": "Creddit purchase",
"gift": "Gift to other user, immediately applied",
"gift-creddit": "Gift to other user, immediately applied (used creddit)",
"giftcode": "Gift code purchase",
"gilding": "Gilding - gift to other user, immediately applied",
"gilding-creddit": "Gilding (used creddit) - gift to other user, immediately applied",
"rg-elves": "redditgifts Elves bundle",
"postcard": "Postcard",
"unknown": "Unknown",
}
overrides = {}
@property
def transaction_id(self):
if 'trans_id' in self.overrides:
return self.overrides['trans_id']
return self.trans_id
@property
def type(self):
if 'type' in self.overrides:
return self.overrides['type']
secret = self.secret or ''
# Statuses to check for first
if self.status == 'expired-promo':
return 'expired-promo'
if self.paying_id == 'bundle':
return 'appsumo-bundle'
# Charter member payments
if self.date <= self._charter_end or secret.startswith('o_'):
return 'charter'
# RG Elves
if self.trans_id.startswith('RG'):
if self.revenue % 399 != 0 and self.revenue % 2999 != 0:
return 'rg-elves'
# Subscriptions
if self.subscr_id:
if secret.startswith('autorenew-'):
# no easy way to tell if a Stripe subscription is new or automatic
if self.subscr_id.startswith('cus_'):
return 'subscription'
else:
return 'subscription-new'
else:
return 'subscription-auto'
# Gift code purchases
if self.status in ('claimed', 'unclaimed') and len(secret) == 10:
return 'giftcode'
# Creddit purchases
if secret.startswith(('creddits-', '{creddits,', 'cr_')):
# some old strange transactions have 0 days
# looks like people experimenting with sending custom amounts
if self.days != 0:
return 'creddits'
# Gifts and Gildings
if self.status == 'gift':
if self.trans_id.endswith('-A'):
trans_type = 'gilding'
else:
trans_type = 'gift'
if self.revenue == 0:
trans_type += '-creddit'
return trans_type
if self.status == 'instagift':
return 'gilding'
if secret.startswith(('{gift,', 'gift-')):
return 'gift'
# One-time purchases
if secret.startswith(('onetime-', '{onetime,')) or not secret:
return 'onetime-self'
# Manual grants via codes
if self.trans_id.startswith('M'):
if secret.startswith(('p_', 'c_')):
return 'postcard'
elif secret.startswith(('el_', 'e_')):
return 'extralife'
return 'other-manual'
return 'unknown'
@property
def description(self):
if 'description' in self.overrides:
return self.overrides['description']
if self.type in self.DESCRIPTIONS:
return self.DESCRIPTIONS[self.type]
return self.type
@property
def payer(self):
if self.type in ('creddits', 'onetime-self', 'subscription-new', 'subscription-auto'):
return self.account_id
return self.paying_id
# used for the trans types that cover 2 rows in the db
@property
def is_split(self):
if self.type in ('gilding', 'gift', 'rg-elves'):
return True
return False
@property
def is_reversed(self):
if 'is_reversed' in self.overrides:
return self.overrides['is_reversed']
if self.status == "reversed":
return True
return False
@property
def processor(self):
if 'processor' in self.overrides:
return self.overrides['processor']
if self.trans_id.startswith('g'):
return 'Google Payments'
elif self.trans_id.startswith('P'):
return 'Paypal'
elif self.trans_id.startswith('RG'):
return 'redditgifts (Balanced)'
elif self.trans_id.startswith('Sch_'):
return 'Stripe'
elif self.trans_id.startswith('C'):
return 'Coinbase'
return 'Unknown'
@property
def revenue(self):
if 'pennies' in self.overrides:
return self.overrides['pennies']
return self.pennies
@property
def months(self):
if self.days % 366 == 0:
return self.days / 366 * 12
elif self.days % 31 == 0:
return self.days / 31
else:
raise ValueError
@classmethod
def get_transactions(cls, start_date=None, end_date=None):
query = session.query(cls)
if start_date:
query = query.filter(cls.date >= start_date)
if end_date:
query = query.filter(cls.date < end_date)
query = query.order_by(cls.date, cls.trans_id)
transactions = query.all()
overrides = {}
for trans in transactions:
if overrides or not trans.is_split:
trans.overrides = overrides
overrides = {}
yield trans
else:
overrides['type'] = trans.type
overrides['pennies'] = trans.pennies
overrides['trans_id'] = trans.trans_id
overrides['processor'] = trans.processor
overrides['description'] = trans.description
overrides['is_reversed'] = trans.is_reversed
class Creddit(object):
def __init__(self, value, processor, purchase_time):
self.value = value
self.processor = processor
self.purchase_time = purchase_time
if self.purchase_time < datetime(2013, 1, 1, tzinfo=psycopg2.tz.FixedOffsetTimezone(offset=-420, name=None)):
self.value = 0
def get_creddit_balances_on(date):
user_creddits = defaultdict(deque)
transactions = GoldTransaction.get_transactions(end_date=date)
for trans in transactions:
if trans.type == 'creddits':
creddit = Creddit(value=trans.revenue / trans.months,
processor=trans.processor,
purchase_time=trans.date)
user_creddits[trans.account_id].extend([creddit] * trans.months)
elif trans.type.endswith('-creddit'):
for _ in xrange(trans.months):
try:
user_creddits[trans.payer].popleft()
except:
break
return user_creddits
def main():
start_date = end_date = None
while not start_date:
try:
date = raw_input('Enter report start date (YYYY-MM-DD): ')
start_date = datetime.strptime(date, '%Y-%m-%d')
except:
print 'Invalid date.'
while not end_date:
try:
date = raw_input('Enter report end date (YYYY-MM-DD) (will not be included): ')
end_date = datetime.strptime(date, '%Y-%m-%d')
except:
print 'Invalid date.'
print 'Determining user creddit balances'
user_creddits = get_creddit_balances_on(start_date)
print 'Outputting report'
filename = ('reddit gold {0} to {1}.csv'
.format(start_date.strftime('%Y-%m-%d'),
end_date.strftime('%Y-%m-%d')))
out_file = open(filename, 'wb')
csvfile = csv.writer(out_file, quoting=csv.QUOTE_ALL)
csvfile.writerow(['Date/time',
'Transaction ID',
'Type',
'Description',
'Amount Billed',
'Value',
'Processor',
'Period (Days)',
'Payer Account',
'Recipient Account'])
transactions = GoldTransaction.get_transactions(start_date, end_date)
for trans in transactions:
value = trans.revenue
if trans.type == 'creddits':
creddit = Creddit(value=trans.revenue / trans.months,
processor=trans.processor,
purchase_time=trans.date)
user_creddits[trans.account_id].extend([creddit] * trans.months)
value = 0
elif trans.type == 'giftcode':
value = 0
elif trans.type.endswith('-creddit'):
# if the user has creddits that we know how they paid for
num_creddits = len(user_creddits[trans.payer])
if num_creddits > 0:
value = sum(c.value for c in user_creddits[trans.payer])
value = value / num_creddits
# if this purchase would use more creddits than we know about
# make sure not to add value for non-existent creddits
value = value * min(num_creddits, trans.months)
# remove the used creddits
for _ in xrange(trans.months):
try:
user_creddits[trans.payer].popleft()
except:
break
csvfile.writerow([trans.date,
trans.transaction_id,
"reversed" if trans.is_reversed else trans.type,
trans.description,
trans.revenue,
value,
trans.processor,
trans.days,
trans.payer,
trans.account_id])
if __name__ == '__main__':
main()
@sheenaisawsum
Copy link

Can I use this on my android device?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment