3.14 Libraries HW
3.14 Libraries: Homework Notebook
Lesson: CSP 3.14 Libraries (School Basketball Season Stats App) Contents: Popcorn Hack 1 (read the API, then use it), Popcorn Hack 2 (write a Flask route), Homework Hack (Season Stats Report), submission notes.
Every code cell keeps # CODE_RUNNER: as its first line and only uses standard-library modules or flask.
Popcorn Hack 1: Read the API, Then Use It
What I changed: the jersey range is now 1-55, and I take the square root of a 225 sq ft court instead of 144.
Predictions (before running):
- Jersey number: a whole number from 1 to 55, both included.
math.sqrt(225)= 15.0 feet.- Average of
[24, 18, 31, 27, 22]= 122 / 5 = 24.4, andmath.ceil(24.4)= 25. random.choicereturns one of 24, 18, 31, 27, or 22 (it changes every run).
Code Runner Challenge
Popcorn Hack 1 - jersey range changed to 1-55, new court size
View IPYNB Source
# CODE_RUNNER: Popcorn Hack 1 - jersey range changed to 1-55, new court size
import math
import random
# Read the API before calling a procedure
print("random.randint:", random.randint.__doc__)
print("math.ceil:", math.ceil.__doc__)
print("math has", len(dir(math)), "names; random has", len(dir(random)), "names")
points_per_game = [24, 18, 31, 27, 22]
# random.choice picks one item out of a list
print("Random game to review:", random.choice(points_per_game), "points")
# CHANGED: jersey numbers now run from 1 to 55 (both included)
jersey = random.randint(1, 55)
print("Random jersey number:", jersey)
# CHANGED: a 225 square foot court instead of 144
side = math.sqrt(225)
print("Square court side:", side, "feet")
# math.ceil rounds up to the next whole number
average = sum(points_per_game) / len(points_per_game)
rounded_up = math.ceil(average)
print("Points per game:", average, "-> rounded up:", rounded_up)
Check: the printed Square court side is 15.0, the average is 24.4 and rounds up to 25, and the jersey number is always between 1 and 55, matching my predictions. Reading help/__doc__ first told me randint(a, b) includes both endpoints, so I didnβt have to know how it works inside. Thatβs abstraction.
Popcorn Hack 2: Write a Flask Route
My extra route: /coach (plus a /record route). I also put my own players in the roster.
Prediction: /score, /roster, /coach, and /record should each return the dictionary I pass to jsonify, as JSON. Routes are defined above app.test_client().
Code Runner Challenge
Popcorn Hack 2 - own roster plus /coach and /record routes
View IPYNB Source
# CODE_RUNNER: Popcorn Hack 2 - own roster plus /coach and /record routes
#!pip install flask
from flask import Flask, jsonify
app = Flask(__name__)
# A route is a procedure with a web address attached to it
@app.route("/score")
def score():
return jsonify({"home": 58, "away": 52, "quarter": 3})
@app.route("/roster")
def roster():
return jsonify({"players": ["Alex", "Jordan", "Priya", "Marcus"]})
# MY EXTRA ROUTES (copying the same pattern)
@app.route("/coach")
def coach():
return jsonify({"coach": "Coach Rivera", "assistant": "Coach Lee"})
@app.route("/record")
def record():
return jsonify({"wins": 6, "losses": 2})
# test_client visits the addresses without needing a browser
client = app.test_client()
print("/score says:", client.get("/score").get_json())
print("/roster says:", client.get("/roster").get_json())
print("/coach says:", client.get("/coach").get_json())
print("/record says:", client.get("/record").get_json())
Three Ways to Import (code example)
Not required for submission, but I fixed the line that breaks when statistics is nicknamed s/stats.
Code Runner Challenge
Three ways to import - nickname stats
View IPYNB Source
# CODE_RUNNER: Three ways to import - nickname stats
import math # whole library: math. prefix required
from random import randint # one procedure: no prefix
import statistics as stats # nickname: stats stands in for statistics
print("Hoop circumference:", round(2 * math.pi * 0.75, 2), "feet")
print("Coin toss, 1 means home ball:", randint(1, 2))
print("Points per game:", stats.mean([24, 18, 31])) # fixed: stats.mean, not statistics.mean
Homework Hack: Season Stats Report
My own version: it uses four standard-library modules (json, statistics, datetime, math), a documented summarize_season procedure, datetime for the days until my next game, and a requirements.txt string. Replace the sample games and next-game date below with your own sport and schedule before you submit.
Code Runner Challenge
Homework Hack - Season Stats Report
View IPYNB Source
# CODE_RUNNER: Homework Hack - Season Stats Report
import json
import math
import statistics
from datetime import date
# Own games for the season (swap in your sport and your real results)
games = [
{"date": "2026-09-05", "opponent": "Jefferson", "points": 19, "minutes": 24},
{"date": "2026-09-12", "opponent": "Kennedy", "points": 27, "minutes": 29},
{"date": "2026-09-19", "opponent": "Jefferson", "points": 22, "minutes": 26},
{"date": "2026-09-26", "opponent": "Adams", "points": 33, "minutes": 31},
{"date": "2026-09-29", "opponent": "Kennedy", "points": 15, "minutes": 20},
]
def summarize_season(season):
"""
Summarize a list of games.
Parameters:
season: list of dictionaries, each with "date" (str), "opponent" (str),
"points" (int), and "minutes" (int)
Returns:
dictionary with
total_points (int): sum of all points scored,
average_points (float): mean points per game, rounded to 1 decimal,
best_game (int): the highest single-game point total,
points_by_opponent (dict): total points scored against each opponent,
points_per_minute (float): total points divided by total minutes, 2 decimals
"""
points = [game["points"] for game in season]
minutes = [game["minutes"] for game in season]
points_by_opponent = {}
for game in season:
opponent = game["opponent"]
points_by_opponent[opponent] = points_by_opponent.get(opponent, 0) + game["points"]
return {
"total_points": sum(points),
"average_points": round(statistics.mean(points), 1),
"best_game": max(points),
"points_by_opponent": points_by_opponent,
"points_per_minute": round(sum(points) / sum(minutes), 2),
}
report = summarize_season(games)
# math.ceil rounds the average up: handy for a "scoring goal" on the scoreboard
report["next_scoring_goal"] = math.ceil(report["average_points"])
# Subtracting two dates returns a timedelta, and .days is the whole number of days
next_game = date(2026, 10, 17)
report["days_until_next_game"] = (next_game - date.today()).days
print(json.dumps(report, indent=2))
# requirements.txt lists only EXTERNAL packages (pandas for the season table, Flask for the
# scoreboard page). json, datetime, statistics, and math come with Python's standard library,
# so pip never needs to install them and they are never listed.
requirements_txt = """pandas
Flask
"""
print()
print("requirements.txt:")
print(requirements_txt)
Libraries used and why
json:json.dumps(report, indent=2)turns the Python dictionary into JSON text that a scoreboard page could read.statistics:statistics.meanreplaces my own add-and-divide loop with a tested procedure.datetime:date(...) - date.today()gives atimedelta;.daysis the whole number of days until the next game.math:math.ceilrounds the average up for a scoring goal.- API documentation: the docstring on
summarize_seasonlists each parameter and every key in the returned dictionary, so someone can use it without reading the code. requirements.txt:pandasandFlaskare external (installed withpip install -r requirements.txt); the four modules above are standard library, so they are never listed.
Submission Notes (paste into the description box)
Lesson: CSP 3.14 Libraries
Popcorn 1: what I changed = jersey range 1-55 and a 225 sq ft court (sqrt = 15.0)
Popcorn 2: my extra route = /coach (and /record)
Homework: libraries used = json, statistics, datetime (plus math)
Homework: days until my next game = see the printed days_until_next_game value (next game 2026-10-17)
The days_until_next_game value depends on the day you run the cell, so copy the number from your own run.