Skip to content
This repository was archived by the owner on Jan 25, 2018. It is now read-only.

Commit 7a6adfc

Browse files
author
Andy McKay
committed
Merge pull request #138 from andymckay/921266
revenue log (bug 921266)
2 parents 7d78fb0 + acafc6e commit 7a6adfc

File tree

8 files changed

+112
-17
lines changed

8 files changed

+112
-17
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,5 @@ tmp/*
1818
*~
1919
*.mo
2020
*.key
21+
*.keys
2122
docs/_build

bin/crontab/crontab.tpl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ MAILTO=amo-developers@mozilla.org
77
HOME=/tmp
88

99
# once per day, generate stats log and upload to S3
10-
05 0 * * * %(django)s stats_log
10+
05 0 * * * %(django)s log --type=stats
11+
10 0 * * * %(django)s log --type=revenue
1112

1213
# once per day, clean statuses older than BANGO_STATUSES_LIFETIME setting
1314
35 0 * * * %(django)s clean_statuses

lib/transactions/constants.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,13 @@
4444
STATUSES_CHOICES = invert(STATUSES)
4545
TYPES_CHOICES = invert(TYPES)
4646
SOURCES_CHOICES = invert(SOURCES)
47+
48+
LOG_STATS = 0
49+
LOG_REVENUE = 1
50+
51+
LOGS = {
52+
'stats': LOG_STATS,
53+
'revenue': LOG_REVENUE
54+
}
55+
56+
LOG_CHOICES = invert(LOGS)

lib/transactions/management/commands/stats_log.py renamed to lib/transactions/management/commands/log.py

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import csv
22
import logging
33
import os
4+
import sys
45
import tempfile
56
from datetime import datetime, timedelta
67
from optparse import make_option
78

9+
from lib.transactions import constants
810
from lib.transactions.models import Transaction
911
from solitude.management.commands.push_s3 import push
1012

@@ -14,15 +16,34 @@
1416
log = logging.getLogger('s.transactions')
1517

1618

17-
def generate_log(day, filename):
19+
def generate_log(day, filename, log_type):
1820
out = open(filename, 'w')
1921
writer = csv.writer(out)
2022
next_day = day + timedelta(days=1)
2123
writer.writerow(('version', 'uuid', 'created', 'modified', 'amount',
2224
'currency', 'status', 'buyer', 'seller'))
25+
2326
transactions = Transaction.objects.filter(modified__range=(day, next_day))
24-
for transaction in transactions:
25-
writer.writerow(transaction.for_log())
27+
28+
if log_type == 'stats':
29+
for row in transactions:
30+
row.log.get_or_create(type=constants.LOG_STATS)
31+
writer.writerow(row.for_log())
32+
33+
if log_type == 'revenue':
34+
transactions = transactions.filter(
35+
status__in=(constants.STATUS_COMPLETED, constants.STATUS_CHECKED),
36+
log__type__isnull=True # Ignore already logged transactions.
37+
)
38+
39+
for row in transactions:
40+
obj, created = row.log.get_or_create(type=constants.LOG_REVENUE)
41+
if not created:
42+
# This should never happen, but just in case.
43+
print 'Transaction skipped: {0}'.format(row.uuid)
44+
continue
45+
46+
writer.writerow(row.for_log())
2647

2748

2849
class Command(BaseCommand):
@@ -38,9 +59,18 @@ class Command(BaseCommand):
3859
option_list = BaseCommand.option_list + (
3960
make_option('--date', action='store', type='string', dest='date'),
4061
make_option('--dir', action='store', type='string', dest='dir'),
62+
make_option('--type', action='store', type='string', dest='log_type'),
4163
)
4264

65+
types = ['stats', 'revenue']
66+
4367
def handle(self, *args, **options):
68+
log_type = options['log_type']
69+
if log_type not in self.types:
70+
msg = 'Type not valid, must be one of: %s' % self.types
71+
log.debug(msg)
72+
sys.exit(1)
73+
4474
dir_ = not options['dir']
4575
if dir_:
4676
log.debug('No directory specified, making temp.')
@@ -49,8 +79,9 @@ def handle(self, *args, **options):
4979
yesterday = datetime.today() - timedelta(days=1)
5080
date = (datetime.strptime(options['date'], '%Y-%m-%d')
5181
if options['date'] else yesterday).date()
52-
filename = os.path.join(dir_, date.strftime('%Y-%m-%d') + '.log')
53-
generate_log(date, filename)
82+
filename = os.path.join(dir_, '{0}.{1}.log'.format(
83+
date.strftime('%Y-%m-%d'), log_type))
84+
generate_log(date, filename, log_type)
5485
log.debug('Log generated to: %s', filename)
5586
push(filename)
5687
if not options['dir']:

lib/transactions/models.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,3 +166,11 @@ def time_status_change(sender, **kwargs):
166166
status = constants.STATUSES_INVERTED[obj.status]
167167
statsd.timing('transaction.status.{0}'.format(status),
168168
(obj.modified - obj.created).seconds)
169+
170+
171+
class TransactionLog(Model):
172+
transaction = models.ForeignKey(Transaction, related_name='log')
173+
type = models.IntegerField(choices=constants.LOG_CHOICES)
174+
175+
class Meta(Model.Meta):
176+
db_table = 'transaction_log'

lib/transactions/tests/test_commands.py

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,55 @@
22
from datetime import datetime
33
from tempfile import NamedTemporaryFile
44

5-
from nose.tools import eq_
5+
from nose.tools import eq_, raises
66
import test_utils
77

8+
from lib.transactions import constants
89
from lib.transactions.models import Transaction
9-
from lib.transactions.management.commands.stats_log import generate_log
10+
from lib.transactions.management.commands.log import generate_log
1011
from lib.sellers.tests.utils import make_seller_paypal
1112

1213

1314
class TestLog(test_utils.TestCase):
1415

15-
def test_filter(self):
16-
seller, paypal, product = make_seller_paypal('some:other:uuid')
16+
def setUp(self):
17+
self.name = NamedTemporaryFile().name
18+
seller, paypal, self.product = make_seller_paypal('some:other:uuid')
1719
self.first = Transaction.objects.create(provider=1,
18-
seller_product=product, uuid='uuid')
19-
name = NamedTemporaryFile().name
20-
generate_log(datetime.today(), name)
21-
with open(name, 'rb') as csvfile:
22-
output = csv.reader(csvfile)
23-
eq_(next(output)[0], 'version')
24-
eq_(next(output)[1], 'uuid')
20+
seller_product=self.product, uuid='uuid')
21+
22+
def results(self):
23+
return csv.reader(open(self.name, 'rb'))
24+
25+
def test_filter(self):
26+
generate_log(datetime.today(), self.name, 'stats')
27+
output = self.results()
28+
eq_(next(output)[0], 'version')
29+
eq_(next(output)[1], 'uuid')
30+
31+
@raises(StopIteration)
32+
def test_stats_log(self):
33+
generate_log(datetime.today(), self.name, 'revenue')
34+
output = self.results()
35+
eq_(next(output)[0], 'version')
36+
next(output) # There is no line 1, transaction not written.
37+
38+
def test_stats_log(self):
39+
self.first.status = constants.STATUS_CHECKED
40+
self.first.save()
41+
42+
generate_log(datetime.today(), self.name, 'revenue')
43+
output = self.results()
44+
eq_(next(output)[0], 'version')
45+
eq_(next(output)[1], 'uuid')
46+
47+
@raises(StopIteration)
48+
def test_multiple(self):
49+
self.first.status = constants.STATUS_CHECKED
50+
self.first.log.create(type=constants.LOG_REVENUE)
51+
self.first.save()
52+
53+
generate_log(datetime.today(), self.name, 'revenue')
54+
output = self.results()
55+
eq_(next(output)[0], 'version')
56+
next(output) # There is no line 1, transaction not written.

lib/transactions/tests/test_models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
from nose.tools import eq_, ok_
33

44
from django.core.exceptions import ValidationError
5+
from test_utils import TestCase
56

67
from lib.sellers.models import Seller, SellerPaypal, SellerProduct
78
from lib.transactions import constants
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CREATE TABLE `transaction_log` (
2+
`id` int(11) unsigned AUTO_INCREMENT NOT NULL PRIMARY KEY,
3+
`created` datetime NOT NULL,
4+
`modified` datetime NOT NULL,
5+
`counter` bigint,
6+
`transaction_id` integer NOT NULL,
7+
`type` int(11) unsigned NOT NULL
8+
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
9+
10+
ALTER TABLE `transaction_log` ADD CONSTRAINT `transaction_id_id` FOREIGN KEY (`transaction_id`) REFERENCES `transaction` (`id`);
11+
CREATE INDEX `transaction_log_id` ON `transaction_log` (`transaction_id`);

0 commit comments

Comments
 (0)