Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add transactions in batch through text #13

Merged
merged 7 commits into from
Oct 16, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ token.json
credentials.json
.vscode
budget.egg-info
venv/
venv/
.vs/

40 changes: 26 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# budget-cli
* **insert transaction entries**
* **view transaction logs & summary**
* **synchronize with annual budget**
* **once-in-a-year configuration**
* insert/edit transaction entries
* view transaction logs & summary
* synchronize with annual budget
* only configure once in a year

![Demo](demo.gif)

Expand Down Expand Up @@ -34,31 +34,43 @@ https://docs.google.com/spreadsheets/d/<SPREADSHEET_ID>/edit#gid=<SHEET_ID>
## Usage
* For `summary`, `categories`, `log` and `sync` commands, this month's spreadsheet will be used unless specified explicitly.

* For `expense` and `income` commands, today's date will be assigned and this month's spreadsheet will be used unless a custom date is specified explicitly as the first argument of transaction parameters.
* For `expense` and `income` commands, today's date will be assumed and this month's spreadsheet will be used unless date is specified explicitly.

* For `edit`, original date of the transaction is assigned and this month's spreadsheet will be used unless date is specified explicitly.
* For `edit` command, the transaction date will stay the same and this month's spreadsheet will be used unless date is specified explicitly, in which case the month will be determined accordingly.

### Transaction Entry
``` sh
# append expense for custom date
# insert expense transaction in June budget
budget expense "Jun 29, 40, Pizza, Food"

# append expense for today
# insert expense transaction for today in this month's budget
budget expense "40, Pizza, Food"

# append income for custom date
# insert income transaction in August budget
budget income "Aug 2, 3000, Salary, Paycheck"

# append income for today
# insert income transaction for today in this month's budget
budget income "3000, Salary, Paycheck"

# edit line 4 of income for this month (see line number in `budget log`)
# edit 4th income transaction in this month's budget (see `budget log` for transaction number)
budget edit income 4 "65, Tax Return, Other"

# edit line 5 of expense for September (see line number in `budget log`)
# edit 5th expense transaction in September budget (see `budget log sep` for transaction number)
budget edit expense 5 "Sep 17, Mobile Plan, Communication"

# execute all transaction commands within ./example.txt (path can be relative or absolute)
budget insert ./example.txt
```

Here's what an input file might look like when using the `insert` command:
```txt
expense "Jun 29, 40, Pizza, Food"
expense "40, Pizza, Food"
income "Aug 2, 3000, Salary, Paycheck"
income "3000, Salary, Paycheck"
```


### Summary
``` sh
# print monthly budget summary for all months so far
Expand Down Expand Up @@ -86,8 +98,8 @@ budget log
budget log mar
```

### Synchronization
For annual synchronization, expense & income categories must be exactly the same across monthly and annual budget spreadsheets.
### Annual Budget Synchronization
In order to synchronize with annual budget, expense & income categories must be exactly the same across all monthly budget spreadsheets and the annual budget spreadsheet.

