Skip to content

Commit

Permalink
HEA-592 first version of Inventory Dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
enjoki committed Jan 3, 2025
1 parent f821c6f commit fffa294
Show file tree
Hide file tree
Showing 34 changed files with 26,935 additions and 107 deletions.
148 changes: 98 additions & 50 deletions apps/viz/dash/inventory_dashboard/app.py
Original file line number Diff line number Diff line change
@@ -1,68 +1,98 @@
import logging
import plotly.express as px
from dash import dash_table, dcc, html
from dash.dependencies import Input, Output
from django_plotly_dash import DjangoDash
import dash_bootstrap_components as dbc
from datetime import datetime

from .functions import fetch_data, prepare_livelihood_data, prepare_wealth_group_data

# API Endpoints
LIVELIHOOD_STRATEGY_URL = "https://headev.fews.net/api/livelihoodstrategy/"
WEALTH_GROUP_URL = "https://headev.fews.net/api/wealthgroupcharacteristicvalue/"

# Fetch and prepare data
livelihood_data = fetch_data(LIVELIHOOD_STRATEGY_URL)
wealth_group_data = fetch_data(WEALTH_GROUP_URL)

livelihood_data = prepare_livelihood_data(livelihood_data)
wealth_group_data = prepare_wealth_group_data(wealth_group_data)
from .functions import clean_livelihood_data, clean_wealth_group_data

# Unique countries and livelihood zones for dropdowns
unique_countries = sorted(livelihood_data["country_code"].unique())
unique_zones = sorted(livelihood_data["livelihood_zone"].unique())
unique_countries = sorted(clean_livelihood_data["country_code"].dropna().unique())
unique_zones = sorted(clean_livelihood_data["livelihood_zone_baseline_label"].dropna().unique())

logger = logging.getLogger(__name__)

# Dash app
app = DjangoDash("Inventory_dashboard")
app = DjangoDash(
"Inventory_dashboard",
suppress_callback_exceptions=True,
external_stylesheets=[dbc.themes.BOOTSTRAP]
)
app.title = "HEA Dashboard"

# Layout
app.layout = html.Div(
[
html.H1("HEA Data Inventory Dashboard", style={"textAlign": "center"}),
dcc.Dropdown(
id="country-dropdown",
options=[{"label": country, "value": country} for country in unique_countries],
placeholder="Select Country",
multi=False,
),
dcc.Dropdown(
id="livelihood-zone-dropdown",
options=[{"label": zone, "value": zone} for zone in unique_zones],
placeholder="Select Livelihood Zone(s)",
multi=True,
html.Div(
[
html.Div(
[
dcc.Dropdown(
id="country-dropdown",
options=[{"label": country, "value": country} for country in unique_countries],
placeholder="Select Country(s)",
multi=True,
),
],
style={"flex": "1", "marginRight": "10px"}, # Set flex and spacing
),
html.Div(
[
dcc.Dropdown(
id="livelihood-zone-dropdown",
options=[{"label": zone, "value": zone} for zone in unique_zones],
placeholder="Select Livelihood Zone(s)",
multi=True,
),
],
style={"flex": "1"},
),
],
style={
"display": "flex",
"width": "100%",
"justifyContent": "space-between",
"marginBottom": "20px",
},
),
html.Div(
[
html.Div(dcc.Graph(id="wealth-chart"), style={"width": "48%", "display": "inline-block"}),
html.Div(dcc.Graph(id="livelihood-chart"), style={"width": "48%", "display": "inline-block"}),
]
html.Div(
dcc.Graph(id="wealth-chart"),
style={"width": "30%", "display": "inline-block"},
),
html.Div(
dcc.Graph(id="livelihood-chart"),
style={"width": "30%", "display": "inline-block"},
),
html.Div(
dcc.Graph(id="wealth-monthly-chart"),
style={"width": "30%", "display": "inline-block"},
),
],
),
html.Div(
[
html.H3("Data Table", style={"textAlign": "center"}),
dash_table.DataTable(
id="data-table",
columns=[
{"name": "Livelihood Zone", "id": "livelihood_zone"},
{"name": "Livelihood Zone", "id": "livelihood_zone_baseline_label"},
{"name": "Strategy Type Count", "id": "Count"},
{"name": "Wealth Characteristics Count", "id": "Wealth Characteristics Count"},
],
style_table={"overflowX": "auto"},
style_cell={"textAlign": "left"},
page_size=10,
page_size=12,
),
]
],
className="inventory-filter inventory-filter-last",
),
]
],
className="div-wrapper control-panel-wrapper",
)


