Skip to content

Commit

Permalink
Merge pull request #20 from guillaumepot/dev-0.1.3
Browse files Browse the repository at this point in the history
Dev 0.1.3
  • Loading branch information
guillaumepot authored Jul 4, 2024
2 parents 2184b2e + 8c1d7a3 commit 310b81c
Show file tree
Hide file tree
Showing 10 changed files with 313 additions and 107 deletions.
2 changes: 2 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# API configuration
UVICORN_HOST=0.0.0.0
UVICORN_PORT=8000
API_VERSION=0.1.3
API_STATE=DEV

# API Auth configuration
ALGORITHM="HS256"
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/.env
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
api_version=0.1.2
streamlit_version=0.1.2
api_version=0.1.3
streamlit_version=0.1.3
18 changes: 13 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ This is a personal project to check my bank accounts. It is currently in develop

## Changelogs

[v0.1.3](./changelogs/0.1.3.md)
[v0.1.2](./changelogs/0.1.2.md)
[v0.1.1](./changelogs/0.1.1.md)
[v0.1.0](./changelogs/0.1.0.md)
Expand All @@ -153,28 +154,35 @@ This is a personal project to check my bank accounts. It is currently in develop

**[Done]**
```
0.1.3 - Mini Analytics update
- Streamlit Analytics page now display charts based on transactions
- Clean Streamlit code
0.1.2 - Repo Update
- Review README.MD
0.1.0
- First version of the app containing an API & a Streamlit interface
0.1.1
- Refactor API files (cleaning & readability)
- Refactor Streamlit files (cleaning & readability)
0.1.0
- First version of the app containing an API & a Streamlit interface
```


**[Todo]**
```
0.1.3
0.1.4 - Api security
- Add a simple logger (API)
- Update API security (max requests, etc.)
0.1.5 - Streamlit code
- Streamlit refactorization
0.1.6 - CI Update
- Add Container tests (Github Actions)
- Add more unit tests, e.g., API tests
0.2.0
- Create an ETL pipline to get transaction datas, put raw datas in a storage; transform datas for analytics
- Add analytics charts (based on transactions) -> Analytics page ; Streamlit
0.3.0
- Use a ML pipeline to predict things (budgets, ..)
```
26 changes: 26 additions & 0 deletions changelogs/0.1.3.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
## Changelogs 0.1.3

---


**Repo**
```
-
```


**POSTGRES**
```
-
```

**API**
```
- Changed how vars api_version & api_state are loaded (Now from env var)
```

