Build a GPA Analyzer Dashboard
Build an interactive GPA dashboard with charts and performance analysis.
Introduction
30 Second Summary
You know that feeling at the end of a semester when you're staring at a pile of grades, trying to figure out your GPA and which subjects are dragging you down? What if you could type in your courses and instantly see where you stand.
In this project, you will build a GPA Analyzer Dashboard using Python. It calculates your GPA, visualizes your grades across multiple chart types, and automatically identifies the subject areas where you need the most improvement.
What You'll Build
A local web dashboard powered by Streamlit and Plotly that turns your course grades into interactive visualizations and actionable performance insights.
By the end of this project, you'll have:
- A working interactive GPA calculator that runs in your browser with no HTML, CSS, or JavaScript required.
- Three visualization types (bar chart, pie chart, and radar chart) showing your academic performance from different angles.
- An automated performance analysis system with an adjustable threshold that flags your weakest subject categories.
- Secret Mission: Add semester tracking with a GPA trend line chart showing your progress over time.
Are there any prerequisites?
This project assumes basic Python knowledge including variables, functions, dictionaries, and lists. No web development experience is needed.
Before We Start
Before building your GPA dashboard, you need to set up a Python project with the right libraries installed.
Three libraries power the entire app. Streamlit creates the web interface, Plotly renders interactive charts, and Pandas handles data manipulation.
In this step, get ready to:
- Install Streamlit, Plotly, and Pandas.
- Create the app.py file with initial imports.
- Confirm the Streamlit dev server launches in your browser.
Set up your project and install packages
- Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
- Type PyCharm and press Enter to open it.
- On the Welcome screen, click New Project.
- In the Location field, change the project name at the end of the path to gpa-analyzer.
- Uncheck Create a main.py welcome script if it is selected.
- Keep the default Project venv interpreter option.
- Click Create.
What is a virtual environment?
PyCharm automatically creates a virtual environment for your new project. This is an isolated Python installation that keeps your project's packages separate from other projects on your machine.
You will see a venv folder appear in your project. All packages you install will live inside it.
PyCharm opens your new project with the Project tool window on the left showing the gpa-analyzer folder.
- Open the Terminal tab at the bottom of the PyCharm window.
- Install the three required packages by running this command:
pip install streamlit==1.58.0 plotly==6.8.0 pandas
What does this command do?
- pip install tells Python's package manager to download and install libraries.
- streamlit==1.58.0 pins Streamlit to version 1.58.0 so your project matches this tutorial exactly.
- plotly==6.8.0 pins Plotly to version 6.8.0 for the same reason.
- pandas installs the latest compatible version of Pandas for data handling.
The install may take a minute. You will see a success message when all packages are installed.
- Verify Streamlit installed correctly by running this command:
streamlit hello
A demo app should open automatically in your browser. You will see a welcome page with interactive examples in the sidebar.
- Close the browser tab when you are done exploring.
- Return to the terminal in PyCharm and press Ctrl+C to stop the demo server.
Don't see the demo app?
- Make sure you are running the command inside PyCharm's Terminal tab, not an external terminal. The virtual environment must be active (you will see (venv) at the start of your terminal prompt).
- If you see a "command not found" error, the install may have failed. Re-run the pip install command and check for error messages.
- If the browser did not open automatically, look in the terminal for a URL (usually http://localhost:8501) and open it manually.
help me troubleshoot why streamlit hello is not working in PyCharm
Create your app file and verify Streamlit runs
- In the Project tool window on the left, right-click the gpa-analyzer project root folder.
- Select New, then Python File.
- Type app as the file name and press Enter.
PyCharm creates app.py and opens it in the editor.
- Add the following imports to app.py:
import streamlit as st
import plotly.express as px
import pandas as pd
What do these imports do?
- streamlit as st gives you access to all of Streamlit's UI components (forms, buttons, charts, layout).
- plotly.express as px provides a concise API for creating interactive charts like bar charts, pie charts, and radar charts.
- pandas as pd is a data manipulation library that makes it easy to work with tabular data.
- Save app.py by pressing Cmd+S (macOS) or Ctrl+S (Windows).
- In the Terminal tab, run your app by entering this command:
streamlit run app.py
A blank page should open in your browser at http://localhost:8501. This confirms Streamlit is reading your file and serving it as a web app.
Seeing an error instead of a blank page?
- If you see an ImportError, one of the packages did not install correctly. Stop the server with Ctrl+C and re-run the pip install command from the previous substep.
- Make sure your file is named exactly app.py and is saved in the project root, not inside the venv folder.
help me fix an error when running streamlit run app.py in PyCharm
Leave the Streamlit server running in the terminal. You will keep it running throughout the project so changes appear instantly in your browser.
✔️ Awesome, I've got everything!
Great. Make sure app.py is saved and your Streamlit server is still running in the terminal.
ⓧ I'd like to double check the full code
Here is the complete app.py file so far:
import streamlit as st
import plotly.express as px
import pandas as pd
Your development environment is ready. Next up, you will build the course input form that captures grades, credit hours, and subject categories.
Set Up the App and Course Input Form
Your Python environment is ready and Streamlit is running. Now it's time to build the interface your users will interact with.
A GPA calculator needs to collect structured data from the user: course names, letter grades, credit hours, and subject categories. Streamlit's form system handles all of this without any HTML or JavaScript.
In this step, get ready to:
- Define the grade-to-GPA mapping and subject categories your app uses.
- Build an interactive form for adding courses with grades and credits.
- Add a button to clear all course data.
Set up the page configuration and grade mapping
Every Streamlit app starts with a page configuration that sets the browser tab title and layout mode. You also need to define the data structures your form will reference.
- Switch back to app.py in PyCharm.
- Add the page configuration, grade mapping, categories, and session state initialization below your existing imports:
# Page configuration
st.set_page_config(page_title="GPA Analyzer", layout="wide")
st.title("GPA Analyzer Dashboard")
# Grade point mapping
grade_points = {
"A+": 4.0, "A": 4.0, "A-": 3.7,
"B+": 3.3, "B": 3.0, "B-": 2.7,
"C+": 2.3, "C": 2.0, "C-": 1.7,
"D+": 1.3, "D": 1.0, "D-": 0.7,
"F": 0.0
}
# Subject categories
categories = ["Math", "Science", "English", "History", "Electives"]
# Initialize session state
if "courses" not in st.session_state:
st.session_state.courses = []
What does this code do?
- st.set_page_config sets the browser tab title to "GPA Analyzer" and uses the wide layout so your dashboard fills the screen.
- grade_points is a dictionary that maps every letter grade to its numeric GPA value. Your form will reference this to convert grades into numbers.
- categories defines the subject areas a course can belong to.
- st.session_state persists data across page reruns. Without it, your course list would reset every time a user interacts with the app.
What is session state?
Streamlit reruns your entire Python script from top to bottom every time a user clicks a button or changes an input. Session state is a special dictionary that survives these reruns.
The if "courses" not in st.session_state check makes sure you only create an empty list the very first time. On subsequent reruns, the existing list (with any added courses) stays intact.
- Save app.py.
- Switch to your browser. You should see "GPA Analyzer Dashboard" as the main heading on the page.
Still seeing a blank page?
- Make sure you saved the file. Streamlit auto-reloads on save, but unsaved changes won't appear.
- Check that st.set_page_config is the very first Streamlit command after your imports. If any other st. call comes before it, Streamlit will throw an error in the terminal.
- Look at the terminal in PyCharm for any red error text pointing to a syntax issue in the dictionary.
Help me figure out why my Streamlit page title isn't showing up.
Create the course input form
Streamlit forms batch multiple inputs together so the app only reruns once when the user clicks submit. This prevents the page from refreshing after every single keystroke.
- Add the course input form below the session state initialization in app.py:
# Course input form
st.header("Add a Course")
with st.form("course_form", clear_on_submit=True):
col1, col2, col3, col4 = st.columns(4)
with col1:
course_name = st.text_input("Course Name")
with col2:
grade = st.selectbox("Grade", list(grade_points.keys()))
with col3:
credits = st.number_input("Credit Hours", min_value=1, max_value=6, value=3)
with col4:
category = st.selectbox("Category", categories)
submitted = st.form_submit_button("Add Course")
if submitted and course_name:
st.session_state.courses.append({
"Course": course_name,
"Grade": grade,
"Grade Points": grade_points[grade],
"Credits": credits,
"Category": category
})
What does this form do?
- st.form("course_form", clear_on_submit=True) creates a form container. The clear_on_submit=True parameter resets all fields after the user submits, making it easy to add another course immediately.
- st.columns(4) splits the form into four side-by-side columns so the inputs sit neatly in a row.
- Each column holds one input widget: a text field for the course name, a dropdown for the grade, a number picker for credits, and a dropdown for the category.
- When the user clicks **Add Course**, the code appends a dictionary with all the course details (including the numeric grade points looked up from grade_points) to st.session_state.courses.
- Save app.py and switch to your browser.
- You should see an "Add a Course" header with a form containing four input fields (Course Name, Grade, Credit Hours, Category) and an **Add Course** button.
Form not showing up?
- Check indentation carefully. Everything inside the with st.form block must be indented one level. The with col1: blocks need another level of indentation inside.
- If you see an error about grade_points not being defined, make sure the dictionary from the previous chunk appears above the form code.
- Confirm you placed this code after the session state initialization, not inside or before it.
Help me fix my Streamlit form that isn't rendering correctly.
Add a clear button
Users need a way to start fresh if they want to clear all their entered courses. A conditional button that only appears when courses exist keeps the interface clean.
- Add the clear button code at the bottom of app.py, below the form:
# Clear button
if st.session_state.courses:
if st.button("Clear All Courses"):
st.session_state.courses = []
st.rerun()
What does this code do?
- The outer if check means the button only appears when the learner has added at least one course. This avoids showing a useless button on an empty page.
- st.rerun() forces Streamlit to re-execute the script immediately after clearing the list, so the page updates to reflect the empty state.
- Save app.py.
- In your browser, type a course name (e.g. Calculus I) in the Course Name field, leave the other fields at their defaults, and click **Add Course**.
- After submitting, a **Clear All Courses** button should appear below the form. This confirms your course was stored in session state.
Clear button not appearing after adding a course?
- Make sure you typed something in the Course Name field before clicking **Add Course**. The form only saves when the name is not empty (if submitted and course_name).
- Verify the clear button code is outside the with st.form block. If it's indented inside the form, it won't render correctly.
- Check the terminal for errors. A typo in st.session_state.courses (like a misspelling) would cause a crash.
Why isn't my Clear All Courses button showing up after I submit a course?
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on.
ⓧ I'd like to double check the full code
Here is the complete app.py so far. Make sure your file matches this exactly:
import streamlit as st
import plotly.express as px
import pandas as pd
# Page configuration
st.set_page_config(page_title="GPA Analyzer", layout="wide")
st.title("GPA Analyzer Dashboard")
# Grade point mapping
grade_points = {
"A+": 4.0, "A": 4.0, "A-": 3.7,
"B+": 3.3, "B": 3.0, "B-": 2.7,
"C+": 2.3, "C": 2.0, "C-": 1.7,
"D+": 1.3, "D": 1.0, "D-": 0.7,
"F": 0.0
}
# Subject categories
categories = ["Math", "Science", "English", "History", "Electives"]
# Initialize session state
if "courses" not in st.session_state:
st.session_state.courses = []
# Course input form
st.header("Add a Course")
with st.form("course_form", clear_on_submit=True):
col1, col2, col3, col4 = st.columns(4)
with col1:
course_name = st.text_input("Course Name")
with col2:
grade = st.selectbox("Grade", list(grade_points.keys()))
with col3:
credits = st.number_input("Credit Hours", min_value=1, max_value=6, value=3)
with col4:
category = st.selectbox("Category", categories)
submitted = st.form_submit_button("Add Course")
if submitted and course_name:
st.session_state.courses.append({
"Course": course_name,
"Grade": grade,
"Grade Points": grade_points[grade],
"Credits": credits,
"Category": category
})
# Clear button
if st.session_state.courses:
if st.button("Clear All Courses"):
st.session_state.courses = []
st.rerun()
Your form is collecting course data behind the scenes. Next, you'll calculate the GPA from this data and display it with summary metrics.
Calculate and Display GPA
Your course input form is working. Every course the learner adds is stored in session state, ready to be processed.
Now it's time to turn that raw data into something useful. In this step, you'll calculate the overall GPA using weighted quality points, then display the results as clean metric cards and an interactive data table.
In this step, get ready to:
- Calculate GPA using quality points (grade points multiplied by credit hours).
- Display GPA, total credits, and course count as metric cards.
- Show course data in an interactive table.
Create the DataFrame and calculate GPA
GPA isn't a simple average of grades. Each course's grade is weighted by its credit hours. A 4-credit A contributes more to your GPA than a 1-credit A.
The formula multiplies each course's grade points by its credits to get "quality points," then divides total quality points by total credits.
- In app.py, scroll to the bottom of the file (below the Clear button code).
- Add the GPA calculation logic by pasting this code at the end of the file:
# Main dashboard
if st.session_state.courses:
df = pd.DataFrame(st.session_state.courses)
# Calculate GPA
df["Quality Points"] = df["Grade Points"] * df["Credits"]
total_quality_points = df["Quality Points"].sum()
total_credits = df["Credits"].sum()
gpa = total_quality_points / total_credits
What does this code do?
The if st.session_state.courses: check ensures the dashboard only renders when the learner has added at least one course.
The DataFrame converts the list of course dictionaries into a structured table. The new Quality Points column multiplies grade points by credits for each row, and the final GPA is total quality points divided by total credits.
- Save app.py.
- You won't see a visible change yet. The next chunk adds the display elements that show the calculated GPA on screen.
Seeing a syntax error in the terminal?
Make sure the if st.session_state.courses: line is at the same indentation level as the Clear button's if statement (no indentation). Everything inside the block should be indented one level.
Check that you pasted after the closing line of the Clear button block, not inside it.
Help me fix a syntax error in my Streamlit GPA calculator
Display GPA metrics and the course table
With the GPA calculated, you need a way to present it clearly. Streamlit's metric cards are perfect for headline numbers, and the built-in data table gives learners a way to review their course list.
- In app.py, add the following code directly below the gpa = total_quality_points / total_credits line (still inside the if block):
# Display metrics
st.header("Your GPA Summary")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Overall GPA", f"{gpa:.2f}")
with col2:
st.metric("Total Credits", int(total_credits))
with col3:
st.metric("Courses Added", len(df))
# Course table
st.dataframe(df[["Course", "Grade", "Credits", "Category"]], use_container_width=True)
else:
st.info("Add courses above to see your GPA analysis.")
What does this code do?
The three st.metric calls create large, bold stat cards. The f"{gpa:.2f}" formatting rounds the GPA to two decimal places.
The st.dataframe displays an interactive table that learners can sort and scroll. It only shows the four most relevant columns.
The else block pairs with the earlier if st.session_state.courses: check. When no courses exist yet, it shows a helpful info message instead of an empty page.
- Save app.py.
- Switch to your browser at http://localhost:8501. The app should auto-refresh.
- Add a test course (e.g., "Calculus", grade A, 3 credits, Math category). You should see three metric cards appear showing your Overall GPA, Total Credits, and Courses Added, plus a table listing your course.
Don't see the metrics?
Make sure you entered a course name before clicking Add Course. The form only saves when the name field is not empty.
Check that the metrics code is indented inside the if st.session_state.courses: block. If the indentation is off, the metrics will either never show or always show.
Help me debug why my GPA metrics are not displaying in Streamlit
Why weighted GPA instead of a simple average?
A simple average treats every course equally. A 1-credit seminar and a 4-credit lecture would have the same impact on your GPA.
Weighted GPA multiplies each grade by its credit hours first, which means harder (higher-credit) courses have a proportionally larger effect on your overall score. This is how universities actually calculate GPA.
Now verify the math is correct by testing with known values.
- Click Clear All Courses to start fresh.
- Add a course with grade A (4.0 points) and 3 credits. That's 12 quality points.
- Add a second course with grade B (3.0 points) and 3 credits. That's 9 quality points.
- Your Overall GPA should display 3.50 (21 total quality points divided by 6 total credits).
✔️ Awesome, I've got everything!
Great. Double check you've saved your file and that the GPA reads 3.50 with the two test courses above.
ⓧ I'd like to double check the full code
Here is the complete app.py file up to this point. Compare it with your code to spot any differences.
import streamlit as st
import plotly.express as px
import pandas as pd
# Page configuration
st.set_page_config(page_title="GPA Analyzer", layout="wide")
st.title("GPA Analyzer Dashboard")
# Grade point mapping
grade_points = {
"A+": 4.0, "A": 4.0, "A-": 3.7,
"B+": 3.3, "B": 3.0, "B-": 2.7,
"C+": 2.3, "C": 2.0, "C-": 1.7,
"D+": 1.3, "D": 1.0, "D-": 0.7,
"F": 0.0
}
# Subject categories
categories = ["Math", "Science", "English", "History", "Electives"]
# Initialize session state
if "courses" not in st.session_state:
st.session_state.courses = []
# Course input form
st.header("Add a Course")
with st.form("course_form", clear_on_submit=True):
col1, col2, col3, col4 = st.columns(4)
with col1:
course_name = st.text_input("Course Name")
with col2:
grade = st.selectbox("Grade", list(grade_points.keys()))
with col3:
credits = st.number_input("Credit Hours", min_value=1, max_value=6, value=3)
with col4:
category = st.selectbox("Category", categories)
submitted = st.form_submit_button("Add Course")
if submitted and course_name:
st.session_state.courses.append({
"Course": course_name,
"Grade": grade,
"Grade Points": grade_points[grade],
"Credits": credits,
"Category": category
})
# Clear button
if st.session_state.courses:
if st.button("Clear All Courses"):
st.session_state.courses = []
st.rerun()
# Main dashboard
if st.session_state.courses:
df = pd.DataFrame(st.session_state.courses)
# Calculate GPA
df["Quality Points"] = df["Grade Points"] * df["Credits"]
total_quality_points = df["Quality Points"].sum()
total_credits = df["Credits"].sum()
gpa = total_quality_points / total_credits
# Display metrics
st.header("Your GPA Summary")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Overall GPA", f"{gpa:.2f}")
with col2:
st.metric("Total Credits", int(total_credits))
with col3:
st.metric("Courses Added", len(df))
# Course table
st.dataframe(df[["Course", "Grade", "Credits", "Category"]], use_container_width=True)
else:
st.info("Add courses above to see your GPA analysis.")
Your dashboard now crunches the numbers and presents them clearly. Next up, you'll visualize this data with interactive charts that reveal patterns a table of numbers can't show.
Create Visualizations
Your dashboard now calculates GPA and displays it in metric cards. Numbers alone don't tell the full story though.
In this step, you'll add three interactive chart types using Plotly. Each visualization shows your academic performance from a different angle, making patterns easy to spot at a glance.
In this step, get ready to:
- Build a bar chart showing grade points per course.
- Create a pie chart displaying your grade distribution.
- Add a radar chart mapping performance across subject categories.
Set up the tabbed layout and bar chart
Streamlit's st.tabs lets you organize multiple visualizations without cluttering the page. The bar chart is a natural first view because it compares grade points across individual courses.
- In app.py, find the course table line that says:
st.dataframe(df[["Course", "Grade", "Credits", "Category"]], use_container_width=True)
- Add the following code directly below that line (still inside the if st.session_state.courses: block):
# Visualizations
st.header("Performance Visualizations")
tab1, tab2, tab3 = st.tabs(["Bar Chart", "Pie Chart", "Radar Chart"])
with tab1:
fig_bar = px.bar(
df, x="Course", y="Grade Points",
color="Grade Points",
color_continuous_scale="RdYlGn",
title="Grade Points by Course"
)
fig_bar.update_layout(yaxis_range=[0, 4.0])
st.plotly_chart(fig_bar)
What does this code do?
- st.tabs(["Bar Chart", "Pie Chart", "Radar Chart"]) creates three clickable tabs. Each tab gets its own content area.
- px.bar() builds a bar chart with course names on the x-axis and grade points on the y-axis.
- color_continuous_scale="RdYlGn" applies a red-yellow-green gradient so low grades appear red and high grades appear green.
- yaxis_range=[0, 4.0] locks the y-axis to the full GPA scale, making comparisons consistent.
- Save app.py.
- Switch back to your browser at http://localhost:8501. Add 2-3 courses using the form, then scroll down. You should see a "Performance Visualizations" header with three tabs, and the Bar Chart tab displaying colored bars for each course.
Don't see the charts?
- Make sure you added courses first. The visualization code only runs when courses exist in session state.
- Check that the new code is indented to match the if st.session_state.courses: block. It should sit at the same indentation level as st.dataframe above it.
- Confirm the new code is placed before the else: block at the bottom of the file.
Help me debug why my bar chart isn't showing up in my Streamlit app.
Add the pie chart
A pie chart shows how your grades are distributed as proportions. It answers the question: what percentage of your courses earned an A versus a B?
- In app.py, find the line st.plotly_chart(fig_bar) inside the with tab1: block. Add the following code below it, replacing the empty with tab2: block:
with tab2:
grade_counts = df["Grade"].value_counts().reset_index()
grade_counts.columns = ["Grade", "Count"]
fig_pie = px.pie(
grade_counts, values="Count", names="Grade",
title="Grade Distribution"
)
st.plotly_chart(fig_pie)
What does this code do?
- value_counts().reset_index() counts how many times each letter grade appears and turns the result into a clean DataFrame.
- The column rename (["Grade", "Count"]) ensures consistent column names across different Pandas versions.
- px.pie() uses the count as slice sizes and grade letters as labels. Hovering over a slice shows the exact percentage.
- Save app.py.
- Switch to your browser and click the Pie Chart tab. You should see a circular chart with colored slices for each grade letter you've entered.
Pie chart showing but looks wrong?
- If you only see one giant slice, you likely have all courses with the same grade. Add courses with different grades (mix of A, B, C) to see multiple slices.
- Check that with tab2: is at the same indentation level as with tab1: above it.
Help me fix my pie chart in Streamlit.
Add the radar chart
A radar chart plots multiple categories on radial axes, forming a polygon shape. It reveals your strengths and weaknesses across subject categories at a glance.
- In app.py, add the following code for the with tab3: block, directly after the with tab2: block you just wrote:
with tab3:
category_avg = df.groupby("Category")["Grade Points"].mean().reset_index()
fig_radar = px.line_polar(
category_avg, r="Grade Points", theta="Category",
line_close=True,
title="Performance by Category"
)
fig_radar.update_traces(fill="toself")
fig_radar.update_layout(
polar=dict(radialaxis=dict(visible=True, range=[0, 4.0]))
)
st.plotly_chart(fig_radar)
What does this code do?
- groupby("Category")["Grade Points"].mean() calculates the average grade points for each subject category.
- px.line_polar() draws the radar chart with each category as an axis radiating from the center.
- line_close=True connects the last point back to the first, forming a closed polygon shape.
- fill="toself" fills the polygon with color, making the area easier to read visually.
- The radial axis range of [0, 4.0] ensures the scale always shows the full GPA range.
- Save app.py.
- In your browser, add at least 5 courses across different categories (Math, Science, English, etc.).
- Click the Radar Chart tab. You should see a filled polygon with each subject category as a spoke. Hover over the chart points to see exact GPA values.
Radar chart not forming a polygon?
- You need courses in at least 3 different categories for the polygon shape to be visible. Add courses across Math, Science, and English at minimum.
- Make sure line_close=True is inside the px.line_polar() call, not after it.
Help me debug my radar chart in Streamlit.
Why Plotly instead of Matplotlib?
Plotly produces interactive charts out of the box. You can hover to see exact values, zoom in, and pan around. Matplotlib produces static images that don't respond to user interaction.
For a dashboard where you explore your own data, interactivity makes the tool genuinely useful rather than just informative.
✔️ Awesome, I've got everything!
Great. Double check you've saved your file before moving on.
ⓧ I'd like to double check the full code
Here's the complete app.py at this point:
import streamlit as st
import plotly.express as px
import pandas as pd
# Page configuration
st.set_page_config(page_title="GPA Analyzer", layout="wide")
st.title("GPA Analyzer Dashboard")
# Grade point mapping
grade_points = {
"A+": 4.0, "A": 4.0, "A-": 3.7,
"B+": 3.3, "B": 3.0, "B-": 2.7,
"C+": 2.3, "C": 2.0, "C-": 1.7,
"D+": 1.3, "D": 1.0, "D-": 0.7,
"F": 0.0
}
# Subject categories
categories = ["Math", "Science", "English", "History", "Electives"]
# Initialize session state
if "courses" not in st.session_state:
st.session_state.courses = []
# Course input form
st.header("Add a Course")
with st.form("course_form", clear_on_submit=True):
col1, col2, col3, col4 = st.columns(4)
with col1:
course_name = st.text_input("Course Name")
with col2:
grade = st.selectbox("Grade", list(grade_points.keys()))
with col3:
credits = st.number_input("Credit Hours", min_value=1, max_value=6, value=3)
with col4:
category = st.selectbox("Category", categories)
submitted = st.form_submit_button("Add Course")
if submitted and course_name:
st.session_state.courses.append({
"Course": course_name,
"Grade": grade,
"Grade Points": grade_points[grade],
"Credits": credits,
"Category": category
})
# Clear button
if st.session_state.courses:
if st.button("Clear All Courses"):
st.session_state.courses = []
st.rerun()
# Main dashboard
if st.session_state.courses:
df = pd.DataFrame(st.session_state.courses)
# Calculate GPA
df["Quality Points"] = df["Grade Points"] * df["Credits"]
total_quality_points = df["Quality Points"].sum()
total_credits = df["Credits"].sum()
gpa = total_quality_points / total_credits
# Display metrics
st.header("Your GPA Summary")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Overall GPA", f"{gpa:.2f}")
with col2:
st.metric("Total Credits", int(total_credits))
with col3:
st.metric("Courses Added", len(df))
# Course table
st.dataframe(df[["Course", "Grade", "Credits", "Category"]], use_container_width=True)
# Visualizations
st.header("Performance Visualizations")
tab1, tab2, tab3 = st.tabs(["Bar Chart", "Pie Chart", "Radar Chart"])
with tab1:
fig_bar = px.bar(
df, x="Course", y="Grade Points",
color="Grade Points",
color_continuous_scale="RdYlGn",
title="Grade Points by Course"
)
fig_bar.update_layout(yaxis_range=[0, 4.0])
st.plotly_chart(fig_bar)
with tab2:
grade_counts = df["Grade"].value_counts().reset_index()
grade_counts.columns = ["Grade", "Count"]
fig_pie = px.pie(
grade_counts, values="Count", names="Grade",
title="Grade Distribution"
)
st.plotly_chart(fig_pie)
with tab3:
category_avg = df.groupby("Category")["Grade Points"].mean().reset_index()
fig_radar = px.line_polar(
category_avg, r="Grade Points", theta="Category",
line_close=True,
title="Performance by Category"
)
fig_radar.update_traces(fill="toself")
fig_radar.update_layout(
polar=dict(radialaxis=dict(visible=True, range=[0, 4.0]))
)
st.plotly_chart(fig_radar)
else:
st.info("Add courses above to see your GPA analysis.")
Your dashboard now turns raw course data into three distinct visual perspectives. Next up, you'll add a performance analysis section that automatically flags the subject categories where you need to improve.
Add Performance Analysis
Your dashboard already visualizes performance through charts. Charts are great for spotting patterns, but they still require the learner to interpret them manually.
In this step, you'll make the dashboard actionable by adding automated analysis. A threshold slider lets the user define their minimum acceptable GPA, and the app flags any subject category that falls below it.
In this step, get ready to:
- Add a GPA threshold slider to the dashboard.
- Filter categories below the threshold and display warnings for weak areas.
Add the GPA threshold slider
A threshold is a cutoff point that separates "acceptable" from "needs improvement." You'll let the user choose their own threshold so the analysis is personalized.
- In app.py, find the closing line of the radar chart tab:
st.plotly_chart(fig_radar)
- Add the following code below that line (making sure the indentation is back to the main if block level, aligned with st.header("Performance Visualizations") above):
# Areas to improve
st.header("Areas to Improve")
threshold = st.slider("Set GPA threshold", 0.0, 4.0, 3.0, 0.1)
What does this code do?
- st.header("Areas to Improve") adds a new section heading below the charts.
- st.slider creates a draggable slider widget. The arguments set the label, minimum (0.0), maximum (4.0), default (3.0), and step size (0.1).
- The slider's current value is stored in the threshold variable. Every time the user moves the slider, Streamlit reruns the script with the updated value.
- Save app.py and refresh your browser.
- Scroll below the charts. You should see an "Areas to Improve" heading and a slider set to 3.0.
Filter and display weak categories
The slider alone does nothing visible yet. You need logic that groups your courses by category, calculates each category's average GPA, and compares it to the threshold.
- Add the following code directly below the threshold = st.slider(...) line you just added:
category_avg_all = df.groupby("Category")["Grade Points"].mean()
weak_areas = category_avg_all[category_avg_all < threshold]
if not weak_areas.empty:
for cat, avg_gpa in weak_areas.items():
st.warning(f"{cat}: Average GPA is {avg_gpa:.2f} (below {threshold:.1f} threshold)")
else:
st.success("All categories are above your threshold! Keep up the great work!")
What does this code do?
- df.groupby("Category")["Grade Points"].mean() groups all courses by their category, then calculates the average grade points for each group.
- category_avg_all[category_avg_all < threshold] filters the result to only include categories whose average falls below the user's chosen threshold.
- The for loop iterates over each weak category and displays a yellow warning banner with st.warning.
- If no categories are below the threshold, st.success displays a green congratulations banner instead.
- Save app.py and refresh your browser.
- Set the threshold slider to 3.5.
- Add courses with mixed grades across different categories (for example, an A in Math and a C in History).
- You should see yellow warning banners for any category with an average GPA below 3.5, or a green success message if all categories are above it.
- Drag the slider up and down. The warnings update in real time as the threshold changes.
Not seeing any warnings?
- Make sure your threshold is set higher than at least one category's average. If all your courses have A grades, set the slider to 4.0 to force a warning.
- Check that the new code is indented at the same level as st.header("Your GPA Summary") (4 spaces). Python uses indentation to determine which code belongs inside the if block.
- Confirm you have at least one course added. The "Areas to Improve" section only appears inside the if st.session_state.courses: block.
Why aren't my GPA warnings showing up when I move the slider?
✔️ Awesome, I've got everything!
Great. Double check you've saved your file.
ⓧ I'd like to double check the full code
Here is the complete app.py file. Compare your code against this to make sure everything matches.
import streamlit as st
import plotly.express as px
import pandas as pd
# Page configuration
st.set_page_config(page_title="GPA Analyzer", layout="wide")
st.title("GPA Analyzer Dashboard")
# Grade point mapping
grade_points = {
"A+": 4.0, "A": 4.0, "A-": 3.7,
"B+": 3.3, "B": 3.0, "B-": 2.7,
"C+": 2.3, "C": 2.0, "C-": 1.7,
"D+": 1.3, "D": 1.0, "D-": 0.7,
"F": 0.0
}
# Subject categories
categories = ["Math", "Science", "English", "History", "Electives"]
# Initialize session state
if "courses" not in st.session_state:
st.session_state.courses = []
# Course input form
st.header("Add a Course")
with st.form("course_form", clear_on_submit=True):
col1, col2, col3, col4 = st.columns(4)
with col1:
course_name = st.text_input("Course Name")
with col2:
grade = st.selectbox("Grade", list(grade_points.keys()))
with col3:
credits = st.number_input("Credit Hours", min_value=1, max_value=6, value=3)
with col4:
category = st.selectbox("Category", categories)
submitted = st.form_submit_button("Add Course")
if submitted and course_name:
st.session_state.courses.append({
"Course": course_name,
"Grade": grade,
"Grade Points": grade_points[grade],
"Credits": credits,
"Category": category
})
# Clear button
if st.session_state.courses:
if st.button("Clear All Courses"):
st.session_state.courses = []
st.rerun()
# Main dashboard
if st.session_state.courses:
df = pd.DataFrame(st.session_state.courses)
# Calculate GPA
df["Quality Points"] = df["Grade Points"] * df["Credits"]
total_quality_points = df["Quality Points"].sum()
total_credits = df["Credits"].sum()
gpa = total_quality_points / total_credits
# Display metrics
st.header("Your GPA Summary")
col1, col2, col3 = st.columns(3)
with col1:
st.metric("Overall GPA", f"{gpa:.2f}")
with col2:
st.metric("Total Credits", int(total_credits))
with col3:
st.metric("Courses Added", len(df))
# Course table
st.dataframe(df[["Course", "Grade", "Credits", "Category"]], use_container_width=True)
# Visualizations
st.header("Performance Visualizations")
tab1, tab2, tab3 = st.tabs(["Bar Chart", "Pie Chart", "Radar Chart"])
with tab1:
fig_bar = px.bar(
df, x="Course", y="Grade Points",
color="Grade Points",
color_continuous_scale="RdYlGn",
title="Grade Points by Course"
)
fig_bar.update_layout(yaxis_range=[0, 4.0])
st.plotly_chart(fig_bar)
with tab2:
grade_counts = df["Grade"].value_counts().reset_index()
grade_counts.columns = ["Grade", "Count"]
fig_pie = px.pie(
grade_counts, values="Count", names="Grade",
title="Grade Distribution"
)
st.plotly_chart(fig_pie)
with tab3:
category_avg = df.groupby("Category")["Grade Points"].mean().reset_index()
fig_radar = px.line_polar(
category_avg, r="Grade Points", theta="Category",
line_close=True,
title="Performance by Category"
)
fig_radar.update_traces(fill="toself")
fig_radar.update_layout(
polar=dict(radialaxis=dict(visible=True, range=[0, 4.0]))
)
st.plotly_chart(fig_radar)
# Areas to improve
st.header("Areas to Improve")
threshold = st.slider("Set GPA threshold", 0.0, 4.0, 3.0, 0.1)
category_avg_all = df.groupby("Category")["Grade Points"].mean()
weak_areas = category_avg_all[category_avg_all < threshold]
if not weak_areas.empty:
for cat, avg_gpa in weak_areas.items():
st.warning(f"{cat}: Average GPA is {avg_gpa:.2f} (below {threshold:.1f} threshold)")
else:
st.success("All categories are above your threshold! Keep up the great work!")
else:
st.info("Add courses above to see your GPA analysis.")
Secret mission
Add Semester Tracking
Your GPA dashboard shows how you're performing right now. But what about tracking how your performance changes over time? In this secret mission, you'll add semester tracking with a sidebar filter and a GPA trend line chart that reveals your academic trajectory across multiple semesters.
Clean Up Your Resources
Clean Up Your Resources
This project runs entirely on your local machine, so there are no ongoing costs or cloud resources to worry about. Decide whether to keep your project files, or delete them entirely.
Resources you used:
- The gpa-analyzer project folder on your Desktop.
- Python packages installed via pip: streamlit, plotly, and pandas.
Keep everything running
No action needed. Choose this if you want to keep using your GPA dashboard or plan to extend it further.
- You can run your dashboard anytime by opening a terminal in your gpa-analyzer folder and running streamlit run app.py.
- The installed packages (streamlit, plotly, pandas) remain available for other Python projects too.
Pause - I'll come back to this later
Stop the running Streamlit server to free up your terminal. Your files stay on disk for next time.
- In the terminal where Streamlit is running, press Ctrl+C to stop the dev server.
- Your gpa-analyzer folder and all installed packages remain on your machine. Run streamlit run app.py from the folder when you want to pick up again.
Delete - I don't want to use this again
Remove all project files and optionally uninstall the packages you installed.
- In the terminal where Streamlit is running, press Ctrl+C to stop the dev server.
- Delete the project folder by running this command (macOS/Linux):
- On Windows, run this instead:
cd ~/Desktop
rm -rf gpa-analyzer
cd ~\Desktop
Remove-Item -Recurse -Force gpa-analyzer
- Optionally, uninstall the Python packages by running:
pip uninstall streamlit plotly pandas -y
Nice Work!
Nice Work!
Nice work! You just built a fully interactive GPA Analyzer Dashboard using nothing but Python.
You've learned how to:
- Built a complete interactive web dashboard with Streamlit using only Python, with no HTML, CSS, or JavaScript required.
- Created three distinct data visualizations with Plotly: a bar chart for per-course performance, a pie chart for grade distribution, and a radar chart for category-level analysis.
- Built an automated performance analysis system that calculates weighted GPA, persists data with session state, and flags weak subject areas using an adjustable threshold.
- Secret Mission: Added semester tracking with sidebar filtering and a GPA trend line chart showing progress over time.
Ready to quiz yourself?