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

#6 AS: dockerized the app, added CI #7

Merged
merged 3 commits into from
Mar 26, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: CI

on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
setup-backend:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.8'

- name: Install backend dependencies
run: |
cd backend
pip install -r requirements.txt

- name: Run backend tests
run: |
cd backend
python -m unittest discover -s tests

setup-frontend:
needs: setup-backend
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '20'

- name: Install frontend dependencies
run: |
cd frontend
npm install

- name: Run frontend lint
run: |
cd frontend
npm run lint

- name: Run frontend tests
run: |
cd frontend
npm test

check-docker-compose:
needs: [setup-backend, setup-frontend]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2

- name: Start services
run: docker-compose up -d

- name: Wait for vue-frontend service to become healthy
run: |
echo "Waiting for vue-frontend service to become healthy..."
RETRIES=10
while [ $RETRIES -gt 0 ]; do
HEALTH_STATUS=$(docker inspect --format='{{.State.Health.Status}}' $(docker-compose ps -q vue-frontend))
if [ "$HEALTH_STATUS" == "healthy" ]; then
echo "vue-frontend service is healthy."
break
else
echo "Waiting for vue-frontend service to become healthy. Current status: $HEALTH_STATUS"
sleep 10
RETRIES=$((RETRIES-1))
fi
done

if [ $RETRIES -le 0 ]; then
echo "vue-frontend service did not become healthy in time."
exit 1
fi

- name: Shutdown services
run: docker-compose down
19 changes: 18 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# Self-Budgeting Tool
# Self-Budgeting Tool ![ workflow](https://github.com/devzero-inc/self-budgeting-tool/actions/workflows/main.yml/badge.svg)

## Overview
The Self-Budgeting Tool is a comprehensive web application designed to empower users with complete control over their finances. Track income, expenses, recurring and fixed costs, savings, loans, and more in one intuitive interface. Make informed decisions and achieve your financial goals with ease.