**Streamlit**
```
- Analytics page now display charts based on transactions
- Minor code changes
```
2 changes: 1 addition & 1 deletion docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ services:
image: unclad3610/personal_bank_app_streamlit:latest
container_name: bank_app_streamlit
environment:
- API_VERSION=0.1.0
- API_VERSION=0.1.3
- API_URL=http://bank_app_api:8000
ports:
- "8001:8001"
Expand Down
4 changes: 2 additions & 2 deletions src/api/api_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
VARS
"""
# API metadatas
api_version = "0.1.2"
current_state = "Prod"
api_version = os.getenv("API_VERSION", "0.1.3")
current_state = os.getenv("API_STATE", "DEV")

# UUID Generation
def generate_uuid():
Expand Down
2 changes: 1 addition & 1 deletion src/streamlit/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ WORKDIR /app
# Copy files
COPY ./requirements.txt /app
COPY ./streamlit.py /app

COPY ./api_requests_functions.py /app

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt
Expand Down
158 changes: 158 additions & 0 deletions src/streamlit/api_requests_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
import os
import pandas as pd
import requests
import streamlit as st


# API VARS
api_version = os.getenv("API_VERSION", "0.1.2")
api_url = os.getenv("API_URL", "http://localhost:8000")

login_placeholder = st.sidebar.empty()



def get_api_status() -> None:
"""
Request the api to get the status, block app if API is not responsding (or bad version)
"""
url = f"{api_url}/api/{api_version}/status"
response = requests.get(url)
if response.status_code != 200:
st.error("Error while checking API status.")
st.stop()


def validate_credentials(login_data: dict) -> None:
"""
This function will be called when the "Log In" button is pressed
"""
# Request API
url = f"{api_url}/api/{api_version}/login"
response = requests.post(url, data=login_data)
if response.status_code == 200:
access_token = response.json().get("access_token")
login_placeholder.empty()
st.session_state.access_token = access_token
st.sidebar.empty()

else:
st.sidebar.error("Incorrect Username or Password.")


def get_account_table() -> pd.DataFrame:
"""
"""
# Request API
url = f"{api_url}/api/{api_version}/table/account"
account_table = requests.get(url, headers=st.session_state.headers)
account_table = dict(account_table.json())
account_table = account_table['account table']

# Generate DF
df_accounts = pd.DataFrame(account_table, columns=["id", "name", "type", "balance", "owner_id", "created_at"])
df_accounts["balance"] = df_accounts["balance"].apply(lambda x: f"{x:.2f} €")
df_accounts.drop(columns=["id", "owner_id"], inplace=True)
df_accounts["created_at"] = df_accounts["created_at"].str.split("T").str[0]

return df_accounts

def get_budget_table() -> pd.DataFrame:
"""
"""
# Request API
url = f"{api_url}/api/{api_version}/table/budget"
budget_table = requests.get(url, headers=st.session_state.headers)
budget_table = budget_table.json()
budget_table = budget_table["budget table"]

# Generate DF
df_budgets = pd.DataFrame(budget_table, columns=["id", "name", "month", "amount","created_at"])
df_budgets["amount"] = df_budgets["amount"].apply(lambda x: f"{x:.2f} €")

# Create a dictionary mapping from id to name in df_budgets (used for df_transactions)
budget_id_to_name = pd.Series(df_budgets.name.values, index=df_budgets.id).to_dict()

df_budgets.drop(columns=["id"], inplace=True)
df_budgets["created_at"] = df_budgets["created_at"].str.split("T").str[0]

return df_budgets, budget_id_to_name


def get_transaction_table(budget_id_to_name) -> pd.DataFrame:
"""
"""
# Request API
url = f"{api_url}/api/{api_version}/table/transaction"
transaction_table = requests.get(url, headers=st.session_state.headers)
transaction_table = dict(transaction_table.json())
transaction_table = transaction_table['transaction table']
# Generate DF
df_transactions = pd.DataFrame(transaction_table, columns=["id", "date", "type", "amount", "origin_account", "destination_account", "budget", "recipient", "category", "description"])
df_transactions["date"] = df_transactions["date"].str.split("T").str[0]
df_transactions["amount"] = df_transactions["amount"].apply(lambda x: f"{x:.2f} €")

# Map each budget id in df_transactions to its corresponding name using the dictionary
df_transactions["budget_name"] = df_transactions["budget"].map(budget_id_to_name).fillna("None")
df_transactions.drop(columns=["id", "budget"], inplace=True)
df_transactions.rename(columns={"budget_name": "budget"}, inplace=True)
df_transactions = df_transactions[["date", "type", "amount", "origin_account", "destination_account", "budget", "recipient", "category", "description"]]

return df_transactions



def get_bar_chart(df, name, amount, legend=None):
"""
"""
x = df[name]
y = df[amount]
fig = plt.figure(figsize=(10, 5))
colors = cm.viridis(np.linspace(0, 1, len(x)))

bars = plt.bar(x, y, color=colors)

#if legend is None:
for bar in bars:
height = bar.get_height()
plt.text(bar.get_x() + bar.get_width() / 2, height - (height*0.05), f'{height}'+"€", ha='center', va='top', color='white')

plt.title(f"{name} expenses")
plt.xlabel(name, fontweight='bold')
plt.ylabel(amount, fontweight='bold')

if legend is not None:
labels = [elt for elt in legend]
plt.legend(bars, labels, title="Categories")

st.pyplot(fig)


def get_time_series(df):
"""
"""
# Convert 'date' to datetime format
df['date'] = pd.to_datetime(df['date'])

df['year_month'] = df['date'].dt.to_period('M')
df = df.groupby('year_month')['amount'].sum().reset_index()

# Calculate cumulative sum
df["cumsum"] = df["amount"].cumsum()

# Plot
fig = plt.figure(figsize=(10, 5))
plt.plot(df['year_month'].astype(str), df["cumsum"], color="blue") # Convert 'year_month' to string for plotting
plt.fill_between(df['year_month'].astype(str), df["cumsum"], color="blue", alpha=0.3)
plt.title("Cumulative expenses over time")
plt.xlabel("Date", fontweight='bold')
plt.ylabel("Amount", fontweight='bold')
st.pyplot(fig)
1 change: 1 addition & 0 deletions src/streamlit/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ jsonschema
jsonschema-specifications
markdown-it-py
MarkupSafe
matplotlib
mdurl
numexpr
numpy
Expand Down
Loading

0 comments on commit 310b81c

Please sign in to comment.