def _entries_in_inclusive_range(self, entries, begin_date=None, end_date=None): """ Returns the list of entries satisfying begin_date <= date <= end_date. """ get_date = lambda x: x.date if begin_date is None: begin_index = 0 else: begin_index = bisect_key.bisect_left_with_key(entries, begin_date, key=get_date) if end_date is None: end_index = len(entries) else: end_index = bisect_key.bisect_left_with_key(entries, end_date+timedelta(days=1), key=get_date) return entries[begin_index:end_index]
def split_rows(rows, file_period): date_first = rows[0][0] date_last = rows[-1][0] date_end = date_first + file_period while date_end < date_last: index_end = bisect_left_with_key(rows, date_end, key=lambda row: row.date) date_begin = date_end - file_period index_begin = bisect_left_with_key(rows, date_begin, key=lambda row: row.date) yield date_end, rows[index_begin:index_end] report_interval = datetime.timedelta(days=random.randint(60, 80)) date_end += report_interval
def iter_entry_dates(entries, date_begin, date_end): """Iterate over the entries in a date window. Args: entries: A date-sorted list of dated directives. date_begin: A datetime.date instance, the first date to include. date_end: A datetime.date instance, one day beyond the last date. Yields: Instances of the dated directives, between the dates, and in the order in which they appear. """ getdate = lambda entry: entry.date index_begin = bisect_left_with_key(entries, date_begin, key=getdate) index_end = bisect_left_with_key(entries, date_end, key=getdate) for index in range(index_begin, index_end): yield entries[index]
def conversions(entries, conversion_account, conversion_currency, date=None): """Insert a conversion entry at date 'date' at the given account. Args: entries: A list of entries. conversion_account: A string, the account to book against. conversion_currency: A string, the transfer currency to use for zero prices on the conversion entry. date: The date before which to insert the conversion entry. The new entry will be inserted as the last entry of the date just previous to this date. Returns: A modified list of entries. """ # Compute the balance at the given date. conversion_balance = interpolate.compute_entries_balance(entries, date=date) # Early exit if there is nothing to do. conversion_cost_balance = conversion_balance.reduce(convert.get_cost) if conversion_cost_balance.is_empty(): return entries # Calculate the index and the date for the new entry. We want to store it as # the last transaction of the day before. if date is not None: index = bisect_key.bisect_left_with_key(entries, date, key=lambda entry: entry.date) last_date = date - datetime.timedelta(days=1) else: index = len(entries) last_date = entries[-1].date meta = data.new_metadata('<conversions>', -1) narration = 'Conversion for {}'.format(conversion_balance) conversion_entry = Transaction(meta, last_date, flags.FLAG_CONVERSIONS, None, narration, data.EMPTY_SET, data.EMPTY_SET, []) for position in conversion_cost_balance.get_positions(): # Important note: Set the cost to zero here to maintain the balance # invariant. (This is the only single place we cheat on the balance rule # in the entire system and this is necessary; see documentation on # Conversions.) price = amount.Amount(ZERO, conversion_currency) neg_pos = -position conversion_entry.postings.append( data.Posting(conversion_account, neg_pos.units, neg_pos.cost, price, None, None)) # Make a copy of the list of entries and insert the new transaction into it. new_entries = list(entries) new_entries.insert(index, conversion_entry) return new_entries
def truncate(entries, date): """Filter out all the entries at and after date. Returns a new list of entries. Args: entries: A sorted list of directives. date: A datetime.date instance. Returns: A truncated list of directives. """ index = bisect_key.bisect_left_with_key(entries, date, key=lambda entry: entry.date) return entries[:index]
def _entries_in_inclusive_range(self, entries, begin_date=None, end_date=None): """ Returns the list of entries satisfying begin_date <= date <= end_date. """ get_date = lambda x: x.date if begin_date is None: begin_index = 0 else: begin_index = bisect_key.bisect_left_with_key(entries, begin_date, key=get_date) if end_date is None: end_index = len(entries) else: end_index = bisect_key.bisect_left_with_key(entries, end_date + timedelta(days=1), key=get_date) return entries[begin_index:end_index]
def test_bisect_left_with_key(self): second = lambda x: x[1] data = [(random.random(), i) for i in range(100)] index = bisect_key.bisect_left_with_key(data, 40, key=second) self.assertEqual(index, 40) self.assertEqual(data[index][1], 40) index = bisect_key.bisect_left_with_key(data, 0, key=second) self.assertEqual(index, 0) self.assertEqual(data[index][1], 0) index = bisect_key.bisect_left_with_key(data, -1, key=second) self.assertEqual(index, 0) self.assertEqual(data[index][1], 0) index = bisect_key.bisect_left_with_key(data, 99, key=second) self.assertEqual(index, 99) index = bisect_key.bisect_left_with_key(data, 100, key=second) self.assertEqual(index, 100) index = bisect_key.bisect_left_with_key(data, 999, key=second) self.assertEqual(index, 100)
def test_bisect_repeats(self): data = [('a', 0), ('b', 0), ('c', 1), ('d', 3), ('e', 4), ('f', 4), ('g', 5), ('h', 6)] index = bisect_key.bisect_left_with_key(data, 4, key=lambda x: x[1]) self.assertEqual(index, 4) self.assertEqual(data[index][0], 'e')