This commit is contained in:
2023-12-31 07:36:28 +01:00
parent 7bc80936a1
commit 38897888cf
782 changed files with 272213 additions and 0 deletions

0
src/__init__.py Normal file
View File

View File

View File

@ -0,0 +1,48 @@
import pandas as pd
import plotly.express as px
from dash import Dash, dcc, html
from dash.dependencies import Input, Output
from ..data.loader import DataSchema
from . import ids
def render(app: Dash, data: pd.DataFrame) -> html.Div:
@app.callback(
Output(ids.BAR_CHART, "children"),
[
Input(ids.YEAR_DROPDOWN, "value"),
Input(ids.MONTH_DROPDOWN, "value"),
Input(ids.CATEGORY_DROPDOWN, "value"),
],
)
def update_bar_chart(
years: list[str], months: list[str], categories: list[str]
) -> html.Div:
filtered_data = data.query(
"year in @years and month in @months and category in @categories"
)
if filtered_data.shape[0] == 0:
return html.Div("No data selected.", id=ids.BAR_CHART)
def create_pivot_table() -> pd.DataFrame:
pt = filtered_data.pivot_table(
values=DataSchema.AMOUNT,
index=[DataSchema.CATEGORY],
aggfunc="sum",
fill_value=0,
dropna=False,
)
return pt.reset_index().sort_values(DataSchema.AMOUNT, ascending=False)
fig = px.bar(
create_pivot_table(),
x=DataSchema.CATEGORY,
y=DataSchema.AMOUNT,
color=DataSchema.CATEGORY,
)
return html.Div(dcc.Graph(figure=fig), id=ids.BAR_CHART)
return html.Div(id=ids.BAR_CHART)

View File

@ -0,0 +1,45 @@
import pandas as pd
from dash import Dash, dcc, html
from dash.dependencies import Input, Output
from ..data.loader import DataSchema
from . import ids
def render(app: Dash, data: pd.DataFrame) -> html.Div:
all_categories: list[str] = data[DataSchema.CATEGORY].tolist()
unique_categories: list[str] = sorted(set(all_categories))
@app.callback(
Output(ids.CATEGORY_DROPDOWN, "value"),
[
Input(ids.YEAR_DROPDOWN, "value"),
Input(ids.MONTH_DROPDOWN, "value"),
Input(ids.SELECT_ALL_CATEGORIES_BUTTON, "n_clicks"),
],
)
def select_all_categories(years: list[str], months: list[str], _: int) -> list[str]:
filtered_data = data.query("year in @years and month in @months")
return sorted(set(filtered_data[DataSchema.CATEGORY].tolist()))
return html.Div(
children=[
html.H6("Category"),
dcc.Dropdown(
id=ids.CATEGORY_DROPDOWN,
options=[
{"label": category, "value": category}
for category in unique_categories
],
value=unique_categories,
multi=True,
placeholder="Select",
),
html.Button(
className="dropdown-button",
children=["Select All"],
id=ids.SELECT_ALL_CATEGORIES_BUTTON,
n_clicks=0,
),
],
)

11
src/components/ids.py Normal file
View File

@ -0,0 +1,11 @@
BAR_CHART = "bar-chart"
PIE_CHART = "pie-chart"
SELECT_ALL_CATEGORIES_BUTTON = "select-all-categories-button"
CATEGORY_DROPDOWN = "category-dropdown"
SELECT_ALL_MONTHS_BUTTON = "select-all-months-button"
MONTH_DROPDOWN = "month-dropdown"
YEAR_DROPDOWN = "year-dropdown"
SELECT_ALL_YEARS_BUTTON = "select-all-years-button"

29
src/components/layout.py Normal file
View File

@ -0,0 +1,29 @@
import pandas as pd
from dash import Dash, html
from src.components import (
bar_chart,
category_dropdown,
month_dropdown,
pie_chart,
year_dropdown,
)
def create_layout(app: Dash, data: pd.DataFrame) -> html.Div:
return html.Div(
className="app-div",
children=[
html.H1(app.title),
html.Hr(),
html.Div(
className="dropdown-container",
children=[
year_dropdown.render(app, data),
month_dropdown.render(app, data),
category_dropdown.render(app, data),
],
),
bar_chart.render(app, data),
pie_chart.render(app, data),
],
)

View File

