> ## Documentation Index
> Fetch the complete documentation index at: https://fastapi2day.codewithsiva.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Building a Complete Streamlit Student Dashboard App

> Build a complete Student Performance Analytics Dashboard step-by-step, combining file upload, filters, session state, tabs, and charts.

You've learned how to display content, collect user input, and work with data in Streamlit. Now it's time to put everything together. In this capstone project, you'll build a **Student Performance Analytics Dashboard** — a complete, production-style Streamlit application that accepts a CSV upload, applies sidebar filters, shows summary metrics, organizes output into tabs, renders Matplotlib charts, and lets users download a filtered report. Follow each step in order, running the app after adding each section to see it grow incrementally.

## Project Setup

<Steps>
  ### Create the Project Folder

  ```text theme={null}
  student_dashboard/
  ├── app.py
  ├── students.csv
  └── requirements.txt
  ```

  ### Install Dependencies

  ```bash theme={null}
  pip install streamlit pandas numpy matplotlib
  ```

  Add the same list to `requirements.txt` for reproducible deployments:

  ```text theme={null}
  streamlit
  pandas
  numpy
  matplotlib
  ```

  ### Verify the Setup

  ```bash theme={null}
  streamlit run app.py
  ```

  You should see the Streamlit welcome screen at `http://localhost:8501`.
</Steps>

## Sample Dataset

Save this as `students.csv` to use while developing:

```csv theme={null}
RollNo,Name,Department,Semester,Subject,Marks
101,Sai Kiran,CSE,4,Python,88
102,Sravani Reddy,CSE,4,Python,75
103,Venkatesh Kumar,ECE,4,Python,91
104,Harika Devi,CSE,4,AI,82
105,Naveen Kumar,ECE,4,AI,95
106,Keerthana,MECH,3,Python,69
107,Sandeep Reddy,CSE,3,AI,90
108,Lakshmi Priya,ECE,2,Maths,74
109,Rohith Varma,CSE,4,Maths,86
110,Bhavya Sri,MECH,2,Physics,72
```

## Building the App Step by Step

### Step 1 — App Configuration and Title

```python theme={null}
import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import time

st.set_page_config(
    page_title="Student Dashboard",
    layout="wide",
    initial_sidebar_state="expanded"
)

st.title("📊 Student Performance Analytics Dashboard")
st.write("Upload a student CSV file to explore performance metrics and visualizations.")
```

`st.set_page_config` must be the **first Streamlit call** in your script. Setting `layout="wide"` makes the app span the full browser width.

### Step 2 — File Upload

```python theme={null}
uploaded_file = st.file_uploader("Upload Student CSV", type="csv")

if uploaded_file:
    df = pd.read_csv(uploaded_file)
    st.success("File uploaded successfully.")
else:
    st.info("Please upload a CSV file to get started.")
    st.stop()   # Halt execution — nothing below renders without data
```

`st.stop()` prevents the rest of the script from running when no file has been uploaded, keeping the app clean for first-time visitors.

### Step 3 — Top-Level Metrics

```python theme={null}
col1, col2, col3 = st.columns(3)
col1.metric("Total Students", len(df))
col2.metric("Subjects", df["Subject"].nunique())
col3.metric("Average Marks", round(df["Marks"].mean(), 1))
```

### Step 4 — Sidebar Filters

```python theme={null}
st.sidebar.title("🔍 Filters")

with st.sidebar.form("filter_form"):
    dept = st.selectbox(
        "Department",
        ["All"] + sorted(df["Department"].unique().tolist())
    )
    subject = st.selectbox(
        "Subject",
        ["All"] + sorted(df["Subject"].unique().tolist())
    )
    search = st.text_input("Search student name")
    submit = st.form_submit_button("Apply Filters")
```

Wrapping filters in `st.form` means the app only reruns when the user clicks **Apply Filters**, not on every keystroke. This is much more efficient for expensive operations.

### Step 5 — Apply Filters to the Data

```python theme={null}
filtered_df = df.copy()

if dept != "All":
    filtered_df = filtered_df[filtered_df["Department"] == dept]

if subject != "All":
    filtered_df = filtered_df[filtered_df["Subject"] == subject]

if search:
    filtered_df = filtered_df[
        filtered_df["Name"].str.contains(search, case=False, na=False)
    ]
```

### Step 6 — Session State for a Visit Counter

```python theme={null}
if "visits" not in st.session_state:
    st.session_state.visits = 0

st.session_state.visits += 1
st.sidebar.markdown(f"**Dashboard opened:** {st.session_state.visits} time(s)")
```

