-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from isaacnorman82/populate_example_data
Populate example data
- Loading branch information
Showing
17 changed files
with
734 additions
and
129 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 |
---|---|---|
@@ -1 +1,88 @@ | ||
# finances | ||
# Finances - By Isaac Norman | ||
|
||
This readme is just a placeholder of enough info to know what this repo is and run it with sample data. | ||
|
||
# About | ||
|
||
I had a spreadsheet for a long time that I kept all my personal finances in. It was good, but I always found it annoying to keep up to date, copying and pasting stuff in. Also, with my excel skills at least, it didn't really scale well if I wanted to make a new visualisation or remap data. | ||
|
||
I finally had a bit of time and thought I would kill two birds with one stone: replace the spreadsheet with a nice app and also create a project that demonstrated my skills. | ||
|
||
I have managed to create an app that should replace the spreadsheet. However, it's taken a lot longer than I really had time for and it's the opposite of polished code I'd love to show off. I'm mostly a backend developer and a LOT of this work was front end. One day I'll make the code nice but that's not now, so no judging. | ||
|
||
# What does it do? | ||
|
||
Basically you can ingest different data files - ofx, csv - and build up a database of accounts, transactions and data points (currently about job/salary/tax) and then view it all in a web front end. | ||
|
||
## Features | ||
- Summary Page | ||
- Balance over time | ||
- Wealth pie chart | ||
- Stacked account bar chart (accounts summed over time) | ||
- Accounts Page | ||
- View all accounts and navigate to Account Details | ||
- Account Details Page | ||
- List of monthly transactions (like on your banking app) | ||
- Balance over time | ||
- value vs contributions for account types that grow such as share ISAs, houses etc. | ||
- link to account login (i.e. your banking web page) | ||
- Taxable Income Page | ||
- visualisations on tax, income, salary and career | ||
- General Features | ||
- Interpolation of missing transactional data (i.e. if you only have the odd value of a pension/asset it will work out the missing values) | ||
- All filterable by account type and time range | ||
|
||
# What is incomplete? | ||
- The code is extremely prototype - was rushed to just get functional. | ||
- Most of the ingest currently happens via python, the UI for this needs implementing | ||
- Editing accounts and manually inputing transactions | ||
- A proper packed build of the front and back end | ||
- A proper docker image for the front and back end | ||
- Tests are basically a skeleton. They need writing around the sample data. | ||
- Plus lots more | ||
|
||
# What's the architecture? | ||
|
||
Backend: Python FastAPI, SqlAlchemy, Pydantic, Alembic | ||
Frontend: Vue 3, Vuetify 3, TypeScript | ||
DB: Dockerised Postgres | ||
|
||
# How do I run it? | ||
|
||
I will in future build both the front and backend and include in docker images, but for now: | ||
|
||
- check out the code | ||
- create a python venv and install the requirements.txt | ||
- run the docker compose: | ||
``` | ||
docker compose up -d | ||
``` | ||
- initialise the db with alembic: | ||
``` | ||
alembic upgrade head | ||
``` | ||
- optionally load some sample data (recommend if you want to just give it a quick try) | ||
``` | ||
python load_sample_data.py | ||
``` | ||
- run the backend | ||
``` | ||
fastapi dev backend/main.py | ||
``` | ||
- install deps for the frontend | ||
``` | ||
cd frontend | ||
npm install | ||
``` | ||
- run the frontend | ||
``` | ||
cd frontend | ||
npm run dev | ||
``` | ||
- navigate to the frontend web address: http://localhost:3000/ | ||
- if you want to erase the db and start again | ||
``` | ||
docker compose down db | ||
docker volume rm finances_db | ||
docker compose up db -d | ||
``` |
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
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
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 |
---|---|---|
@@ -1,11 +1,13 @@ | ||
black==24.4.2 | ||
isort==5.13.2 | ||
SQLAlchemy==2.0.30 | ||
SQLAlchemy-Utils==0.41.2 | ||
fastapi==0.111.0 | ||
psycopg2==2.9.9 | ||
python-dateutil==2.9.0.post0 | ||
ofxtools==0.9.5 | ||
httpx==0.27.0 | ||
alembic==1.13.2 | ||
pytest==8.3.2 | ||
pytest-asyncio==0.23.8 | ||
pytest-asyncio==0.23.8 | ||
pytest-postgresql==6.0.0 |
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
File renamed without changes.
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,93 @@ | ||
import os | ||
import uuid | ||
from datetime import datetime | ||
from decimal import Decimal | ||
from typing import List | ||
|
||
import pytest | ||
from pydantic import BaseModel | ||
from sqlalchemy import create_engine | ||
from sqlalchemy.orm import sessionmaker | ||
from sqlalchemy_utils import create_database, database_exists, drop_database | ||
|
||
from backend import crud | ||
from backend.api_models import AccountCreate, AccountType, IngestType | ||
from backend.db import Base, get_db_session | ||
from backend.main import app | ||
from backend.test.sample_data_utils import ( | ||
create_sample_accounts, | ||
create_sample_transactions, | ||
) | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def unique_test_db_name(request): | ||
worker_id = getattr(request.config, "workerinput", {}).get("workerid", "master") | ||
return f"test_db_{worker_id}_{uuid.uuid4().hex}" | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def db_engine(unique_test_db_name): | ||
base_url = "postgresql://postgres:[email protected]/" | ||
test_db_url = f"{base_url}{unique_test_db_name}" | ||
|
||
if not database_exists(test_db_url): | ||
create_database(test_db_url) | ||
|
||
engine = create_engine(test_db_url) | ||
yield engine | ||
|
||
engine.dispose() # Ensure all connections are closed | ||
drop_database(test_db_url) | ||
|
||
|
||
@pytest.fixture(scope="function", autouse=True) | ||
def db_session(db_engine): | ||
""" | ||
Automatically use the db_session fixture in all tests. | ||
""" | ||
Base.metadata.create_all(bind=db_engine) | ||
TestingSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=db_engine) | ||
session = TestingSessionLocal() | ||
|
||
yield session | ||
|
||
session.close() # Ensure session is closed after each test | ||
Base.metadata.drop_all(bind=db_engine) # Cleanup tables after each test | ||
|
||
|
||
@pytest.fixture(scope="function", autouse=True) | ||
def setup_test_environment(db_session): | ||
""" | ||
Override the FastAPI dependencies to use the test database session. | ||
""" | ||
|
||
def get_db_override(): | ||
yield db_session | ||
|
||
app.dependency_overrides[get_db_session] = get_db_override | ||
|
||
yield # This allows the fixture to run both at setup and teardown | ||
|
||
app.dependency_overrides.clear() # Clear overrides after the session | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def sample_accounts(): | ||
return create_sample_accounts() | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def sample_transactions(): | ||
return create_sample_transactions() | ||
|
||
|
||
@pytest.fixture(scope="function") | ||
def insert_sample_accounts(db_session, sample_accounts): | ||
crud.create_accounts(db_session=db_session, accounts=sample_accounts) | ||
|
||
|
||
@pytest.fixture(scope="function") | ||
def insert_sample_accounts_and_transactions(db_session, sample_accounts, sample_transactions): | ||
crud.create_accounts(db_session=db_session, accounts=sample_accounts) | ||
crud.create_transactions(db_session=db_session, transactions=sample_transactions) |
Oops, something went wrong.