@ -0,0 +1,40 @@
import pandas as pd
from dash import Dash, dcc, html
from dash.dependencies import Input, Output
from ..data.loader import DataSchema
from . import ids
def render(app: Dash, data: pd.DataFrame) -> html.Div:
all_months: list[str] = data[DataSchema.MONTH].tolist()
unique_months = sorted(set(all_months))
@app.callback(
Output(ids.MONTH_DROPDOWN, "value"),
[
Input(ids.YEAR_DROPDOWN, "value"),
Input(ids.SELECT_ALL_MONTHS_BUTTON, "n_clicks"),
],
)
def select_all_months(years: list[str], _: int) -> list[str]:
filtered_data = data.query("year in @years")
return sorted(set(filtered_data[DataSchema.MONTH].tolist()))
return html.Div(
children=[
html.H6("Month"),
dcc.Dropdown(
id=ids.MONTH_DROPDOWN,
options=[{"label": month, "value": month} for month in unique_months],
value=unique_months,
multi=True,
),
html.Button(
className="dropdown-button",
children=["Select All"],
id=ids.SELECT_ALL_MONTHS_BUTTON,
n_clicks=0,
),
]
)

View File

@ -0,0 +1,41 @@
import pandas as pd
import plotly.graph_objects as go
from dash import Dash, dcc, html
from dash.dependencies import Input, Output
from ..data.loader import DataSchema
from . import ids
def render(app: Dash, data: pd.DataFrame) -> html.Div:
@app.callback(
Output(ids.PIE_CHART, "children"),
[
Input(ids.YEAR_DROPDOWN, "value"),
Input(ids.MONTH_DROPDOWN, "value"),
Input(ids.CATEGORY_DROPDOWN, "value"),
],
)
def update_pie_chart(
years: list[str], months: list[str], categories: list[str]
) -> html.Div:
filtered_data = data.query(
"year in @years and month in @months and category in @categories"
)
if filtered_data.shape[0] == 0:
return html.Div("No data selected.", id=ids.PIE_CHART)
pie = go.Pie(
labels=filtered_data[DataSchema.CATEGORY].tolist(),
values=filtered_data[DataSchema.AMOUNT].tolist(),
hole=0.5,
)
fig = go.Figure(data=[pie])
fig.update_layout(margin={"t": 40, "b": 0, "l": 0, "r": 0})
fig.update_traces(hovertemplate="%{label}<br>$%{value:.2f}<extra></extra>")
return html.Div(dcc.Graph(figure=fig), id=ids.PIE_CHART)
return html.Div(id=ids.PIE_CHART)

View File

@ -0,0 +1,36 @@
import pandas as pd
from dash import Dash, dcc, html
from dash.dependencies import Input, Output
from ..data.loader import DataSchema
from . import ids
def render(app: Dash, data: pd.DataFrame) -> html.Div:
all_years: list[str] = data[DataSchema.YEAR].tolist()
unique_years = sorted(set(all_years), key=int)
@app.callback(
Output(ids.YEAR_DROPDOWN, "value"),
Input(ids.SELECT_ALL_YEARS_BUTTON, "n_clicks"),
)
def select_all_years(_: int) -> list[str]:
return unique_years
return html.Div(
children=[
html.H6("Year"),
dcc.Dropdown(
id=ids.YEAR_DROPDOWN,
options=[{"label": year, "value": year} for year in unique_years],
value=unique_years,
multi=True,
),
html.Button(
className="dropdown-button",
children=["Select All"],
id=ids.SELECT_ALL_YEARS_BUTTON,
n_clicks=0,
),
]
)

0
src/data/__init__.py Normal file
View File

25
src/data/loader.py Normal file
View File

@ -0,0 +1,25 @@
import pandas as pd
class DataSchema:
AMOUNT = "amount"
CATEGORY = "category"
DATE = "date"
MONTH = "month"
YEAR = "year"
def load_transaction_data(path: str) -> pd.DataFrame:
# load the data from the CSV file
data = pd.read_csv(
path,
dtype={
DataSchema.AMOUNT: float,
DataSchema.CATEGORY: str,
DataSchema.DATE: str,
},
parse_dates=[DataSchema.DATE],
)
data[DataSchema.YEAR] = data[DataSchema.DATE].dt.year.astype(str)
data[DataSchema.MONTH] = data[DataSchema.DATE].dt.month.astype(str)
return data

25
src/data/loader_gz.py Normal file
View File

@ -0,0 +1,25 @@
import pandas as pd
class DataSchema:
AMOUNT = "amount"
CATEGORY = "category"
DATE = "date"
MONTH = "month"
YEAR = "year"
def load_transaction_data(path: str) -> pd.DataFrame:
# load the data from the CSV file
data = pd.read_csv(
path,
dtype={
DataSchema.AMOUNT: float,
DataSchema.CATEGORY: str,
DataSchema.DATE: str,
},
parse_dates=[DataSchema.DATE],
)
data[DataSchema.YEAR] = data[DataSchema.DATE].dt.year.astype(str)
data[DataSchema.MONTH] = data[DataSchema.DATE].dt.month.astype(str)
return data