Athlete Recovery Tracker in Python
Build an athlete recovery tracker using core Python data structures and functions.
Introduction
30 Second Summary
When athletes get injured, sports clinics need to track how each person is recovering day by day. Pain scores change, timelines shift, and staff need a quick way to see who needs attention and who is ready to return to the field.
In this project, you will build an Athlete Recovery Tracker in a Jupyter Notebook that stores athlete profiles, classifies their recovery status, and simulates how many days until they're cleared to play.
What You'll Build
Type generate_report(roster) into a single cell and watch a formatted recovery status report print for every athlete in your system, complete with status classifications and projected days to clearance.
By the end of this project, you'll have:
- A roster of athlete profiles stored as labeled dictionaries, so you can look up any athlete's injury, pain score, or sport instantly by field name.
- A classification system that prints each athlete as "At Risk", "Progressing", or "Cleared" based on their pain score.
- A one-line report generator that processes the full roster and prints a formatted summary with recovery projections for every athlete.
- Secret Mission: A recovery projection function that returns detailed day-by-day forecasts with custom parameters.
Are there any prerequisites?
This project assumes you already have Python and Jupyter Notebook installed on Windows. If you haven't set those up yet, complete that first before jumping in.
Before We Start
Before we dive in, let's lock in what you're about to build and why it matters.
Set Up Your Notebook
To build your Athlete Recovery Tracker, you need a workspace that lets you write and test code in small pieces, seeing results immediately after each one.
Jupyter Notebook is that workspace. It splits your code into individual cells you can run one at a time. This is how data-focused professionals actually write and test code.
In this step, get ready to:
- Launch Jupyter Notebook from Command Prompt.
- Create a new notebook and give it a name.
- Run your first cell to confirm everything works.
Launch Jupyter Notebook
Jupyter Notebook runs as a local server on your computer. You start it from the command line, and it opens in your browser.
- Press the Windows key to open your search bar.
- Type Command Prompt and press Enter to open it.
- Launch Jupyter by running this command:
jupyter notebook
Your browser opens automatically to the Jupyter Notebook Dashboard at http://localhost:8888. This is your home base for creating and managing notebooks.
Command not found?
If you see an error about jupyter not being recognized, Jupyter may not be on your system PATH. Close Command Prompt, reinstall with pip install notebook, then try again.
Help me fix a 'jupyter not recognized' error on Windows.
Create and rename your notebook
The Dashboard shows your files. From here you create a new notebook that will hold all your Athlete Recovery Tracker code.
- Click New in the top right corner of the Dashboard.
- Select Python 3 from the dropdown.
A new tab opens with a blank notebook called Untitled. You will see an empty cell waiting for code.
- Click the title Untitled at the top of the page (next to the Jupyter logo).
- Type athlete_recovery as the new name.
- Click Rename to confirm.
The title at the top now reads athlete_recovery. Your notebook file is saved as athlete_recovery.ipynb.
Write and run your first cell
Each cell in a Jupyter Notebook is a small container for code. When you run a cell, Python executes only that cell and prints any output directly below it.
- Click inside the first empty cell in your notebook.
- Type this code into the cell:
# Cell 1: Setup
print("Recovery Tracker ready!")
What does this code do?
The line starting with # is a comment. Python ignores it completely. It is a label for you.
print() outputs whatever is inside the parentheses as text below the cell. This confirms your notebook and Python kernel are connected and working.
Before you run this cell, what do you think will appear below it?
- Press Shift+Enter to run the cell.
You should see Recovery Tracker ready! printed directly below the cell. That output confirms Python executed your code successfully.
Don't see any output?
Make sure you pressed Shift+Enter (not just Enter alone). Pressing Enter alone just adds a new line inside the cell without running it.
Check that your parentheses and quotes match exactly. A missing quote or parenthesis will cause a SyntaxError.
Help me fix my first Jupyter cell that won't run.
✔️ Awesome, I've got everything!
Great. Make sure you have saved your notebook by pressing Ctrl+S.
ⓧ I'd like to double check the full code
Your first cell should contain exactly this:
# Cell 1: Setup
print("Recovery Tracker ready!")
When you run it with Shift+Enter, the output Recovery Tracker ready! should appear directly below the cell.
Your notebook is ready and your Python kernel is confirmed working. Next up, you will store your first athlete's data and discover how Python handles different types of information.
Store One Athlete with Variables and Data Types
Your notebook is running and ready for code. Now it's time to store your first athlete's data and see how Python handles it.
Before you can track a full roster, you need to store one athlete's profile. This reveals that Python treats text, whole numbers, decimals, and true/false values as fundamentally different types. You'll hit a moment where mixing them breaks your code until you learn to convert between them.
In this step, get ready to:
- Create variables for one athlete using all four basic Python data types.
- Hit a TypeError when mixing types, then fix it with type casting.
- See why numbered variables become unmanageable with just two athletes.
Create variables for one athlete
Every athlete in the recovery tracker has a name, an age, a pain score, and a clearance status. Each of these is a different kind of data, and Python needs to know which kind it's working with.
- Click inside a new empty cell below Cell 1 and type the following code:
# Cell 2: Variables and Data Types
athlete_name = "Marcus Johnson"
age = 28
pain_score = 7.5
is_cleared = False
print(type(athlete_name))
print(type(age))
print(type(pain_score))
print(type(is_cleared))
print("\nAthlete:", athlete_name)
print("Age:", age)
print("Pain Score:", pain_score)
print("Cleared to play:", is_cleared)
What does this code do?
Each line creates a variable that holds one piece of Marcus Johnson's profile. Python automatically detects the type based on how you write the value.
- Text in quotes becomes a string (str).
- A whole number becomes an integer (int).
- A number with a decimal becomes a float (float).
- True or False becomes a boolean (bool).
The type() function confirms what Python sees for each variable.
- Press Shift+Enter to run the cell.
- Confirm you see four type lines (<class 'str'>, <class 'int'>, <class 'float'>, <class 'bool'>) followed by Marcus Johnson's full profile.
Not seeing any output?
- Make sure you pressed Shift+Enter, not just Enter. Pressing Enter alone adds a new line inside the cell without running it.
- Check that the cell has a number in square brackets to its left (e.g. [2]). If it shows [*], the cell is still running.
- If you see a NameError, make sure Cell 1 ran successfully first. Help me debug my cell output issue.
Mix types and fix the failure
You have Marcus's name (text) and his pain score (a decimal number). What happens when you try to combine them into one sentence using +?
Before you run this, what do you think Python will do when it tries to join a string and a float with +?
- In a new cell, type this code and press Shift+Enter:
athlete_name + " has pain score " + pain_score
You'll see a TypeError: can only concatenate str (not "float") to str. Python doesn't know how to glue text and a number together with +. This is exactly the problem type casting solves.
- Replace that cell's content with the following code, which documents the failure and applies the fix:
# Cell 3: Type Casting
# This would fail: athlete_name + " has pain score " + pain_score
# Fix with str():
pain_report = athlete_name + " has a pain score of " + str(pain_score)
print(pain_report)
# Converting string to float for arithmetic
pain_input = "8"
pain_as_float = float(pain_input)
print("Pain after treatment:", pain_as_float - 1.5)
What does this code do?
- str(pain_score) converts the float 7.5 into the string "7.5" so Python can concatenate it with other text.
- float(pain_input) converts the string "8" into the number 8.0 so Python can do arithmetic on it.
This is type casting: explicitly converting a value from one type to another when Python won't do it automatically.
- Press Shift+Enter to run the cell.
- Confirm you see Marcus Johnson has a pain score of 7.5 on the first line, and Pain after treatment: 6.5 on the second.
Still seeing a TypeError?
- Make sure you replaced the entire cell content, not just added below the failing line. The failing line should now be a comment (starting with #).
- Check that str( wraps pain_score with a closing ).
- Help me fix my type casting code.
See why numbered variables don't scale
You've stored one athlete successfully. What if the clinic has a second athlete? Let's try the obvious approach and see what happens.
- In a new cell, type the following code to add a second athlete:
# Cell 4: The Limitation of Plain Variables
athlete_name_2 = "Sarah Chen"
age_2 = 24
pain_score_2 = 4.2
is_cleared_2 = False
print("Athlete 1:", athlete_name, "| Pain:", pain_score)
print("Athlete 2:", athlete_name_2, "| Pain:", pain_score_2)
# Already messy with just 2... imagine 20 athletes!
What does this code do?
It creates a second set of variables with _2 appended to each name. The code works, but look at how messy it already is with just two athletes.
Imagine needing athlete_name_20, age_20, pain_score_20, is_cleared_20. That's the problem the next step solves with proper data structures.
What do you think the output will show when you run this cell?
- Press Shift+Enter to run the cell.
- Confirm you see both athletes printed side by side with their pain scores.
You should see two lines showing Marcus Johnson with pain 7.5 and Sarah Chen with pain 4.2. The code works, but the pattern of numbering every variable is already cluttered.
Seeing a NameError for athlete_name?
- Cell 4 uses athlete_name and pain_score from Cell 2. Make sure Cell 2 ran successfully (it should show a number like [2] to its left).
- If you restarted the kernel at any point, re-run Cell 2 first, then Cell 4.
- Help me fix my NameError.
✔️ Awesome, I've got everything!
Great. Make sure all three cells (Cell 2, Cell 3, Cell 4) show output below them and have run numbers to their left.
ⓧ I'd like to double check the full code
Scroll through your notebook and confirm these cells are present in order:
- Cell 1: Setup (from the previous step).
- Cell 2: Variables and Data Types with four variables, four type() prints, and the athlete profile output.
- Cell 3: Type Casting with the failed attempt as a comment, the str() fix, and the float() conversion.
- Cell 4: The Limitation of Plain Variables with _2 variables and both athletes printed.
You've stored one athlete's data across four different types, and you already feel how numbered variables get messy. Next up, you'll discover the right container for each kind of data so your roster stays clean and accessible.
Organize Data with the Right Structures
In the previous step, you stored Marcus's data in individual variables and already felt the messiness of numbered variables like athlete_name_2 for just two athletes.
Python offers four built-in containers, each designed for a different access pattern. You'll try each one on real sports healthcare data and discover which fits which job.
In this step, get ready to:
- Rebuild an athlete profile as a dictionary with labeled fields.
- Explore tuples for fixed data and sets for unique values.
- Combine structures into a full roster of athlete dictionaries.
Rebuild an athlete profile as a dictionary
In Cell 4, you used numbered variables to store a second athlete. Imagine doing that for 20 athletes. A dictionary solves this by storing data with labeled keys you can look up by name.
- Click into a new cell in your notebook and type this code to rebuild Marcus's profile as a dictionary:
# Cell 5: Dictionary - Labeled Athlete Profile
athlete = {
"name": "Marcus Johnson",
"age": 28,
"sport": "Basketball",
"injury": "ACL Sprain",
"pain_score": 7.5,
"is_cleared": False
}
print(athlete["name"], "-", athlete["injury"])
print("Pain:", athlete["pain_score"])
- Press Shift+Enter to run the cell.
What does this code do?
Instead of separate variables like athlete_name and pain_score, the dictionary groups everything under one variable called athlete.
You access values with athlete["injury"] instead of guessing what athlete_data[3] means. The keys act like labels.
You should see Marcus Johnson - ACL Sprain and his pain score printed below the cell.
Now you need a way to track Marcus's pain scores over time. Order matters here because day 1 is different from day 7.
- In a new cell, type this code to create an ordered list of daily pain scores:
# Cell 6: List - Daily Pain Scores (order matters)
daily_scores = [7.5, 7.0, 6.5, 6.0, 5.5, 5.0, 4.5]
print("Day 1 pain:", daily_scores[0])
print("Latest pain:", daily_scores[-1])
print("Total days tracked:", len(daily_scores))
- Press Shift+Enter to run the cell.
What does this code do?
A list preserves insertion order, so daily_scores[0] is always the first day's reading and daily_scores[-1] is always the latest.
The len() function returns how many items the list contains.
You should see Day 1 pain as 7.5, latest pain as 4.5, and 7 total days tracked.
Not seeing the expected output?
Make sure you used square brackets [] for the list and commas between each value. Curly braces {} would create a set instead.
help me debug my daily_scores list in Jupyter Notebook
Use tuples for fixed data and sets for unique values
Not all data should be changeable. Body regions in a clinic system are fixed. And when you collect injury reports, you often want just the unique types without duplicates.
- In a new cell, type this code to create a tuple and a set:
# Cell 7: Tuple and Set
# Tuple: fixed data that shouldn't change
body_regions = ("knee", "ankle", "shoulder", "hip", "back", "wrist")
print("Body regions:", body_regions)
# body_regions[0] = "elbow" # Uncomment to see TypeError!
# Set: unique injury types (duplicates removed automatically)
injuries_this_week = {"ACL Sprain", "Hamstring Strain", "ACL Sprain", "Concussion", "Hamstring Strain", "Ankle Fracture"}
print("Unique injuries:", injuries_this_week)
print("Distinct injury count:", len(injuries_this_week))
- Press Shift+Enter to run the cell.
What does this code do?
The tuple uses parentheses () and locks its contents. If you uncomment the assignment line, Python raises TypeError: 'tuple' object does not support item assignment.
The set uses curly braces {} and automatically discards duplicate values. You typed 6 entries but the set keeps only 4 unique ones.
You should see 6 body regions printed and a distinct injury count of 4 (the duplicates were removed).
- Try uncommenting the line body_regions[0] = "elbow" by removing the # at the start, then run the cell again.
- Confirm you see the TypeError, then add the # back and re-run so the cell completes successfully.
Why use a tuple over a list?
When data should never change (like body regions in a medical system), a tuple communicates that intent to anyone reading your code. The error you just saw is Python enforcing that intent.
Build the full athlete roster
You have a dictionary for one athlete and a list for sequential data. Now combine both patterns into one structure: a list of dictionaries that holds your entire clinic roster.
- In a new cell, start typing the roster with the first three athletes:
# Cell 8: List of Dictionaries - The Full Roster
roster = [
{
"name": "Marcus Johnson",
"age": 28,
"sport": "Basketball",
"injury": "ACL Sprain",
"pain_score": 7.5,
"is_cleared": False
},
{
"name": "Sarah Chen",
"age": 24,
"sport": "Swimming",
"injury": "Shoulder Impingement",
"pain_score": 4.2,
"is_cleared": False
},
{
"name": "David Okafor",
"age": 31,
"sport": "Football",
"injury": "Hamstring Strain",
"pain_score": 2.1,
"is_cleared": True
},
What's happening here?
Each athlete is a dictionary (curly braces with labeled fields). The outer square brackets [] make it a list, so athletes have a position: roster[0] is Marcus, roster[1] is Sarah.
This structure gives you the best of both worlds. You can loop through athletes by position AND look up each athlete's data by field name.
- Continue typing in the same cell to add the fourth athlete and the print statements:
{
"name": "Emily Torres",
"age": 22,
"sport": "Soccer",
"injury": "Ankle Sprain",
"pain_score": 5.8,
"is_cleared": False
}
]
print("Roster size:", len(roster))
print("First athlete:", roster[0]["name"])
Before you run this cell, predict what number len(roster) will print.
- Press Shift+Enter to run the cell.
You should see Roster size: 4 and First athlete: Marcus Johnson printed below.
Seeing a SyntaxError?
The most common cause is a missing comma between dictionaries. Each closing }, needs a comma except the very last one before ].
Also check that every opening { has a matching } and every key has a colon after it.
help me fix a SyntaxError in my roster list of dictionaries
- Verify you can access a specific athlete's field by typing this in a new cell and pressing Shift+Enter:
print(roster[0]["injury"])
You should see ACL Sprain printed. This confirms you can reach any piece of data in the roster by combining list position with dictionary key.
✔️ Awesome, I've got everything!
Great. Confirm you've saved your notebook by pressing Ctrl+S.
ⓧ I'd like to double check the full code
Scroll through your notebook and confirm you have these cells in order:
- Cell 5: Dictionary - Labeled Athlete Profile (defines athlete dict, prints name and injury).
- Cell 6: List - Daily Pain Scores (defines daily_scores, prints first, last, and length).
- Cell 7: Tuple and Set (defines body_regions tuple and injuries_this_week set, prints both).
- Cell 8: List of Dictionaries - The Full Roster (defines roster with 4 athletes, prints size and first athlete name).
Each cell should run without errors and produce the expected output.
Your clinic now has a proper data structure holding four athletes, each with labeled fields you can access instantly. Next up, you'll teach Python to make decisions about this data and process the entire roster automatically.
Classify Athletes and Simulate Recovery
Your roster of athlete dictionaries is ready. Four athletes, each with labeled fields you can access by name. The data is well-organized, but it doesn't do anything on its own.
In this step, you'll teach Python to make decisions about each athlete's recovery status and repeat work across the full roster. By the end, you'll have a formatted status report and a day-by-day recovery simulation that reveals a key limitation in your current approach.
In this step, get ready to:
- Classify athletes as "At Risk", "Progressing", or "Cleared" based on pain scores.
- Loop through the full roster to generate a formatted status report.
- Simulate day-by-day recovery using a while loop.
Classify athletes by recovery status
Right now, you'd have to look at each athlete's pain score and mentally decide their status. Python can automate that decision with if/elif/else blocks. These check conditions in order and run only the first branch that matches.
- Create a new cell in your notebook and type this code to classify one athlete:
# Cell 9: If/Elif/Else - Classify One Athlete
pain = roster[0]["pain_score"]
name = roster[0]["name"]
if pain >= 7.0:
status = "At Risk"
elif pain >= 4.0:
status = "Progressing"
else:
status = "Cleared"
print(name, "->", status)
What does this code do?
- roster[0]["pain_score"] pulls the first athlete's pain score from the roster you built in Step 3.
- The if/elif/else chain checks conditions top to bottom. Only the FIRST matching branch runs. Pain of 7.5 hits pain >= 7.0 immediately, so Python never checks the other conditions.
- The order matters. If you put pain >= 4.0 first, a pain score of 7.5 would match that condition and be labeled "Progressing" incorrectly.
- Press Shift+Enter to run the cell.
You should see Marcus Johnson -> At Risk printed below the cell.
Seeing a NameError?
If you see NameError: name 'roster' is not defined, your Cell 8 (which defines the roster) hasn't been run yet. Click on Cell 8 and press Shift+Enter to run it, then re-run Cell 9.
Help me debug my if/elif/else block
One athlete classified. Now you need this logic to run for every athlete in the roster. A for loop walks through a list one item at a time, running the indented code for each.
- Create a new cell below and type this code to classify all athletes at once:
# Cell 10: For Loop - Classify All Athletes
print("--- Recovery Status Report ---")
for athlete in roster:
if athlete["pain_score"] >= 7.0:
status = "At Risk"
elif athlete["pain_score"] >= 4.0:
status = "Progressing"
else:
status = "Cleared"
print(f"{athlete['name']:20} | Pain: {athlete['pain_score']} | Status: {status}")
What does this code do?
- for athlete in roster: takes each dictionary from the roster list, one at a time, and assigns it to the variable athlete. The indented code runs once per athlete.
- The f"..." syntax is a formatted string literal. Anything inside {} gets replaced with that variable's value. The :20 pads the name to 20 characters so your output aligns in neat columns.
- Press Shift+Enter to run the cell.
You should see a formatted report with all 4 athletes, each on their own line with their pain score and status. Marcus shows "At Risk", Sarah and Emily show "Progressing", and David shows "Cleared".
Output looks garbled or unaligned?
Check that you have the f before the opening quote in your print statement. Without it, Python prints the literal curly braces instead of the values.
Also confirm you're using single quotes inside the f-string brackets (athlete['name']) since the outer string already uses double quotes.
Help me fix my for loop output
Simulate recovery day by day
Classifying athletes tells you where they are right now. But a clinic also needs to project the future. A while loop repeats a block of code as long as a condition stays true. You'll use it to simulate daily pain reduction until an athlete recovers.
- Create a new cell and type this code to simulate Marcus's recovery:
# Cell 11: While Loop - Simulate Recovery (with visible repetition)
print("--- Simulating Recovery for Marcus ---")
current_pain = 7.5
day = 1
daily_reduction = 0.5
while current_pain > 2.0:
current_pain = current_pain - daily_reduction
current_pain = round(current_pain, 1)
print(f" Day {day}: Pain = {current_pain}")
day = day + 1
print(f"Marcus reached low pain on day {day - 1}!")
What does this code do?
- while current_pain > 2.0: keeps looping as long as the pain score is above 2.0. Each pass through the loop represents one day of recovery.
- round(current_pain, 1) rounds to one decimal place. Without this, floating-point math can produce values like 6.999999 instead of 7.0.
- The loop exits when current_pain is no longer greater than 2.0. At that point, Python continues to the print statement after the loop.
- Press Shift+Enter to run the cell.
You should see 11 lines of output, one per day, with the pain dropping from 7.0 down to 2.0. The final line confirms Marcus reached low pain on day 11.
Loop running forever?
If the cell keeps running with no output (or printing endlessly), your condition might never become false. Press the stop button (square icon) in the toolbar to interrupt the kernel. Then check that you have current_pain = current_pain - daily_reduction inside the loop. Without the subtraction, the pain never decreases and the loop never ends.
Help me fix my infinite while loop
Now you need the same simulation for Sarah Chen. Her starting pain is 4.2 and her daily reduction is 0.4.
- In the same Cell 11, add this code below Marcus's simulation:
# Copy-pasting the same logic for Sarah...
print("\n--- Simulating Recovery for Sarah ---")
current_pain = 4.2
day = 1
daily_reduction = 0.4
while current_pain > 2.0:
current_pain = current_pain - daily_reduction
current_pain = round(current_pain, 1)
print(f" Day {day}: Pain = {current_pain}")
day = day + 1
print(f"Sarah reached low pain on day {day - 1}!")
# What if we need to simulate 20 athletes? Copy-pasting is not the answer.
What's different about Sarah's version?
Almost nothing. You changed three values: the starting pain (4.2 instead of 7.5), the daily reduction (0.4 instead of 0.5), and the name in the print statement. The logic is identical. That repetition is intentional. Keep it for now.
Before you run this, how many days do you think it will take Sarah to recover? Her pain starts lower than Marcus's.
- Press Shift+Enter to run the full cell.
You should see Marcus's 11-day simulation followed by Sarah's 6-day simulation. Both work correctly. But scroll up and look at the code you just wrote.
Do you see the problem?
You have two blocks of nearly identical code. The only difference is three numbers and a name. Imagine needing to simulate recovery for all 20 athletes in a full clinic roster. You'd have 20 copy-pasted blocks, each one a chance to introduce a typo.
The comment at the bottom says it plainly: copy-pasting is not the answer. The next step introduces the tool that eliminates this repetition entirely.
Only seeing Sarah's output?
Make sure both loops are in the same cell. If you accidentally created a new cell for Sarah's code, Marcus's output will be in the cell above. Move Sarah's code into Cell 11 so both simulations appear together.
Help me troubleshoot my while loop cell
✔️ Awesome, I've got everything!
Great. Confirm you've run all three new cells (9, 10, and 11) and that each shows output below it.
ⓧ I'd like to double check the full code
Scroll through your notebook and confirm these three cells are present in order, each with output below them:
- Cell 9: If/Elif/Else - Classify One Athlete — prints Marcus Johnson -> At Risk.
- Cell 10: For Loop - Classify All Athletes — prints a formatted report with all 4 athletes and their statuses.
- Cell 11: While Loop - Simulate Recovery (with visible repetition) — contains two while loops (Marcus and Sarah) and ends with the comment about copy-pasting not being the answer.
Your tracker now makes decisions and simulates the future. But you've already felt the pain of writing the same logic twice. Next up, you'll learn how to write it once and reuse it for any athlete with a single line of code.
Build Reusable Functions
Your recovery simulation works. You can classify athletes by pain score and simulate their recovery day by day. But scroll back to Cell 11 and look at that duplicated while loop.
Functions solve this. They let you write logic once, give it a name, and reuse it with different inputs. This is the difference between a script that works and code that scales.
In this step, get ready to:
- Extract the classification logic into a reusable function.
- Extract the recovery simulation into a reusable function.
- Build a report generator that processes the entire roster in one call.
Extract classification into a function
The if/elif/else block you wrote in Cell 10 classifies one athlete based on their pain score. Right now that logic is embedded inside a for loop. If you needed to classify an athlete anywhere else in your code, you would have to copy those lines again.
A function wraps that logic into a named, reusable unit. You call it with an input value and it gives you back a result.
- Click into a new cell below Cell 11 and type the following function:
# Cell 12: Function - Classify Athlete
def classify_athlete(pain_score):
"""Return recovery status based on pain score."""
if pain_score >= 7.0:
return "At Risk"
elif pain_score >= 4.0:
return "Progressing"
else:
return "Cleared"
print(classify_athlete(7.5))
print(classify_athlete(4.2))
print(classify_athlete(2.1))
What does this code do?
- def classify_athlete(pain_score): defines a function that accepts one input (the pain score) and packages the classification logic inside it.
- return sends a value back to whoever called the function. Unlike print(), which just displays text, return gives you a value you can store in a variable or pass to another function.
- The three print() calls at the bottom test the function with values that should hit each branch: 7.5 (At Risk), 4.2 (Progressing), and 2.1 (Cleared).
- Press Shift+Enter to run the cell.
- You should see three lines printed: At Risk, Progressing, and Cleared.
Seeing a NameError or unexpected output?
- Make sure you typed def at the start of the function line and that the function body is indented with four spaces.
- Check that each return statement is inside the function body (indented), not at the same level as def.
- help me debug my classify_athlete function
Extract recovery simulation into a function
Remember the copy-pasted while loop in Cell 11? You wrote the same recovery simulation twice, changing only the starting pain and reduction rate. A function eliminates that duplication entirely.
- Create a new cell below Cell 12 and type this function:
# Cell 13: Function - Simulate Recovery
def simulate_recovery(athlete_name, starting_pain, daily_reduction):
"""Simulate daily recovery and return the number of days to reach pain below 2.0."""
current_pain = starting_pain
day = 0
while current_pain > 2.0:
current_pain = current_pain - daily_reduction
current_pain = round(current_pain, 1)
day = day + 1
return day
# One line per athlete instead of a whole block!
marcus_days = simulate_recovery("Marcus", 7.5, 0.5)
sarah_days = simulate_recovery("Sarah", 4.2, 0.4)
print(f"Marcus: {marcus_days} days to recovery")
print(f"Sarah: {sarah_days} days to recovery")
What does this code do?
- The function takes three arguments: the athlete's name, their starting pain level, and how much pain decreases each day.
- Inside, the same while loop logic from Cell 11 runs until pain drops below 2.0. But instead of printing every day, it just counts the days and returns that number.
- The two calls at the bottom replace the entire duplicated code block from Cell 11. One line per athlete instead of 10+ lines each.
- Press Shift+Enter to run the cell.
- You should see Marcus: 11 days to recovery and Sarah: 6 days to recovery.
Why does Marcus take 11 days?
Starting at 7.5 and subtracting 0.5 each day, the loop runs until pain is no longer above 2.0. That takes 11 iterations to reach exactly 2.0, at which point the condition current_pain > 2.0 is False and the loop exits.
Getting different numbers or an infinite loop?
- If the cell runs forever, check that your while condition is current_pain > 2.0 (greater than), not current_pain > 0. A target of 0 with 0.5 reduction takes much longer.
- If the numbers are slightly off, make sure you included round(current_pain, 1). Without rounding, floating-point math can produce values like 6.999999 instead of 7.0.
- help me debug my simulate_recovery function
Build the full report generator
You now have two functions that each do one job well. The final step is combining them into a single function that processes the entire roster and prints a formatted report.
- Create a new cell below Cell 13 and type this function definition:
# Cell 14: Function - Generate Full Report
def generate_report(roster):
"""Generate a full recovery report for all athletes."""
print("=" * 55)
print(" ATHLETE RECOVERY REPORT")
print("=" * 55)
at_risk_count = 0
progressing_count = 0
cleared_count = 0
for athlete in roster:
status = classify_athlete(athlete["pain_score"])
days = simulate_recovery(
athlete["name"],
athlete["pain_score"],
0.5
)
print(f"{athlete['name']:20} | {status:12} | ~{days} days to clear")
if status == "At Risk":
at_risk_count = at_risk_count + 1
elif status == "Progressing":
progressing_count = progressing_count + 1
else:
cleared_count = cleared_count + 1
print("=" * 55)
print(f"Summary: {at_risk_count} at risk, {progressing_count} progressing, {cleared_count} cleared")
What does this function do?
- generate_report(roster) accepts the full roster list as its input.
- It loops through every athlete, calling your two existing functions to classify them and estimate their recovery days.
- Three counters (at_risk_count, progressing_count, cleared_count) track how many athletes are in each category for the summary line.
- This is the power of functions calling other functions. Each one handles one responsibility, and the report generator orchestrates them.
Before you run this, think about what you expect to see. You have 4 athletes in the roster with pain scores of 7.5, 4.2, 2.1, and 5.8. How many do you think will be classified as "At Risk"?
- Below the function definition in the same cell, add this call:
generate_report(roster)
- Press Shift+Enter to run the cell.
You should see a formatted table with all four athletes, each showing their name, status, and projected days to recovery. At the bottom, a summary line counts 1 at risk, 2 progressing, and 1 cleared.
Why is this better than what you had before?
Look at what you just accomplished with one line. generate_report(roster) classified every athlete, simulated every recovery, formatted the output, and tallied the counts. If you add 50 more athletes to the roster, that same single line handles all of them.
Seeing a NameError for classify_athlete or simulate_recovery?
- Make sure you ran Cell 12 and Cell 13 before running Cell 14. Functions must be defined before they can be called.
- If you restarted your notebook kernel at any point, re-run all cells from the top (Cell 1 through Cell 14) in order.
- help me fix my generate_report function
✔️ Awesome, I've got everything!
Great. Make sure you have saved your notebook.
ⓧ I'd like to double check the full code
Scroll through your notebook and confirm these three cells are present in order:
- Cell 12: defines classify_athlete(pain_score) and tests it with three print statements.
- Cell 13: defines simulate_recovery(athlete_name, starting_pain, daily_reduction) and prints days for Marcus and Sarah.
- Cell 14: defines generate_report(roster) and calls it, producing a formatted table with a summary line.
Secret mission
Build a Recovery Projection Function
Your current functions work, but they only return a single number. Real clinical tools provide detailed day-by-day projections with customizable parameters. Can you build a function that returns a rich data structure combining everything you've learned?
Clean Up Your Resources
Clean Up Your Resources
Decide whether to keep your resources running, pause them to come back later, or delete them entirely. This project runs entirely locally, so there are no ongoing costs.
Resources you used:
- Jupyter Notebook server (running in Command Prompt).
- athlete_recovery.ipynb notebook file.
Keep everything running
No action needed. Choose this if you're still actively building or want to keep experimenting with your notebook.
- Your Jupyter server stays running in Command Prompt.
- Your athlete_recovery.ipynb file remains in the directory where you started the server.
- You can return to it anytime by opening http://localhost:8888 in your browser.
Pause - I'll come back to this later
Shut down the Jupyter server to free up your Command Prompt window. Your notebook file stays on disk so you can pick up where you left off.
- Switch to the Command Prompt window where Jupyter is running.
- Press Ctrl+C to stop the server. Confirm with y if prompted.
- To resume later, open Command Prompt and run jupyter notebook again from the same directory.
Delete - I don't want to use this again
Remove the notebook file and stop the Jupyter server entirely.
- Switch to the Command Prompt window where Jupyter is running.
- Press Ctrl+C to stop the server. Confirm with y if prompted.
- Delete the notebook file by running this command in Command Prompt:
del athlete_recovery.ipynb
- If the file is not in your current directory, navigate to where you originally launched Jupyter and run the command there.
Nice Work!
Nice Work!
You just built a complete Athlete Recovery Tracker in a single Jupyter Notebook. From raw variables to a one-line report generator, you now have working code that processes real sports healthcare data using every core Python concept.
You've learned how to:
- Store athlete data using dictionaries for labeled profiles, lists for ordered sequences, tuples for immutable data, and sets for unique values.
- Classify athletes using if/elif/else logic and process entire rosters with for loops and while loops to simulate recovery timelines.
- Extract repetitive code into reusable functions with arguments and return values, turning a full recovery report into a single generate_report(roster) call.
- Secret Mission: Built a projection function that returns rich data structures with default parameter values and a detailed daily log.
Ready to quiz yourself?