from bisect import bisect_right
from decimal import Decimal
from math import log10
+import re
import csv
# gnucash imports
# its an easy edit to switch to xml: etc...
-# a dictionary with a period name as key, and number of months in that
-# kind of period as the value
-PERIODS = {"monthly": 1,
- "quarterly": 3,
- "yearly": 12 }
-
-NUM_MONTHS = 12
-
-ONE_DAY = timedelta(days=1)
-
-DEBITS_SHOW, CREDITS_SHOW = ("debits-show", "credits-show")
-
ZERO = Decimal(0)
def gnc_numeric_to_python_Decimal(numeric):
return Decimal( (sign, digit_tuple, -exponent) )
-def next_period_start(start_year, start_month, period_type):
- # add numbers of months for the period length
- end_month = start_month + PERIODS[period_type]
- # use integer division to find out if the new end month is in a different
- # year, what year it is, and what the end month number should be changed
- # to.
- # Because this depends on modular arithmatic, we have to curvert the month
- # values from 1-12 to 0-11 by subtracting 1 and putting it back after
- #
- # the really cool part is that this whole thing is implemented without
- # any branching; if end_month > NUM_MONTHS
- #
- # A the super nice thing is that you can add all kinds of period lengths
- # to PERIODS
- end_year = start_year + ( (end_month-1) / NUM_MONTHS )
- end_month = ( (end_month-1) % NUM_MONTHS ) + 1
-
- return end_year, end_month
-
-
-def period_end(start_year, start_month, period_type):
- if period_type not in PERIODS:
- raise Exception("%s is not a valid period, should be %s" % (
- period_type, str(PERIODS.keys()) ) )
-
- end_year, end_month = next_period_start(start_year, start_month,
- period_type)
-
- # last step, the end date is day back from the start of the next period
- # so we get a period end like
- # 2010-03-31 for period starting 2010-01 instead of 2010-04-01
- return date(end_year, end_month, 1) - ONE_DAY
-
-
-def generate_period_boundaries(start_year, start_month, period_type, periods):
- for i in xrange(periods):
- yield ( date(start_year, start_month, 1),
- period_end(start_year, start_month, period_type) )
- start_year, start_month = next_period_start(start_year, start_month,
- period_type)
-
def account_from_path(top_account, account_path, original_path=None):
if original_path==None: original_path = account_path
account, account_path = account_path[0], account_path[1:]
def main():
- (gnucash_file, start_year, start_month, period_type, periods,
- debits_show, credits_show) = argv[1:8]
- start_year, start_month, periods = [int(blah)
- for blah in (start_year, start_month,
- periods) ]
-
- debits_show = debits_show == DEBITS_SHOW
- credits_show = credits_show == CREDITS_SHOW
+ (gnucash_file) = argv[1]
- account_path = argv[8:]
+ account_path = argv[2:]
gnucash_session = Session(gnucash_file, is_new=False)
root_account = gnucash_session.book.get_root_account()
account_of_interest = account_from_path(root_account, account_path)
- # a list of all the periods of interest, for each period
- # keep the start date, end date, a list to store debits and credits,
- # and sums for tracking the sum of all debits and sum of all credits
- period_list = [
- [start_date, end_date,
- [], # debits
- [], # credits
- ZERO, # debits sum
- ZERO, # credits sum
- ]
- for start_date, end_date in generate_period_boundaries(
- start_year, start_month, period_type, periods)
- ]
- # a copy of the above list with just the period start dates
- period_starts = [e[0] for e in period_list ]
+ ical = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Example Corp.//CalDAV Client//EN"""
# insert and add all splits in the periods of interest
for split in account_of_interest.GetSplitList():
trans = split.parent
trans_date = date.fromtimestamp(trans.GetDate())
- # use binary search to find the period that starts before or on
- # the transaction date
- period_index = bisect_right( period_starts, trans_date ) - 1
-
- # ignore transactions with a date before the matching period start
- # (after subtracting 1 above start_index would be -1)
- # and after the last period_end
- if period_index >= 0 and \
- trans_date <= period_list[len(period_list)-1][1]:
-
- # get the period bucket appropriate for the split in question
- period = period_list[period_index]
-
- # more specifically, we'd expect the transaction date
- # to be on or after the period start, and before or on the
- # period end, assuming the binary search (bisect_right)
- # assumptions from above are are right..
- #
- # in other words, we assert our use of binary search
- # and the filtered results from the above if provide all the
- # protection we need
- assert( trans_date>= period[0] and trans_date <= period[1] )
-
- split_amount = gnc_numeric_to_python_Decimal(split.GetAmount())
-
- # if the amount is negative, this is a credit
- if split_amount < ZERO:
- debit_credit_offset = 1
- # else a debit
- else:
- debit_credit_offset = 0
-
- # store the debit or credit Split with its transaction, using the
- # above offset to get in the right bucket
- #
- # if we wanted to be really cool we'd keep the transactions
- period[2+debit_credit_offset].append( (trans, split) )
-
- # add the debit or credit to the sum, using the above offset
- # to get in the right bucket
- period[4+debit_credit_offset] += split_amount
-
- csv_writer = csv.writer(stdout)
- csv_writer.writerow( ('period start', 'period end', 'debits', 'credits') )
-
- def generate_detail_rows(values):
- return (
- ('', '', '', '', trans.GetDescription(),
- gnc_numeric_to_python_Decimal(split.GetAmount()))
- for trans, split in values )
+ num = trans.GetNum()
+ if (re.search('To Do', num, re.I)):
+ split_amount = gnc_numeric_to_python_Decimal(split.GetAmount())
- for start_date, end_date, debits, credits, debit_sum, credit_sum in \
- period_list:
- csv_writer.writerow( (start_date, end_date, debit_sum, credit_sum) )
+ day = trans_date.strftime("%Y%m%dZ");
+ stamp = trans_date.strftime("%Y%m%dT%H%M%SZ");
+ event = """BEGIN:VEVENT
+UID:""" + trans.GetGUID().to_string() + """
+DTSTAMP:""" + stamp + """
+DTSTART:""" + day + """
+SUMMARY:""" + trans.GetDescription() + """ - $%d
+END:VEVENT""" % split_amount
+ print event
+ else:
+ continue
- if debits_show and len(debits) > 0:
- csv_writer.writerow(
- ('DEBITS', '', '', '', 'description', 'value') )
- csv_writer.writerows( generate_detail_rows(debits) )
- csv_writer.writerow( () )
- if credits_show and len(credits) > 0:
- csv_writer.writerow(
- ('CREDITS', '', '', '', 'description', 'value') )
- csv_writer.writerows( generate_detail_rows(credits) )
- csv_writer.writerow( () )
# no save needed, we're just reading..
gnucash_session.end()