From c1f2634c902dfe5ab6ed887094fb78fb40305176 Mon Sep 17 00:00:00 2001 From: Rebecca Law Date: Wed, 26 Jul 2017 13:19:17 +0100 Subject: [PATCH] Removed month and year and replaced it with start_date and end_date. This will allow us to sort the data properly. --- app/commands.py | 1 + app/dao/monthly_billing_dao.py | 14 ++--- app/models.py | 10 ++-- .../versions/0112_add_start_end_dates.py | 54 +++++++++++++++++++ tests/app/celery/test_scheduled_tasks.py | 4 +- tests/app/dao/test_monthly_billing.py | 41 ++++++++------ 6 files changed, 95 insertions(+), 29 deletions(-) create mode 100644 migrations/versions/0112_add_start_end_dates.py diff --git a/app/commands.py b/app/commands.py index c7ec0567b..14e26b887 100644 --- a/app/commands.py +++ b/app/commands.py @@ -158,6 +158,7 @@ class PopulateMonthlyBilling(Command): ) def run(self, service_id, month, year): + print('Starting populating monthly billing') create_or_update_monthly_billing_sms(service_id, datetime(int(year), int(month), 1)) results = get_monthly_billing_sms(service_id, datetime(int(year), int(month), 1)) print("Finished populating data for {} for service id {}".format(month, service_id)) diff --git a/app/dao/monthly_billing_dao.py b/app/dao/monthly_billing_dao.py index d2eabb9db..4f4e254c8 100644 --- a/app/dao/monthly_billing_dao.py +++ b/app/dao/monthly_billing_dao.py @@ -25,25 +25,25 @@ def create_or_update_monthly_billing_sms(service_id, billing_month): monthly = get_billing_data_for_month(service_id=service_id, start_date=start_date, end_date=end_date) # update monthly monthly_totals = _monthly_billing_data_to_json(monthly) - row = MonthlyBilling.query.filter_by(year=billing_month.year, - month=datetime.strftime(end_date, "%B"), + row = MonthlyBilling.query.filter_by(start_date=start_date, notification_type='sms').first() if row: row.monthly_totals = monthly_totals + row.updated_at = datetime.utcnow() else: row = MonthlyBilling(service_id=service_id, notification_type=SMS_TYPE, - year=billing_month.year, - month=datetime.strftime(end_date, "%B"), - monthly_totals=monthly_totals) + monthly_totals=monthly_totals, + start_date=start_date, + end_date=end_date) db.session.add(row) @statsd(namespace="dao") def get_monthly_billing_sms(service_id, billing_month): + start_date, end_date = get_month_start_end_date(billing_month) monthly = MonthlyBilling.query.filter_by(service_id=service_id, - year=billing_month.year, - month=datetime.strftime(billing_month, "%B"), + start_date=start_date, notification_type=SMS_TYPE).first() return monthly diff --git a/app/models.py b/app/models.py index b58eaf348..5ab742b4f 100644 --- a/app/models.py +++ b/app/models.py @@ -1253,20 +1253,20 @@ class MonthlyBilling(db.Model): id = db.Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) service_id = db.Column(UUID(as_uuid=True), db.ForeignKey('services.id'), index=True, nullable=False) service = db.relationship('Service', backref='monthly_billing') - month = db.Column(db.String, nullable=False) - year = db.Column(db.Float(asdecimal=False), nullable=False) + start_date = db.Column(db.DateTime, nullable=False) + end_date = db.Column(db.DateTime, nullable=False) notification_type = db.Column(notification_types, nullable=False) monthly_totals = db.Column(JSON, nullable=False) updated_at = db.Column(db.DateTime, nullable=False, default=datetime.datetime.utcnow) __table_args__ = ( - UniqueConstraint('service_id', 'month', 'year', 'notification_type', name='uix_monthly_billing'), + UniqueConstraint('service_id', 'start_date', 'notification_type', name='uix_monthly_billing'), ) def serialized(self): return { - "month": self.month, - "year": self.year, + "start_date": self.start_date, + "end_date": self.end_date, "service_id": str(self.service_id), "notification_type": self.notification_type, "monthly_totals": self.monthly_totals diff --git a/migrations/versions/0112_add_start_end_dates.py b/migrations/versions/0112_add_start_end_dates.py new file mode 100644 index 000000000..658c573ae --- /dev/null +++ b/migrations/versions/0112_add_start_end_dates.py @@ -0,0 +1,54 @@ +"""empty message + +Revision ID: 0112_add_start_end_dates +Revises: 0111_drop_old_service_flags +Create Date: 2017-07-12 13:35:45.636618 + +""" +from datetime import datetime +from alembic import op +import sqlalchemy as sa +from app.dao.date_util import get_month_start_end_date + +down_revision = '0111_drop_old_service_flags' +revision = '0112_add_start_end_dates' + + +def upgrade(): + op.drop_index('uix_monthly_billing', 'monthly_billing') + op.add_column('monthly_billing', sa.Column('start_date', sa.DateTime)) + op.add_column('monthly_billing', sa.Column('end_date', sa.DateTime)) + conn = op.get_bind() + results = conn.execute("Select id, month, year from monthly_billing") + res = results.fetchall() + for x in res: + start_date, end_date = get_month_start_end_date( + datetime(int(x.year), datetime.strptime(x.month, '%B').month, 1)) + conn.execute("update monthly_billing set start_date = '{}', end_date = '{}' where id = '{}'".format(start_date, + end_date, + x.id)) + op.alter_column('monthly_billing', 'start_date', nullable=False) + op.alter_column('monthly_billing', 'end_date', nullable=False) + op.drop_column('monthly_billing', 'month') + op.drop_column('monthly_billing', 'year') + + op.create_index(op.f('uix_monthly_billing'), 'monthly_billing', ['service_id', 'start_date', 'notification_type'], + unique=True) + + +def downgrade(): + op.add_column('monthly_billing', sa.Column('month', sa.String(), nullable=True)) + op.add_column('monthly_billing', sa.Column('year', sa.Float(), nullable=True)) + conn = op.get_bind() + results = conn.execute("Select id, start_date, end_date from monthly_billing") + res = results.fetchall() + for x in res: + year = datetime.strftime(x.end_date, '%Y') + month = datetime.strftime(x.end_date, '%B') + conn.execute("update monthly_billing set month = '{}', year = {} where id = '{}'".format(month, year, x.id)) + + op.drop_column('monthly_billing', 'start_date') + op.drop_column('monthly_billing', 'end_date') + + op.create_index(op.f('uix_monthly_billing'), 'monthly_billing', + ['service_id', 'month', 'year', 'notification_type'], unique=True) diff --git a/tests/app/celery/test_scheduled_tasks.py b/tests/app/celery/test_scheduled_tasks.py index 6ea42844d..8ce518dfb 100644 --- a/tests/app/celery/test_scheduled_tasks.py +++ b/tests/app/celery/test_scheduled_tasks.py @@ -627,8 +627,8 @@ def test_populate_monthly_billing(sample_template): monthly_billing = MonthlyBilling.query.all() assert len(monthly_billing) == 1 assert monthly_billing[0].service_id == sample_template.service_id - assert monthly_billing[0].year == 2017 - assert monthly_billing[0].month == 'July' + assert monthly_billing[0].start_date == datetime(2017, 6, 30, 23) + assert monthly_billing[0].end_date == datetime(2017, 7, 31, 22, 59, 59, 99999) assert monthly_billing[0].notification_type == 'sms' assert len(monthly_billing[0].monthly_totals) == 1 assert sorted(monthly_billing[0].monthly_totals[0]) == sorted({'international': False, diff --git a/tests/app/dao/test_monthly_billing.py b/tests/app/dao/test_monthly_billing.py index 538f19c51..e07588374 100644 --- a/tests/app/dao/test_monthly_billing.py +++ b/tests/app/dao/test_monthly_billing.py @@ -1,5 +1,8 @@ from datetime import datetime +from freezegun import freeze_time +from freezegun.api import FakeDatetime + from app.dao.monthly_billing_dao import ( create_or_update_monthly_billing_sms, get_monthly_billing_sms, @@ -23,8 +26,8 @@ def test_add_monthly_billing(sample_template): billing_month=feb) monthly_billing = MonthlyBilling.query.all() assert len(monthly_billing) == 2 - assert monthly_billing[0].month == 'January' - assert monthly_billing[1].month == 'February' + assert monthly_billing[0].start_date == datetime(2017, 1, 1) + assert monthly_billing[1].start_date == datetime(2017, 2, 1) january = get_monthly_billing_sms(service_id=sample_template.service_id, billing_month=jan) expected_jan = {"billing_units": 1, @@ -32,7 +35,8 @@ def test_add_monthly_billing(sample_template): "international": False, "rate": 0.0158, "total_cost": 1 * 0.0158} - assert_monthly_billing(january, 2017, "January", sample_template.service_id, 1, expected_jan) + assert_monthly_billing(january, sample_template.service_id, 1, expected_jan, + start_date=datetime(2017, 1, 1), end_date=datetime(2017, 1, 31)) february = get_monthly_billing_sms(service_id=sample_template.service_id, billing_month=feb) expected_feb = {"billing_units": 2, @@ -40,7 +44,8 @@ def test_add_monthly_billing(sample_template): "international": False, "rate": 0.0158, "total_cost": 2 * 0.0158} - assert_monthly_billing(february, 2017, "February", sample_template.service_id, 1, expected_feb) + assert_monthly_billing(february, sample_template.service_id, 1, expected_feb, + start_date=datetime(2017, 2, 1), end_date=datetime(2017, 2, 28)) def test_add_monthly_billing_multiple_rates_in_a_month(sample_template): @@ -62,7 +67,7 @@ def test_add_monthly_billing_multiple_rates_in_a_month(sample_template): billing_month=rate_2) monthly_billing = MonthlyBilling.query.all() assert len(monthly_billing) == 1 - assert monthly_billing[0].month == 'January' + assert monthly_billing[0].start_date == datetime(2017, 1, 1) january = get_monthly_billing_sms(service_id=sample_template.service_id, billing_month=rate_2) first_row = {"billing_units": 2, @@ -70,7 +75,8 @@ def test_add_monthly_billing_multiple_rates_in_a_month(sample_template): "international": False, "rate": 0.0158, "total_cost": 3 * 0.0158} - assert_monthly_billing(january, 2017, "January", sample_template.service_id, 2, first_row) + assert_monthly_billing(january, sample_template.service_id, 2, first_row, + start_date=datetime(2017, 1, 1), end_date=datetime(2017, 1, 1)) second_row = {"billing_units": 6, "rate_multiplier": 1, "international": False, @@ -83,30 +89,35 @@ def test_update_monthly_billing_overwrites_old_totals(sample_template): july = datetime(2017, 7, 1) create_rate(july, 0.123, 'sms') create_notification(template=sample_template, created_at=datetime(2017, 7, 2), billable_units=1, status='delivered') - - create_or_update_monthly_billing_sms(sample_template.service_id, july) + with freeze_time('2017-07-20 02:30:00'): + create_or_update_monthly_billing_sms(sample_template.service_id, july) first_update = get_monthly_billing_sms(sample_template.service_id, july) expected = {"billing_units": 1, "rate_multiplier": 1, "international": False, "rate": 0.123, "total_cost": 1 * 0.123} - assert_monthly_billing(first_update, 2017, "July", sample_template.service_id, 1, expected) + assert_monthly_billing(first_update, sample_template.service_id, 1, expected, + start_date=datetime(2017, 6, 30, 23), end_date=datetime(2017, 7, 31, 23, 59, 59, 99999)) + first_updated_at = first_update.updated_at + with freeze_time('2017-07-20 03:30:00'): + create_notification(template=sample_template, created_at=datetime(2017, 7, 5), billable_units=2, + status='delivered') - create_notification(template=sample_template, created_at=datetime(2017, 7, 5), billable_units=2, status='delivered') - create_or_update_monthly_billing_sms(sample_template.service_id, july) + create_or_update_monthly_billing_sms(sample_template.service_id, july) second_update = get_monthly_billing_sms(sample_template.service_id, july) expected_update = {"billing_units": 3, "rate_multiplier": 1, "international": False, "rate": 0.123, "total_cost": 3 * 0.123} - assert_monthly_billing(second_update, 2017, "July", sample_template.service_id, 1, expected_update) + assert_monthly_billing(second_update, sample_template.service_id, 1, expected_update, + start_date=datetime(2017, 6, 30, 23), end_date=datetime(2017, 7, 31, 23, 59, 59, 99999)) + assert second_update.updated_at == FakeDatetime(2017, 7, 20, 3, 30) + assert first_updated_at != second_update.updated_at -def assert_monthly_billing(monthly_billing, year, month, service_id, expected_len, first_row): - assert monthly_billing.year == year - assert monthly_billing.month == month +def assert_monthly_billing(monthly_billing, service_id, expected_len, first_row, start_date, end_date): assert monthly_billing.service_id == service_id assert len(monthly_billing.monthly_totals) == expected_len assert sorted(monthly_billing.monthly_totals[0]) == sorted(first_row)