``` sh
# update annual budget with expenses & income of this month
Expand Down
47 changes: 40 additions & 7 deletions budget/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
('Jan','D'), ('Feb','E'), ('Mar','F'), ('Apr','G'), ('May','H'), ('Jun','I'),
('Jul','J'), ('Aug','K'), ('Sep','L'), ('Oct','M'), ('Nov','N'), ('Dec','O')
])
COMMAND_SET = ['summary', 'categories', 'log', 'sync', 'expense', 'income', 'edit']
COMMAND_SET = ['summary', 'categories', 'log', 'sync', 'expense', 'income', 'edit', 'insert']

Categories = namedtuple('Categories', 'expense, income')
Summary = namedtuple('Summary', 'cells, title, categories')
Expand Down Expand Up @@ -75,7 +75,8 @@ def validateLineIndex(lineIndex, transactions):
raise UserWarning("Line index of {0} is invalid.".format(lineIndex))

# raises a UserWarning if the transaction is invalid
def validate(transaction, categories):
def validateCategory(command, transaction, summary):
categories = summary.categories.expense if command == 'expense' else summary.categories.income
if transaction[3] not in categories.keys():
message = "Invalid category: {0}. Valid categories are:".format(transaction[3])
for key in categories:
Expand Down Expand Up @@ -155,10 +156,13 @@ def readTransactions(service, ssheetId, type):

# authorize & build spreadsheet service
def getSheetService():
temp = os.getcwd()
os.chdir(APP_DIR)
store = file.Storage('token.json')
creds = store.get()
return build('sheets', 'v4', http=creds.authorize(Http())).spreadsheets().values()
service = build('sheets', 'v4', http=creds.authorize(Http())).spreadsheets().values()
os.chdir(temp)
return service

# reads configuration from file
def readConfig():
Expand All @@ -180,6 +184,19 @@ def getMonthlySheetId(date, sheetIds):
except KeyError:
raiseInvalidMonthError(month)

# parses multiple transaction commands from file
def parseTransactionsFile(filename):
try:
with open(filename, "r") as f:
transactions = []
for line in f.readlines():
transaction = line.split('"')
transaction[0] = transaction[0].strip()
transactions.append(transaction)
return transactions
except FileNotFoundError:
raise UserWarning("File not found: {0}".format(filename))

# reads program arguments
def readArgs():
if sys.argv[1] not in COMMAND_SET:
Expand All @@ -194,6 +211,8 @@ def readArgs():
if len(sys.argv) != 5:
raise UserWarning("Invalid command: Edit command requires exactly 5 arguments.")
return sys.argv[1], (sys.argv[2], sys.argv[3], sys.argv[4])
elif sys.argv[1] == "insert":
return sys.argv[1], sys.argv[2]
else:
if len(sys.argv) == 3:
month = sys.argv[2].lower()
Expand All @@ -214,11 +233,26 @@ def main():
print("Only 3 fields were specified. Assigning today to date field.")
monthlySheetId = getMonthlySheetId(transaction[0], sheetIds)
summary = readSummaryPage(service, monthlySheetId)
categories = summary.categories.expense if command == 'expense' else summary.categories.income
validate(transaction, categories)
validateCategory(command, transaction, summary)
transaction[0] = str(transaction[0])[:10]
insertTransaction(transaction, service, command, monthlySheetId, summary.title)
return
if command == 'insert':
lines = parseTransactionsFile(param)
for line in lines:
print('\nProcessing command: {0} "{1}"'.format(line[0], line[1]))
try:
transaction, noExplicitDate = parseTransaction(line[1])
if noExplicitDate is True:
print("Only 3 fields were specified. Assigning today to date field.")
monthlySheetId = getMonthlySheetId(transaction[0], sheetIds)
summary = readSummaryPage(service, monthlySheetId)
validateCategory(line[0], transaction, summary)
transaction[0] = str(transaction[0])[:10]
insertTransaction(transaction, service, line[0], monthlySheetId, summary.title)
except UserWarning as e:
print("Warning: {0}".format(e))
return
if command == "edit":
subcommand = param[0]
lineIndex = int(param[1])
Expand All @@ -230,8 +264,7 @@ def main():
print("Only 3 fields were specified. Assigning original date to date field.")
transaction[0] = transactions[lineIndex - 1][0]
summary = readSummaryPage(service, monthlySheetId)
categories = summary.categories.expense if subcommand == 'expense' else summary.categories.income
validate(transaction, categories)
validateCategory(subcommand, transaction, summary)
transaction[0] = str(transaction[0])[:10]
editTransaction(lineIndex, transaction, service, subcommand, monthlySheetId, summary.title)
return
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

setup(
name='budget',
version='1.1.0',
version='1.2.0',
packages=['budget'],
entry_points={
'console_scripts': [
Expand Down