def test_add_first_transaction(self): transaction = self.create_transaction() interval = 10 margin = 3 sequence = TransactionSequence(interval) sequence.add_transaction(transaction, margin) assert transaction.sequence is None assert transaction.id in sequence.transactions
def test_add_transaction_set_ownership(self): transaction = self.create_transaction() interval = 10 margin = 3 sequence = TransactionSequence(interval) sequence.add_transaction(transaction, margin, set_ownership=True) assert transaction.sequence == sequence assert transaction.id in sequence.transactions
def test_add_transactions(self, add_transaction): transaction1 = self.create_transaction() transaction2 = self.create_transaction() transaction3 = self.create_transaction() transactions = [transaction1, transaction2, transaction3] interval = 10 margin = 3 ownership = True sequence = TransactionSequence(interval) sequence.add_transactions(transactions, margin, ownership) add_transaction.assert_has_calls([ call(transaction1, margin, ownership), call(transaction2, margin, ownership), call(transaction3, margin, ownership) ])
def create_sequence(self): transaction1 = Transaction(date='01/02/2020', description='TEST INVOICE 1234', amount='435.23') transaction2 = Transaction(date='01/12/2020', description='TEST INVOICE 1234', amount='435.23') transaction3 = Transaction(date='01/22/2020', description='TEST INVOICE 1234', amount='435.23') transactions = [transaction1, transaction2, transaction3] interval = 10 margin = 3 ownership = True sequence = TransactionSequence(interval) sequence.add_transactions(transactions, margin, ownership) return sequence
def test_contains(self): transaction1 = Transaction(date='01/02/2020', description='TEST INVOICE 1234', amount='435.23') transaction2 = Transaction(date='01/12/2020', description='TEST INVOICE 1234', amount='435.23') transaction3 = Transaction(date='01/22/2020', description='TEST INVOICE 1234', amount='435.23') transactions = [transaction1, transaction2] interval = 10 margin = 3 ownership = True sequence = TransactionSequence(interval) sequence.add_transactions(transactions, margin, ownership) assert transaction1 in sequence assert transaction3 not in sequence
def test_get_last_transaction(self): transaction1 = Transaction(date='01/02/2020', description='TEST INVOICE 1234', amount='435.23') transaction2 = Transaction(date='01/12/2020', description='TEST INVOICE 1234', amount='435.23') transaction3 = Transaction(date='01/22/2020', description='TEST INVOICE 1234', amount='435.23') transactions = [transaction1, transaction2, transaction3] interval = 10 margin = 3 ownership = True sequence = TransactionSequence(interval) sequence.add_transactions(transactions, margin, ownership) first = sequence.get_last_transaction() assert first == transaction3
def test_len(self): transaction1 = Transaction(date='01/02/2020', description='TEST INVOICE 1234', amount='435.23') transaction2 = Transaction(date='01/12/2020', description='TEST INVOICE 1234', amount='435.23') transaction3 = Transaction(date='01/22/2020', description='TEST INVOICE 1234', amount='435.23') transactions = [transaction1, transaction2, transaction3] interval = 10 margin = 3 ownership = True expected_length = 3 sequence = TransactionSequence(interval) sequence.add_transactions(transactions, margin, ownership) length = len(sequence) assert length == expected_length
def test_add_sequence(self): transaction1 = Transaction(date='01/02/2020', description='TEST INVOICE 1234', amount='435.23') transaction2 = Transaction(date='01/12/2020', description='TEST INVOICE 1234', amount='435.23') transaction3 = Transaction(date='01/22/2020', description='TEST INVOICE 1234', amount='435.23') transactions = [transaction1, transaction2, transaction3] interval = 10 margin = 3 ownership = True sequence = TransactionSequence(interval) sequence.add_transactions(transactions, margin, ownership) storage = SequenceStorage() storage.add_sequence(sequence) assert transaction1.id in storage.sequences assert transaction2.id in storage.sequences assert transaction3.id in storage.sequences
def test_get_sequence(self): transaction1 = Transaction(date='01/02/2020', description='TEST INVOICE 1234', amount='435.23') transaction2 = Transaction(date='01/12/2020', description='TEST INVOICE 1234', amount='435.23') transaction3 = Transaction(date='01/22/2020', description='TEST INVOICE 1234', amount='435.23') transactions = [transaction1, transaction2] interval = 10 margin = 3 ownership = True sequence = TransactionSequence(interval) sequence.add_transactions(transactions, margin, ownership) storage = SequenceStorage() storage.add_sequence(sequence) sequence1 = storage.get_sequence(transaction1) sequence2 = storage.get_sequence(transaction2) sequence3 = storage.get_sequence(transaction3) assert sequence1 == sequence2 == sequence assert sequence3 is None
def test_iter(self): called = Mock() transaction1 = Transaction(date='01/02/2020', description='TEST INVOICE 1234', amount='435.23') transaction2 = Transaction(date='01/12/2020', description='TEST INVOICE 1234', amount='435.23') transaction3 = Transaction(date='01/22/2020', description='TEST INVOICE 1234', amount='435.23') transactions = [transaction1, transaction2, transaction3] interval = 10 margin = 3 ownership = True sequence = TransactionSequence(interval) sequence.add_transactions(transactions, margin, ownership) for transaction in sequence: called(transaction) called.assert_has_calls( [call(transaction1), call(transaction2), call(transaction3)])
def parse_sequences(transaction_list, margin=DEFAULT_MARGIN): """ Given a list of transactions with similar descriptions, find sequences that respect the interval rule. :param transaction_list: List of transactions with similar descriptions. :param margin: Acceptable margin for the interval rule. :return: List of valid sequences. """ def get_margin(days): return [str(i) for i in range(days - margin, days + margin + 1)] # First, we should evaluate sequence candidates from transaction combinations. # By using itertools.combinations, we avoid the issue of singleton transactions breaking # a possible sequence. delta_hash = OrderedDict() for a, b in itertools.combinations(transaction_list, 2): xdiff = b.date - a.date if xdiff.days >= MINIMUM_INTERVAL: # '4 day rule' evaluation keys = list( set(delta_hash.keys()).intersection(get_margin( xdiff.days))) # margin evaluation if len(keys) > 0: delta_hash[keys[0]].add_transactions([a, b], margin=margin) else: sequence = TransactionSequence(interval=xdiff.days) sequence.add_transactions([a, b], margin=margin) delta_hash[str(xdiff.days)] = sequence sequences = [] # Now that we have the candidates, we should make sure that we are adhering to the minimum transactions and # the single-sequence rules, iterating through our candidates to get rid of extra sequences. for key, sequence in delta_hash.items(): transactions = [ transaction for transaction in sequence if not transaction.sequence ] # Single-sequence evaluation if len(transactions ) >= MINIMUM_TRANSACTIONS: # Minimum transactions evaluation clean_sequence = TransactionSequence(interval=int(key)) clean_sequence.add_transactions(transactions, margin=margin, set_ownership=True) sequences.append(clean_sequence) return sequences
def test_add_second_transaction_outside_margin(self): transaction = self.create_transaction() second_transaction = Transaction(date='01/02/2021', description='TEST INVOICE 1234', amount='435.23') interval = 10 margin = 3 sequence = TransactionSequence(interval) sequence.add_transaction(transaction, margin) sequence.add_transaction(second_transaction, margin) assert transaction.sequence is None assert second_transaction.sequence is None assert transaction.id in sequence.transactions assert second_transaction.id not in sequence.transactions
def test_creation(self): interval = 10 sequence = TransactionSequence(interval) assert sequence.interval == interval assert sequence.transactions == {}