test
This commit is contained in:
0
src/__init__.py
Normal file
0
src/__init__.py
Normal file
0
src/components/__init__.py
Normal file
0
src/components/__init__.py
Normal file
48
src/components/bar_chart.py
Normal file
48
src/components/bar_chart.py
Normal 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)
|
45
src/components/category_dropdown.py
Normal file
45
src/components/category_dropdown.py
Normal 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
11
src/components/ids.py
Normal 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
29
src/components/layout.py
Normal 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),
|
||||
],
|
||||
)
|
40
src/components/month_dropdown.py
Normal file
40
src/components/month_dropdown.py
Normal 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,
|
||||
),
|
||||
]
|
||||
)
|
41
src/components/pie_chart.py
Normal file
41
src/components/pie_chart.py
Normal 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)
|
36
src/components/year_dropdown.py
Normal file
36
src/components/year_dropdown.py
Normal 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
0
src/data/__init__.py
Normal file
25
src/data/loader.py
Normal file
25
src/data/loader.py
Normal 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
25
src/data/loader_gz.py
Normal 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
|
Reference in New Issue
Block a user