Expand All @@ -72,54 +102,72 @@
Output("livelihood-zone-dropdown", "options"),
Output("wealth-chart", "figure"),
Output("livelihood-chart", "figure"),
Output("wealth-monthly-chart", "figure"),
Output("data-table", "data"),
],
[Input("country-dropdown", "value"), Input("livelihood-zone-dropdown", "value")],
)
def update_charts(selected_country, selected_zones):
# Filter data based on selected country
if selected_country:
filtered_livelihood = livelihood_data[livelihood_data["country_code"] == selected_country]
filtered_wealth = wealth_group_data[wealth_group_data["country_code"] == selected_country]
filtered_zones = sorted(filtered_livelihood["livelihood_zone"].unique())
def update_charts(selected_countries, selected_zones):
# Handle multi-country selection
if selected_countries:
filtered_livelihood = clean_livelihood_data[clean_livelihood_data["country_code"].isin(selected_countries)]
filtered_wealth = clean_wealth_group_data[clean_wealth_group_data["country_code"].isin(selected_countries)]
filtered_zones = sorted(filtered_livelihood["livelihood_zone_baseline_label"].unique())
else:
filtered_livelihood = livelihood_data
filtered_wealth = wealth_group_data
filtered_livelihood = clean_livelihood_data
filtered_wealth = clean_wealth_group_data
filtered_zones = unique_zones

# Update options for livelihood zone dropdown
zone_options = [{"label": zone, "value": zone} for zone in filtered_zones]

# Filter data based on selected livelihood zones
if selected_zones:
filtered_livelihood = filtered_livelihood[filtered_livelihood["livelihood_zone"].isin(selected_zones)]
filtered_wealth = filtered_wealth[filtered_wealth["livelihood_zone"].isin(selected_zones)]
filtered_livelihood = filtered_livelihood[filtered_livelihood["livelihood_zone_baseline_label"].isin(selected_zones)]
filtered_wealth = filtered_wealth[filtered_wealth["livelihood_zone_baseline_label"].isin(selected_zones)]

# Group data for charts
livelihood_grouped = (
filtered_livelihood.groupby(["livelihood_zone", "strategy_type_label"]).size().reset_index(name="Count")
filtered_livelihood.groupby(["livelihood_zone_baseline_label", "strategy_type_label"]).size().reset_index(name="Count")
)
wealth_grouped = filtered_wealth.groupby("livelihood_zone_baseline_label").size().reset_index(name="Wealth Characteristics Count")
wealth_monthly_grouped = (
filtered_wealth.groupby(["created_month", "livelihood_zone_baseline_label"]).size().reset_index(name="Wealth Characteristics Count")
)
wealth_grouped = filtered_wealth.groupby("livelihood_zone").size().reset_index(name="Wealth Characteristics Count")

wealth_fig = px.bar(
wealth_grouped,
x="livelihood_zone",
x="livelihood_zone_baseline_label",
y="Wealth Characteristics Count",
title="Wealth Characteristics per Baseline",
labels={"livelihood_zone": "Baseline", "Wealth Characteristics Count": "Count"},
labels={"livelihood_zone_baseline_label": "Baseline", "Wealth Characteristics Count": "No. of Wealth Characteristics"},
)

livelihood_fig = px.bar(
livelihood_grouped,
x="strategy_type_label",
y="Count",
color="livelihood_zone",
color="livelihood_zone_baseline_label",
title="Livelihood Strategies per Baseline",
labels={"strategy_type_label": "Strategy Type", "Count": "Count", "livelihood_zone": "Baseline"},
labels={"strategy_type_label": "Strategy Type", "Count": "No. of Livelihood Strategies", "livelihood_zone_baseline_label": "Baseline"},
)

