def start(cls): """Try to start a new Payday. If there is a Payday that hasn't finished yet, then the UNIQUE constraint on ts_end will kick in and notify us of that. In that case we load the existing Payday and work on it some more. We use the start time of the current Payday to synchronize our work. """ try: d = cls.db.one(""" INSERT INTO paydays (id) VALUES (COALESCE(( SELECT id FROM paydays ORDER BY id DESC LIMIT 1 ), 0) + 1) RETURNING id, (ts_start AT TIME ZONE 'UTC') AS ts_start """, back_as=dict) log("Starting payday #%s." % d['id']) except IntegrityError: # Collision, we have a Payday already. d = cls.db.one(""" SELECT id, (ts_start AT TIME ZONE 'UTC') AS ts_start FROM paydays WHERE ts_end='1970-01-01T00:00:00+00'::timestamptz """, back_as=dict) log("Picking up payday #%s." % d['id']) d['ts_start'] = d['ts_start'].replace(tzinfo=pando.utils.utc) log("Payday started at %s." % d['ts_start']) payday = Payday() payday.__dict__.update(d) return payday
def start(cls, public_log=''): """Try to start a new Payday. If there is a Payday that hasn't finished yet, then the UNIQUE constraint on ts_end will kick in and notify us of that. In that case we load the existing Payday and work on it some more. We use the start time of the current Payday to synchronize our work. """ d = cls.db.one(""" INSERT INTO paydays (id, public_log, ts_start) VALUES ( COALESCE((SELECT id FROM paydays ORDER BY id DESC LIMIT 1), 0) + 1 , %s , now() ) ON CONFLICT (ts_end) DO UPDATE SET ts_start = COALESCE(paydays.ts_start, excluded.ts_start) RETURNING id, (ts_start AT TIME ZONE 'UTC') AS ts_start, stage """, (public_log, ), back_as=dict) log("Running payday #%s." % d['id']) d['ts_start'] = d['ts_start'].replace(tzinfo=pando.utils.utc) log("Payday started at %s." % d['ts_start']) payday = Payday() payday.__dict__.update(d) return payday
def test_iter_payday_events(self): Payday.start().run() team = self.make_participant('team', kind='group') alice = self.make_participant('alice') self.make_exchange('mango-cc', 10000, 0, alice) self.make_exchange('mango-cc', -5000, 0, alice) self.db.run(""" UPDATE transfers SET timestamp = "timestamp" - interval '1 month' """) bob = self.make_participant('bob') carl = self.make_participant('carl') david = self.make_participant('david') self.make_exchange('mango-cc', 10000, 0, david) david.set_tip_to(team, Decimal('1.00')) team.set_take_for(bob, Decimal('1.00'), bob) alice.set_tip_to(bob, Decimal('5.00')) assert bob.balance == 0 for i in range(2): Payday.start().run() self.db.run(""" UPDATE paydays SET ts_start = ts_start - interval '1 week' , ts_end = ts_end - interval '1 week'; UPDATE transfers SET timestamp = "timestamp" - interval '1 week'; """) bob = Participant.from_id(bob.id) assert bob.balance == 12 Payday().start() events = list(iter_payday_events(self.db, bob)) assert len(events) == 9 assert events[0]['kind'] == 'totals' assert events[0]['given'] == 0 assert events[0]['received'] == 12 assert events[1]['kind'] == 'day-open' assert events[1]['payday_number'] == 2 assert events[2]['balance'] == 12 assert events[-1]['kind'] == 'day-close' assert events[-1]['balance'] == 0 alice = Participant.from_id(alice.id) assert alice.balance == 4990 events = list(iter_payday_events(self.db, alice)) assert events[0]['given'] == 10 assert len(events) == 11 carl = Participant.from_id(carl.id) assert carl.balance == 0 events = list(iter_payday_events(self.db, carl)) assert len(events) == 0
def test_end_raises_NoPayday(self): with self.assertRaises(NoPayday): Payday().end()
def test_iter_payday_events(self): now = datetime.now() Payday.start().run() team = self.make_participant('team', kind='group') alice = self.make_participant('alice') self.make_exchange('mango-cc', 10000, 0, alice) self.make_exchange('mango-cc', -5000, 0, alice) self.db.run(""" UPDATE exchanges SET timestamp = "timestamp" - interval '1 week' """) bob = self.make_participant('bob') carl = self.make_participant('carl') david = self.make_participant('david') self.make_exchange('mango-cc', 10000, 0, david) david.set_tip_to(team, EUR('1.00')) team.set_take_for(bob, EUR('1.00'), team) alice.set_tip_to(bob, EUR('5.00')) assert bob.balance == 0 for i in range(2): Payday.start().run() self.db.run(""" UPDATE exchanges SET timestamp = "timestamp" - interval '1 week'; UPDATE paydays SET ts_start = ts_start - interval '1 week' , ts_end = ts_end - interval '1 week'; UPDATE transfers SET timestamp = "timestamp" - interval '1 week'; """) bob = Participant.from_id(bob.id) assert bob.balance == 12 Payday().start() # Make sure events are all in the same year delta = '%s days' % (364 - (now - datetime(now.year, 1, 1)).days) self.db.run( """ UPDATE exchanges SET timestamp = "timestamp" + interval %(delta)s; UPDATE paydays SET ts_start = ts_start + interval %(delta)s; UPDATE paydays SET ts_end = ts_end + interval %(delta)s WHERE ts_end >= ts_start; UPDATE transfers SET timestamp = "timestamp" + interval %(delta)s; """, dict(delta=delta)) events = list(iter_payday_events(self.db, bob, now.year)) assert len(events) == 9 assert events[0]['kind'] == 'totals' assert not events[0]['regular_donations']['sent'] assert events[0]['regular_donations']['received'] == EUR(12) assert events[1]['kind'] == 'day-open' assert events[1]['payday_number'] == 3 assert events[2]['balances'] == EUR(12) assert events[-1]['kind'] == 'day-close' assert events[-1]['balances'] == 0 alice = Participant.from_id(alice.id) assert alice.balance == 4990 events = list(iter_payday_events(self.db, alice, now.year)) assert events[0]['regular_donations']['sent'] == EUR(10) assert len(events) == 11 carl = Participant.from_id(carl.id) assert carl.balance == 0 events = list(iter_payday_events(self.db, carl, now.year)) assert len(events) == 0