`st.session_state` persists values across reruns within the same browser session. Without it, the counter would reset to 0 on every rerun.

### Step 7 — Tabbed Layout

```python theme={null}
tab1, tab2, tab3 = st.tabs(["📋 Dataset", "📈 Reports", "📊 Charts"])
```

### Step 8 — Dataset Tab

```python theme={null}
with tab1:
    st.subheader("Filtered Student Records")
    st.dataframe(filtered_df, use_container_width=True)
    st.caption(f"Showing {len(filtered_df)} of {len(df)} records.")
```

### Step 9 — Reports Tab

```python theme={null}
with tab2:
    st.subheader("Summary Statistics")

    col1, col2, col3, col4 = st.columns(4)
    col1.metric("Students",  len(filtered_df))
    col2.metric("Highest",   int(filtered_df["Marks"].max()) if len(filtered_df) else "–")
    col3.metric("Lowest",    int(filtered_df["Marks"].min()) if len(filtered_df) else "–")
    col4.metric("Average",   round(filtered_df["Marks"].mean(), 1) if len(filtered_df) else "–")
```

### Step 10 — Charts Tab

```python theme={null}
with tab3:
    if filtered_df.empty:
        st.warning("No data matches the current filters.")
    else:
        col_a, col_b = st.columns(2)

        # Average marks by department
        with col_a:
            fig1, ax1 = plt.subplots(figsize=(5, 4))
            filtered_df.groupby("Department")["Marks"].mean().plot(kind="bar", ax=ax1, color="#3B82F6")
            ax1.set_ylabel("Average Marks")
            ax1.set_title("Avg. Marks by Department")
            plt.xticks(rotation=30)
            plt.tight_layout()
            st.pyplot(fig1)

        # Marks distribution histogram
        with col_b:
            fig2, ax2 = plt.subplots(figsize=(5, 4))
            filtered_df["Marks"].plot(kind="hist", bins=10, ax=ax2, color="#10B981", edgecolor="white")
            ax2.set_xlabel("Marks")
            ax2.set_title("Grade Distribution")
            plt.tight_layout()
            st.pyplot(fig2)
```

### Step 11 — Progress Indicator and Download

```python theme={null}
with st.spinner("Preparing download..."):
    time.sleep(0.5)

csv_export = filtered_df.to_csv(index=False)

st.download_button(
    label="⬇️ Download Filtered Report (CSV)",
    data=csv_export,
    file_name="students_report.csv",
    mime="text/csv"
)
```

## Complete App at a Glance

After completing all steps, your `app.py` produces this layout:

```text theme={null}
┌─────────────────────────────────────────────────────────────────┐
│  📊 Student Performance Analytics Dashboard                     │
├──────────────┬──────────────────────────────────────────────────┤
│  SIDEBAR     │  [Total Students] [Subjects] [Average Marks]     │
│              │                                                  │
│  Filters     │  Tabs: Dataset | Reports | Charts               │
│  ─────────── │                                                  │
│  Department  │  Dataset: filterable table                       │
│  Subject     │  Reports: KPI metrics                           │
│  Search      │  Charts: bar chart + histogram                  │
│  Apply       │                                                  │
│              │  ⬇️ Download Filtered Report                     │
│  Visits: N   │                                                  │
└──────────────┴──────────────────────────────────────────────────┘
```

## Concepts Demonstrated

| Concept              | Where Used                           |
| -------------------- | ------------------------------------ |
| `st.set_page_config` | App-wide configuration               |
| `st.file_uploader`   | Loading student CSV data             |
| `st.stop()`          | Guard clause before data is uploaded |
| `st.columns`         | Side-by-side metric cards            |
| `st.sidebar.form`    | Grouped filter controls              |
| `st.session_state`   | Persistent visit counter             |
| `st.tabs`            | Organized multi-section layout       |
| `st.dataframe`       | Interactive filtered table           |
| `st.metric`          | Summary KPI display                  |
| `st.pyplot`          | Matplotlib bar chart and histogram   |
| `st.spinner`         | Progress feedback                    |
| `st.download_button` | Export filtered CSV                  |
| Pandas filtering     | Applying widget values to DataFrames |
| Matplotlib           | Bar chart and histogram rendering    |

<Tip>
  Once your single-page app is working well, consider splitting it into a multipage app. Create a `pages/` folder and add separate `.py` files for each page — Streamlit automatically adds them to the sidebar navigation with no additional routing code.
</Tip>