return zone_options, wealth_fig, livelihood_fig, livelihood_grouped.to_dict("records")
# Stacked/multiple column chart for wealth characteristics by month
wealth_monthly_fig = px.bar(
wealth_monthly_grouped,
x="created_month",
y="Wealth Characteristics Count",
color="livelihood_zone_baseline_label",
barmode="stack", # Use 'group' for multiple column chart
title="Wealth Characteristics by Month and Baseline",
labels={
"created_month": "Month",
"Wealth Characteristics Count": "No. of Wealth Characteristics",
"livelihood_zone_baseline_label": "Baseline",
},
)

return zone_options, wealth_fig, livelihood_fig, wealth_monthly_fig, livelihood_grouped.to_dict("records")

# Run the app
if __name__ == "__main__":
Expand Down
42 changes: 39 additions & 3 deletions apps/viz/dash/inventory_dashboard/functions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import pandas as pd
import requests

# API Endpoints
LIVELIHOOD_STRATEGY_URL = "https://headev.fews.net/api/livelihoodstrategy/"
WEALTH_GROUP_URL = "https://headev.fews.net/api/wealthgroupcharacteristicvalue/"
LIVELIHOOD_ACTIVITY_URL = "https://headev.fews.net/api/livelihoodactivity/"

def fetch_data(api_url):
"""
Expand All @@ -15,20 +19,52 @@ def fetch_data(api_url):
print(f"Error fetching data: {e}")
return pd.DataFrame()


def prepare_livelihood_data(df):
"""
Prepare livelihood strategy data for visualization.
"""
df.rename(columns={"livelihood_zone_country": "country_code"}, inplace=True)
df["ls_baseline_date"] = df["livelihood_zone_baseline_label"].str.split(": ").str[1]
df["ls_baseline_month"] = pd.to_datetime(df["ls_baseline_date"], errors="coerce").dt.month_name()
df["ls_baseline_month"] = pd.to_datetime(df["ls_baseline_date"], errors="coerce").dt.month
return df


def prepare_wealth_group_data(df):
"""
Prepare wealth group data for visualization.
"""
# Rename columns for consistency
df.rename(columns={"livelihood_zone_country_code": "country_code"}, inplace=True)

# Extract baseline date from 'livelihood_zone_baseline_label'
if "livelihood_zone_baseline_label" in df.columns:
df["ls_baseline_date"] = df["livelihood_zone_baseline_label"].str.split(": ").str[1]
else:
df["ls_baseline_date"] = None # Assign None if the column is missing

# Convert baseline date to datetime and extract the month
df["ls_baseline_month"] = pd.to_datetime(df["ls_baseline_date"], errors="coerce").dt.month

# Define month mapping dictionary
month_mapping = {
1: "January", 2: "February", 3: "March", 4: "April",
5: "May", 6: "June", 7: "July", 8: "August",
9: "September", 10: "October", 11: "November", 12: "December"
}

# Extract 'created_month' from 'created_date' or fallback to 'ls_baseline_date'
if "created_date" in df.columns:
df["created_month"] = pd.to_datetime(df["created_date"], errors="coerce").dt.month.map(month_mapping)
else:
print("Warning: 'created_date' column is missing. Using 'ls_baseline_date' instead.")
df["created_month"] = pd.to_datetime(df["ls_baseline_date"], errors="coerce").dt.month.map(month_mapping)

return df



# Fetch and prepare data
livelihood_data = fetch_data(LIVELIHOOD_STRATEGY_URL)
wealth_group_data = fetch_data(WEALTH_GROUP_URL)

clean_livelihood_data = prepare_livelihood_data(livelihood_data)
clean_wealth_group_data = prepare_wealth_group_data(wealth_group_data)
Loading

0 comments on commit fffa294

Please sign in to comment.