-
-
Notifications
You must be signed in to change notification settings - Fork 320
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
;examples: csv: add an example python converter script
- Loading branch information
1 parent
a6542e6
commit 7a88130
Showing
1 changed file
with
87 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
#!/usr/bin/env python3 | ||
# An example of using Python to convert a certain CSV to hledger journal entries. | ||
# This won't work as-is (unless you have this particular kind of CSV); | ||
# use it for inspiration. hledger's own CSV rules are not used at all here. | ||
|
||
|
||
__version__ = "1.0" | ||
__author__ = "Simon Michael" | ||
|
||
VERSION = "%prog " + __version__ | ||
USAGE = """%prog [options] [CSVFILE [JOURNALFILE]] | ||
Reads a [certain kind of] CSV, writes a hledger journal. | ||
Journal entries will be in the same order as the CSV records. | ||
Requirements: python 3. | ||
""" | ||
|
||
# from pprint import pprint as pp | ||
import csv | ||
import datetime | ||
import optparse | ||
import re | ||
import subprocess | ||
import sys | ||
import tempfile | ||
# import warnings | ||
|
||
def parse_options(): | ||
parser = optparse.OptionParser(usage=USAGE, version=VERSION) | ||
opts, args = parser.parse_args() | ||
if len(args) > 2: | ||
parser.print_help() | ||
sys.exit() | ||
return opts, args | ||
|
||
def single_space(ms): | ||
if ms is not None: ms = re.sub(r' +',' ',ms) | ||
return ms | ||
|
||
# Join two strings with the give separator, but omit the separator | ||
# if either string is empty. | ||
def intercalate2(sep,astr,bstr): | ||
if astr and bstr: | ||
return astr + sep + bstr | ||
else: | ||
return astr + bstr | ||
|
||
def main(): | ||
opts, args = parse_options() | ||
out = open(args[1],'w') if len(args) > 1 else sys.stdout | ||
with open(args[0],'r') if len(args) > 0 else sys.stdin as csvfile: | ||
|
||
# Process CSV records, and generate a hledger journal entry for each transaction. | ||
|
||
for r in csv.reader(csvfile): | ||
|
||
# skip headings (record containing no numbers) | ||
if all(map(lambda v: not any(c.isdigit() for c in v), r)): continue | ||
|
||
# hledger doesn't like records with only one field | ||
if len(r) < 2: continue | ||
|
||
# skip non-transaction records | ||
if re.match(r'^(Starting Balance|Net Change|Total|)$',r[0]): continue | ||
|
||
# write a hledger journal entry | ||
property_,date_,payee_payer_,type_,reference_,debit_,credit_,balance_,description_,gl_account_ = r | ||
date = datetime.datetime.strptime(date_,"%m/%d/%Y").date().isoformat() | ||
code = f" ({reference_})" if reference_ else "" | ||
description = intercalate2(' | ', payee_payer_, intercalate2(' ', description_, type_)) | ||
amount1 = f"${debit_}" if debit_ else "" | ||
amount2 = f"${credit_}" if credit_ else "" | ||
balance1 = balance_ | ||
account1 = f"Properties:{property_}" | ||
if gl_account_[0].isdigit(): | ||
account2 = f"{gl_account_[0]}xxx:{gl_account_}" | ||
else: | ||
account2 = f"{gl_account_}" | ||
account1, account2 = single_space(account1), single_space(account2) | ||
out.write(f"""\ | ||
{date}{code} {description} | ||
{account1} {amount1} ; = {balance1} | ||
{account2} {amount2} | ||
""") | ||
|
||
if __name__ == "__main__": main() |