Week 9 of CS50x is where Python moves from the command line into the browser. The two problems this week are Birthdays and Finance, and between them they cover Flask routing, Jinja2 templating, SQLite database integration, session management, form handling, and the kind of full-stack thinking that ties together everything from the previous eight weeks. Birthdays is the more straightforward of the two: a birthday tracking web app where you can add a person’s name and birthday, view everyone in the database, edit existing entries, and delete ones you no longer need. Full CRUD in a Flask app, backed by SQLite, rendered through Jinja2 templates. The routes are clean — GET / fetches all birthdays and renders the index, POST / handles the form submission to insert a new birthday, /edit/ handles both the pre-filled edit form and the update submission, and /delete/ removes the record. Input validation rejects empty names and invalid dates before anything touches the database. The custom CSS gives the app a clean, card-based layout with a simple colour scheme that makes it feel like a finished product rather than a CS50 exercise.
Finance is the big one. C$50 Finance is a full stock trading simulation — a web application where users can register an account, log in, look up real stock prices via the IEX API, buy and sell shares, view their current portfolio with live values, check a full transaction history, deposit additional funds, and change their password. The routes cover register, login, logout, index, quote, buy, sell, history, change_password, deposit, and withdraw. Every route is protected by a @login_required decorator — unauthenticated users are redirected to login immediately. The database schema uses a users table for account data and a purchases table for every transaction, with a foreign key constraint linking purchases to users and an index on user_id for query performance. The portfolio view on the index route aggregates holdings using SUM(shares) GROUP BY symbol with a HAVING SUM(shares) > 0 filter to exclude fully sold positions — negative share values are used to record sales, which makes the aggregation work cleanly without a separate sales table. Usernames are lowercased on both register and login for case-insensitive authentication. Error codes follow check50 expectations precisely — 400 for bad input, not 403. The init_db() function auto-creates the database schema on startup if the database file does not exist, which means the app is portable and self-initialising.
What Week 9 makes clear is how much of web development is just connecting pieces you already understand — SQL queries, Python logic, HTML forms — through a framework that handles the HTTP layer. Flask makes that connection explicit rather than hiding it, which is exactly the right way to learn it. Building Finance end-to-end — authentication, session management, live API calls, portfolio aggregation, transaction history — is a serious project, and finishing it with all routes working, no leaks, and check50 passing is genuinely satisfying. The full code for this week is on GitHub — browse the Birthdays folder and the Finance folder directly, or explore the entire CS50x repository to follow the course week by week.