Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/akevalion/life_cost/llms.txt

Use this file to discover all available pages before exploring further.

Life Cost persists all financial data — wallets, transactions, categories, tags, and user accounts — in a MySQL 8.0 database. The application uses Flask-SQLAlchemy as its ORM and creates all required tables automatically the first time the server starts. No separate migration step is needed for a fresh installation.

Requirements

  • MySQL 8.0 (or a compatible MariaDB version)
  • The database and user must be created before starting the app
  • The mysqlclient Python package (included in requirements.txt) is used as the database driver
The easiest way to satisfy these requirements is with the bundled Docker Compose setup described at the bottom of this page.

Connection String Format

The database connection is configured entirely through the DATABASE_URL environment variable. SQLAlchemy expects a URL in the following format:
mysql://user:password@host:port/dbname
SegmentDescriptionExample
userMySQL usernameadmin
passwordMySQL password for that usersecret
hostHostname or IP of the MySQL serverlocalhost
portMySQL port (optional; defaults to 3306)3306
dbnameName of the target databaselife_db
Example:
DATABASE_URL=mysql://admin:secret@localhost/life_db

Schema Overview

All tables are defined as SQLAlchemy models in app/models.py. The following tables are created automatically by db.create_all() on startup.

user

Stores authenticated user accounts. Populated on first Google sign-in.
ColumnTypeNotes
idInteger (PK)Auto-increment primary key
usernameString(150)Unique; synced from Google profile name
emailString(150)Unique; synced from Google account email
pictureString(300)URL of Google profile picture; nullable
last_visited_wallet_idInteger (FK)Foreign key → wallet.id; nullable

wallet

Represents a spending wallet (e.g., a budget or account).
ColumnTypeNotes
idInteger (PK)Auto-increment primary key
nameString(150)Unique; required
descriptionString(300)Optional

user_wallet

Many-to-many association table linking users to wallets. Every new wallet is automatically associated with all existing users.
ColumnTypeNotes
user_idInteger (PK/FK)Foreign key → user.id
wallet_idInteger (PK/FK)Foreign key → wallet.id
created_atDateTimeDefaults to UTC timestamp at insert

category

Represents a spending category (tag label). Categories can be nested via category_parent.
ColumnTypeNotes
idInteger (PK)Auto-increment primary key
nameString(200)Required
category_parentIntegerOptional self-reference to a parent category
number_of_operationIntegerCount of operations using this category

money_transfer

The core financial record — a single income or expense entry.
ColumnTypeNotes
idInteger (PK)Auto-increment primary key
wallet_idIntegerReferences wallet.id (no FK constraint at DB level)
amountFloatPositive = expense, negative = income; required
descriptionString(200)Free-text note; required
created_atDateTimeDefaults to UTC timestamp at insert
modifed_atDateTimeDefaults to UTC timestamp at insert; updated on edit
user_idIntegerReferences user.id (no FK constraint at DB level); required

tag

Join table connecting a money_transfer record to one or more category entries.
ColumnTypeNotes
idInteger (PK)Auto-increment primary key
category_idInteger (FK)Foreign key → category.id
money_transfer_idInteger (FK)Foreign key → money_transfer.id

Automatic Table Creation

On every application startup, Flask-SQLAlchemy calls db.create_all() inside the app context:
with app.app_context():
    db.create_all()
This inspects all registered models and issues CREATE TABLE IF NOT EXISTS statements for any tables that do not yet exist. It is safe to run against an already-initialised database — existing tables and data are never touched.
db.create_all() does not run schema migrations. If you add a new column or change a column type in a model after the table has already been created, the change will not be applied automatically. You must either apply the change manually with raw SQL or introduce a migration tool such as Flask-Migrate / Alembic.
-- Example: manually adding a column that was added to a model
ALTER TABLE money_transfer ADD COLUMN currency VARCHAR(10) DEFAULT 'USD';

Using the Bundled Docker MySQL

The docker-compose.yml file in the repository starts a MySQL 8.0 container alongside the application. The relevant portion of the file is:
services:
  mysql:
    image: mysql:8.0
    container_name: mysql_container
    environment:
      MYSQL_ROOT_PASSWORD: Spigit123!
      MYSQL_DATABASE: life_db
      MYSQL_USER: admin
      MYSQL_PASSWORD: 123
    volumes:
      - mysql_data:/var/lib/mysql
    ports:
      - "3306:3306"

  phpmyadmin:
    image: phpmyadmin/phpmyadmin
    container_name: phpmyadmin
    environment:
      PMA_HOST: mysql_container
      MYSQL_ROOT_PASSWORD: Spigit123!
    ports:
      - "8080:80"
    depends_on:
      - mysql

volumes:
  mysql_data:
The compose file also starts a phpMyAdmin instance at http://localhost:8080, which gives you a browser-based GUI to inspect tables, run queries, and manage the database without installing any additional tooling. Database files are stored in the named volume mysql_data, so data persists across container restarts.
After starting the MySQL container, verify connectivity before launching the app by running a simple liveness query:
docker exec -it mysql_container mysql -u admin -p123 life_db -e "SELECT 1;"
A result of 1 confirms the server is reachable and the life_db database exists. If you are connecting from outside Docker, you can use any MySQL client:
mysql -h 127.0.0.1 -P 3306 -u admin -p123 life_db -e "SELECT 1;"

Build docs developers (and LLMs) love