Add a bunch of fixes, categories
- Go through and update database
This commit is contained in:
parent
9e544ec64a
commit
d986461019
7
.vscode/launch.json
vendored
7
.vscode/launch.json
vendored
@ -11,7 +11,12 @@
|
|||||||
"program": "main.py",
|
"program": "main.py",
|
||||||
"console": "integratedTerminal",
|
"console": "integratedTerminal",
|
||||||
"justMyCode": true,
|
"justMyCode": true,
|
||||||
"args": ["C:\\Users\\saram\\Downloads\\Statement_072022_4653.pdf"]
|
"args": [
|
||||||
|
// "C:\\Users\\saram\\Downloads\\Statement_072022_4653.pdf",
|
||||||
|
// "C:\\Users\\saram\\Downloads\\Statement_082022_4653.pdf",
|
||||||
|
"C:\\Users\\saram\\Downloads\\Statement_092022_4653.pdf",
|
||||||
|
"C:\\Users\\saram\\Downloads\\Statement_102022_4653.pdf",
|
||||||
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "Create Database",
|
"name": "Create Database",
|
||||||
|
19
app.py
19
app.py
@ -105,8 +105,9 @@ def transaction(uuid=None):
|
|||||||
# Default if no method is hit
|
# Default if no method is hit
|
||||||
abort(400)
|
abort(400)
|
||||||
|
|
||||||
@app.route("/categories")
|
@app.route("/categories", methods=["GET", "POST"])
|
||||||
def categories():
|
def categories():
|
||||||
|
if request.method == "GET":
|
||||||
try:
|
try:
|
||||||
raw_categories = database.TransactionCategory.select()
|
raw_categories = database.TransactionCategory.select()
|
||||||
categories = {}
|
categories = {}
|
||||||
@ -123,5 +124,21 @@ def categories():
|
|||||||
abort(404)
|
abort(404)
|
||||||
except:
|
except:
|
||||||
abort(500)
|
abort(500)
|
||||||
|
if request.method == "POST":
|
||||||
|
body = request.get_json()
|
||||||
|
try:
|
||||||
|
if 'parent' in body:
|
||||||
|
parent = database.TransactionCategory.get(database.TransactionCategory.name == body['parent'])
|
||||||
|
database.TransactionCategory.create(name=body['name'], parent=parent)
|
||||||
|
else:
|
||||||
|
database.TransactionCategory.create(name=body['name'])
|
||||||
|
return ('', 200)
|
||||||
|
except peewee.DoesNotExist:
|
||||||
|
abort(404)
|
||||||
|
except:
|
||||||
|
abort(500)
|
||||||
|
|
||||||
|
# Should never get here.
|
||||||
|
abort(500)
|
||||||
|
|
||||||
app.run()
|
app.run()
|
@ -53,13 +53,16 @@ class Transactions(Regex):
|
|||||||
|
|
||||||
date = result[0]
|
date = result[0]
|
||||||
description = " ".join(result[1].split())
|
description = " ".join(result[1].split())
|
||||||
is_payment = '-' in result[2]
|
is_credit = '-' in result[2]
|
||||||
amount = float(result[3].replace(',', ''))
|
amount = float(result[3].replace(',', ''))
|
||||||
|
|
||||||
if is_payment:
|
if is_credit:
|
||||||
|
if "AUTOPAY" in description:
|
||||||
print(f"Skipping payment: {amount}")
|
print(f"Skipping payment: {amount}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
amount *= -1
|
||||||
|
|
||||||
transactions.append([date, description, amount])
|
transactions.append([date, description, amount])
|
||||||
|
|
||||||
return transactions
|
return transactions
|
||||||
|
@ -41,6 +41,15 @@ def add_default_categories():
|
|||||||
for child in children:
|
for child in children:
|
||||||
TransactionCategory.create(name=child, parent=parent_db)
|
TransactionCategory.create(name=child, parent=parent_db)
|
||||||
|
|
||||||
|
|
||||||
|
def delete_transactions():
|
||||||
|
instance.connect()
|
||||||
|
# august_2022 = datetime.datetime(2022, 8, 18, 0)
|
||||||
|
# delete_query = Transaction.delete().where(Transaction.transaction_date > august_2022)
|
||||||
|
delete_query = Transaction.delete().where(Transaction.primary_key == 83)
|
||||||
|
delete_query.execute()
|
||||||
|
instance.close()
|
||||||
|
|
||||||
class BaseModel(Model):
|
class BaseModel(Model):
|
||||||
class Meta:
|
class Meta:
|
||||||
database = instance
|
database = instance
|
||||||
|
2
main.py
2
main.py
@ -34,7 +34,7 @@ for f in args.files:
|
|||||||
transaction_date=date,
|
transaction_date=date,
|
||||||
description=description,
|
description=description,
|
||||||
amount=amount,
|
amount=amount,
|
||||||
source_filename=filename,
|
source_file=filename,
|
||||||
type=file_parser.source
|
type=file_parser.source
|
||||||
)
|
)
|
||||||
database.instance.close()
|
database.instance.close()
|
||||||
|
@ -100,6 +100,12 @@
|
|||||||
<button id="modal_next" class="next">Submit</button>
|
<button id="modal_next" class="next">Submit</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<h2>Add category</h2>
|
||||||
|
<label for="category_parent">Parent: </label>
|
||||||
|
<select id="category_parent"></select>
|
||||||
|
<label for="category_name">Name: </label>
|
||||||
|
<input id="category_name">
|
||||||
|
<button id="category_add">Add</button>
|
||||||
<!-- Transaction Modal End -->
|
<!-- Transaction Modal End -->
|
||||||
</body>
|
</body>
|
||||||
<script>
|
<script>
|
||||||
@ -142,6 +148,13 @@
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const category =
|
||||||
|
{
|
||||||
|
name_input: document.getElementById('category_name'),
|
||||||
|
parent_select: document.getElementById('category_parent'),
|
||||||
|
add_button: document.getElementById('category_add'),
|
||||||
|
};
|
||||||
|
|
||||||
// Global variables.
|
// Global variables.
|
||||||
const global_data =
|
const global_data =
|
||||||
{
|
{
|
||||||
@ -153,7 +166,8 @@
|
|||||||
process_index: 0,
|
process_index: 0,
|
||||||
unprocessed_cnt: function()
|
unprocessed_cnt: function()
|
||||||
{
|
{
|
||||||
return this.unprocessed_indices.length;
|
const diff = this.unprocessed_indices.length - 1 - this.process_index
|
||||||
|
return diff >= 0 ? diff : 0;
|
||||||
},
|
},
|
||||||
get_current: function()
|
get_current: function()
|
||||||
{
|
{
|
||||||
@ -303,7 +317,7 @@
|
|||||||
{
|
{
|
||||||
x_action.split.container.style.display = 'block';
|
x_action.split.container.style.display = 'block';
|
||||||
const transaction = global_data.transaction.get_current();
|
const transaction = global_data.transaction.get_current();
|
||||||
x_action.split.amount.value = transaction.amount / 2;
|
x_action.split.type.onchange();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -315,11 +329,11 @@
|
|||||||
{
|
{
|
||||||
const selected = x_action.split.type.value;
|
const selected = x_action.split.type.value;
|
||||||
const transaction = global_data.transaction.get_current();
|
const transaction = global_data.transaction.get_current();
|
||||||
if (selected == "Splitwise")
|
if (selected == 2) // Splitwise
|
||||||
{
|
{
|
||||||
x_action.split.amount.value = transaction.amount / 2;
|
x_action.split.amount.value = transaction.amount / 2;
|
||||||
}
|
}
|
||||||
else
|
else if (selected == 3) //SpaceX
|
||||||
{
|
{
|
||||||
x_action.split.amount.value = transaction.amount;
|
x_action.split.amount.value = transaction.amount;
|
||||||
}
|
}
|
||||||
@ -346,9 +360,43 @@
|
|||||||
x_action.subcategory.appendChild(opt);
|
x_action.subcategory.appendChild(opt);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
category.add_button.onclick = function()
|
||||||
|
{
|
||||||
|
const body = {};
|
||||||
|
body.name = category.name_input.value;
|
||||||
|
const selected = category.parent_select.value;
|
||||||
|
if (selected != "none" )
|
||||||
|
{
|
||||||
|
body.parent = selected;
|
||||||
|
}
|
||||||
|
const url = "/categories";
|
||||||
|
const handler =
|
||||||
|
{
|
||||||
|
success: function()
|
||||||
|
{
|
||||||
|
get_categories();
|
||||||
|
},
|
||||||
|
error: function()
|
||||||
|
{
|
||||||
|
alert("Failed to add category");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
http_request(url, handler, "POST", body);
|
||||||
|
|
||||||
|
category.parent_select.value = "none";
|
||||||
|
category.name_input.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
category.name_input.onkeypress = function(event) {
|
||||||
|
if (event.key === "Enter")
|
||||||
|
{
|
||||||
|
event.preventDefault();
|
||||||
|
category.add_button.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
function reset_transaction_ui(close_modal=true)
|
function reset_transaction_ui(close_modal=true)
|
||||||
{
|
{
|
||||||
// Reset all stateful inputs.
|
// Reset all stateful inputs.
|
||||||
@ -359,11 +407,13 @@
|
|||||||
|
|
||||||
x_action.split.container.style.display = 'none';
|
x_action.split.container.style.display = 'none';
|
||||||
x_action.split.enabled.checked = false;
|
x_action.split.enabled.checked = false;
|
||||||
|
x_action.split.notes.value = "";
|
||||||
x_action.category.innerHTML = "";
|
x_action.category.innerHTML = "";
|
||||||
x_action.subcategory.innerHTML = "";
|
x_action.subcategory.innerHTML = "";
|
||||||
x_action.subcategory.disabled = "true";
|
x_action.subcategory.disabled = "true";
|
||||||
x_action.notes.value = "";
|
x_action.notes.value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
function update_transaction_ui(transaction)
|
function update_transaction_ui(transaction)
|
||||||
{
|
{
|
||||||
// Reset old state.
|
// Reset old state.
|
||||||
@ -408,6 +458,7 @@
|
|||||||
{
|
{
|
||||||
success: function(data) {
|
success: function(data) {
|
||||||
global_data.categories = data
|
global_data.categories = data
|
||||||
|
update_category_ui();
|
||||||
},
|
},
|
||||||
error: function() {
|
error: function() {
|
||||||
console.error("Failed to get categories");
|
console.error("Failed to get categories");
|
||||||
@ -416,6 +467,22 @@
|
|||||||
http_request(request_url, handler);
|
http_request(request_url, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function update_category_ui()
|
||||||
|
{
|
||||||
|
function add_opt(key)
|
||||||
|
{
|
||||||
|
const opt = document.createElement("option");
|
||||||
|
opt.value = key;
|
||||||
|
opt.innerText = key;
|
||||||
|
|
||||||
|
category.parent_select.appendChild(opt);
|
||||||
|
}
|
||||||
|
|
||||||
|
category.parent_select.innerHTML = "";
|
||||||
|
add_opt("none");
|
||||||
|
for (key in global_data.categories) add_opt(key);
|
||||||
|
}
|
||||||
|
|
||||||
function http_request(url, response_handler, method="GET", body=null)
|
function http_request(url, response_handler, method="GET", body=null)
|
||||||
{
|
{
|
||||||
let request = new XMLHttpRequest();
|
let request = new XMLHttpRequest();
|
||||||
|
8
todo.txt
8
todo.txt
@ -4,11 +4,11 @@ Every week, I need to:
|
|||||||
2. Update SplitWise for shared purchases
|
2. Update SplitWise for shared purchases
|
||||||
|
|
||||||
# Tasks
|
# Tasks
|
||||||
|
xParse capital one statement
|
||||||
## Parse capital one statement
|
|
||||||
x Given a PDF bank statement from capital one, extract the transaction date, description, and amount from each transaction
|
x Given a PDF bank statement from capital one, extract the transaction date, description, and amount from each transaction
|
||||||
xStore the data in a SQL lite database
|
xStore the data in a SQL lite database
|
||||||
x Create database schema
|
x Create database schema
|
||||||
x Automatically add capital one transactions to database
|
x Automatically add capital one transactions to database
|
||||||
Add web page to categorize transactions
|
xAdd web page to categorize transactions
|
||||||
Add web page to add categories
|
xAdd web page to add categories
|
||||||
|
Visualize spend for each month
|
Loading…
Reference in New Issue
Block a user