![frontend](https://github.com/devzero-inc/self-budgeting-tool/assets/93814858/ab9d0e56-df58-45fa-9df3-d3de658db4a7)

## Features

### Income and Expenses Tracking
Expand Down Expand Up @@ -52,3 +54,18 @@ MySQL is the world's most popular open-source database. With its proven performa
**Caching and Performance Optimization**: While not specified in the initial setup, implementing caching with tools such as Redis could significantly enhance the application's performance. It would be particularly beneficial for quickly accessing frequently used data, such as monthly expense summaries or savings progress reports.

**Response Handling**: After the backend processes a request, including data storage, updates, or retrieval, it generates a response that is sent back to the frontend via the middleware. The Vue.js application then updates the user interface accordingly, reflecting the new or modified data. This ensures that users have real-time access to their financial information, contributing to a seamless and efficient user experience.

## Installation

### Prerequisites
- Docker

Run locally:
```bash
git clone https://github.com/devzero-inc/self-budgeting-tool.git
cd self-budgeting-tool
docker compose up
```
App will be running on ```PORT:4173``` -> [http://localhost:4173/](http://localhost:4173/)

Now just go to [http://localhost:4173/](http://localhost:4173/) and explore the application.
7 changes: 7 additions & 0 deletions backend/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
__pycache__
venv
.dockerignore
Dockerfile
.env
.gitignore
docker-compose.yml
16 changes: 16 additions & 0 deletions backend/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
FROM python:3.8-slim

WORKDIR /app

COPY . /app

RUN apt-get update && \
apt-get install -y curl && \
rm -rf /var/lib/apt/lists/* && \
pip install --no-cache-dir -r requirements.txt

EXPOSE 5000

ENV FLASK_ENV=production

CMD ["flask", "run", "--host=0.0.0.0"]
81 changes: 6 additions & 75 deletions backend/app.py
Original file line number Diff line number Diff line change
@@ -1,83 +1,14 @@
from flask import Flask, jsonify, request
import mysql.connector
from mysql.connector import Error
import os
from flask import Flask
from flask_cors import CORS
from dotenv import load_dotenv
from flask_cors import CORS

app = Flask(__name__)
CORS(app)
import routes

load_dotenv()

DATABASE_CONFIG = {
'host': os.getenv('DB_HOST'),
'database': os.getenv('DB_DATABASE'),
'user': os.getenv('DB_USER'),
'password': os.getenv('DB_PASSWORD')
}

def get_db_connection():
try:
conn = mysql.connector.connect(**DATABASE_CONFIG)
if conn.is_connected():
return conn
except Error as e:
print(f"Error connecting to MySQL Database: {e}")
return None

@app.route('/accounts', methods=['GET'])
def get_accounts():
conn = get_db_connection()
if conn:
cursor = conn.cursor(dictionary=True)
cursor.execute('SELECT * FROM accounts')
accounts = cursor.fetchall()
cursor.close()
conn.close()
return jsonify({"data": accounts})
else:
return jsonify({"error": "Database connection failed"}), 500
app = Flask(__name__)
CORS(app)

@app.route('/transactions', methods=['GET'])
def get_transactions():
conn = get_db_connection()
if conn:
cursor = conn.cursor(dictionary=True)
cursor.execute('SELECT * FROM transactions')
transactions = cursor.fetchall()
cursor.close()
conn.close()
return jsonify({"data": transactions})
else:
return jsonify({"error": "Database connection failed"}), 500

@app.route('/transactions', methods=['POST'])
def add_transaction():
transaction_data = request.json
conn = get_db_connection()
if conn:
cursor = conn.cursor()
cursor.execute(
'INSERT INTO transactions '
'(account_id, bank_name, date, type, payee, amount, category) '
'VALUES (%s, %s, %s, %s, %s, %s, %s)',
(
transaction_data['account_id'],
transaction_data['bank_name'],
transaction_data['date'],
transaction_data['type'],
transaction_data['payee'],
transaction_data['amount'],
transaction_data['category']
)
)
conn.commit()
cursor.close()
conn.close()
return jsonify({"message": "Transaction added successfully"}), 201
else:
return jsonify({"error": "Database connection failed"}), 500
app.register_blueprint(routes.api)

if __name__ == "__main__":
app.run(debug=True)
8 changes: 8 additions & 0 deletions backend/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import os

DATABASE_CONFIG = {
'host': os.getenv('DB_HOST'),
'database': os.getenv('DB_DATABASE'),
'user': os.getenv('DB_USER'),
'password': os.getenv('DB_PASSWORD')
}
49 changes: 49 additions & 0 deletions backend/databaseService.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from db import get_db_connection

def fetch_all_accounts():
conn = get_db_connection()
if conn:
cursor = conn.cursor(dictionary=True)
cursor.execute('SELECT * FROM accounts')
accounts = cursor.fetchall()
cursor.close()
conn.close()
return accounts, None
else:
return None, "Database connection failed"

def fetch_all_transactions():
conn = get_db_connection()
if conn:
cursor = conn.cursor(dictionary=True)
cursor.execute('SELECT * FROM transactions')
transactions = cursor.fetchall()
cursor.close()
conn.close()
return transactions, None
else:
return None, "Database connection failed"

def insert_transaction(transaction_data):
conn = get_db_connection()
if conn:
cursor = conn.cursor()
cursor.execute(
'INSERT INTO transactions (account_id, bank_name, date, type, payee, amount, category) '
'VALUES (%s, %s, %s, %s, %s, %s, %s)',
(
transaction_data['account_id'],
transaction_data['bank_name'],
transaction_data['date'],
transaction_data['type'],
transaction_data['payee'],
transaction_data['amount'],
transaction_data['category']
)
)
conn.commit()
cursor.close()
conn.close()
return "Transaction added successfully", None
else:
return None, "Database connection failed"
13 changes: 13 additions & 0 deletions backend/db.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import mysql.connector
from mysql.connector import Error
from config import DATABASE_CONFIG

def get_db_connection():
try:
conn = mysql.connector.connect(**DATABASE_CONFIG)
if conn.is_connected():
print("Connected to MySQL database")
return conn
except Error as e:
print(f"Error connecting to MySQL Database: {e}")
return None
56 changes: 56 additions & 0 deletions backend/init-db/db-init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
CREATE TABLE accounts (
id char(36) NOT NULL DEFAULT (UUID()),
account_number varchar(20) NOT NULL,
account_holder varchar(255) NOT NULL,
account_type enum('savings','checking','loan','credit_card') NOT NULL,
balance decimal(15,2) NOT NULL,
bank_name varchar(255) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id)
);

CREATE TABLE transactions (
id char(36) NOT NULL DEFAULT (UUID()),
date date NOT NULL,
account_id char(36) NOT NULL,
bank_name varchar(255) NOT NULL,
payee varchar(255) DEFAULT NULL,
category varchar(255) DEFAULT NULL,
amount decimal(10,2) NOT NULL,
type enum('debit','credit') NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (account_id) REFERENCES accounts(id)
);

INSERT INTO accounts (account_number, account_holder, account_type, balance, bank_name) VALUES
('1234567890', 'John Doe', 'savings', 1000, 'HSBC Bank'),
('2345678901', 'John Doe', 'checking', 2000, 'Chase Bank'),
('3456789012', 'John Doe', 'loan', 3000, 'Wells Fargo'),
('4567890123', 'John Doe', 'savings', 4000, 'Bank of America');

INSERT INTO transactions (date, account_id, bank_name, payee, category, amount, type) VALUES
('2024-03-01', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Netflix', 'Entertainment', -50.00, 'debit'),
('2024-03-02', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'Amazon', 'Shopping', -120.00, 'debit'),
('2024-03-03', (SELECT id FROM accounts WHERE bank_name = 'Wells Fargo'), 'Wells Fargo', 'Apple', 'Electronics', -200.00, 'debit'),
('2024-03-04', (SELECT id FROM accounts WHERE bank_name = 'Bank of America'), 'Bank of America', 'Spotify', 'Entertainment', -10.00, 'debit'),
('2024-03-05', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Adobe', 'Software', -50.00, 'debit'),
('2024-03-06', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'Local Cafe', 'Dining', -30.00, 'debit'),
('2024-03-07', (SELECT id FROM accounts WHERE bank_name = 'Wells Fargo'), 'Wells Fargo', 'Gym', 'Health', -60.00, 'debit'),
('2024-03-08', (SELECT id FROM accounts WHERE bank_name = 'Bank of America'), 'Bank of America', 'Electric Company', 'Utilities', -150.00, 'debit'),
('2024-03-09', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Water Supply', 'Utilities', -80.00, 'debit'),
('2024-03-10', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'Internet Provider', 'Utilities', -60.00, 'debit'),
('2024-03-11', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Mortgage', 'Deposit', 2000.00, 'credit'),
('2024-03-12', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'House Asset', 'Deposit', 1500.00, 'credit'),
('2024-03-13', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Salary', 'Deposit', 3000.00, 'credit'),
('2024-03-14', (SELECT id FROM accounts WHERE bank_name = 'Bank of America'), 'Bank of America', 'Investment Return', 'Deposit', 1200.00, 'credit'),
('2024-03-15', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Mortgage', 'Deposit', 2200.00, 'credit'),
('2024-03-16', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'House Asset', 'Deposit', 1800.00, 'credit'),
('2024-03-17', (SELECT id FROM accounts WHERE bank_name = 'Wells Fargo'), 'Wells Fargo', 'Salary', 'Deposit', 2500.00, 'credit'),
('2024-03-18', (SELECT id FROM accounts WHERE bank_name = 'Bank of America'), 'Bank of America', 'Investment Return', 'Deposit', 1300.00, 'credit'),
('2024-03-19', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Mortgage', 'Deposit', 2100.00, 'credit'),
('2024-03-20', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'House Asset', 'Deposit', 1600.00, 'credit'),
('2024-03-21', (SELECT id FROM accounts WHERE bank_name = 'Wells Fargo'), 'Wells Fargo', 'Salary', 'Deposit', 2800.00, 'credit'),
('2024-03-22', (SELECT id FROM accounts WHERE bank_name = 'Bank of America'), 'Bank of America', 'Investment Return', 'Deposit', 1500.00, 'credit'),
('2024-03-23', (SELECT id FROM accounts WHERE bank_name = 'HSBC Bank'), 'HSBC Bank', 'Mortgage', 'Deposit', 2300.00, 'credit'),
('2024-03-24', (SELECT id FROM accounts WHERE bank_name = 'Chase Bank'), 'Chase Bank', 'House Asset', 'Deposit', 1700.00, 'credit');
Loading
Loading