Luke's Project Guide
Project: Study Buddy Tracker Category: Web Development (Flask) Last updated: April 18
Note: This guide reflects the latest state of your project repo. It may not match the most up-to-date version if you've worked since.
Where You Are
You're ahead. Your Flask app already has:
- A dashboard that groups assignments (overdue / due soon / this week / later)
- An assignments list
- A new-assignment form with validation in
helpers.py - A real SQLite layer with schema init
- A mark-complete route (
POST /assignments/<id>/complete)
What's left: a button on the dashboard that calls the mark-complete route, edit and delete routes, a show/hide completed toggle, and a route audit.
Project Structure
Your project already has a clean split — let's name it:
- Business logic — you handwrite this.
helpers.py(validation, date parsing, grouping logic) and the SQL indb.py/schema.sql. This is what makes your tracker useful: how assignments are validated and how they're grouped by urgency. - Library / view code — agent-assisted is fine. Flask routes in
app.py, HTML templates, Bootstrap classes. These are the same for any CRUD Flask app.
Current layout:
final-project-Pattlu3/
├── app.py ← Flask routes — agent-assisted OK
├── db.py ← DB connection — library code
├── helpers.py ← business logic — handwrite (yours to own)
├── schema.sql ← SQL — yours (product decision)
├── pyproject.toml
├── templates/ ← HTML — agent-assisted OK
└── instance/ ← generated DB (gitignored)
Why the split? From Lecture 1: The MVP — on demo day, the question is "how does your tracker decide what's 'due soon'?" That's in helpers.py (group_assignments). The Flask routing that plumbs data from DB to page is not the interesting part.
helpers.py should not import flask. It's pure logic — you can test it without spinning up a server.
Phase 1: Route Audit
No code — research phase. Walk your app like a user would. Find the gaps.
Objective
Before adding anything new, walk every URL in your app. Know what works, what's half-built, and what's missing.
Instructions
/(dashboard)/assignments(list)/assignments/new(GET — form)/assignments/new(POST — submit)/assignments/<id>/complete(POST — triggered how?)
Sample Notes
GET / → ✅ dashboard renders with groups
GET /assignments → ✅ list shows uncompleted
GET /assignments/new → ✅ form renders, empty
POST /assignments/new → ✅ saves, redirects to list
POST /assignments/<id>/complete → ⚠️ route exists, no button triggers it
GET /assignments/<id>/edit → ❌ doesn't exist
POST /assignments/<id>/delete → ❌ doesn't exist
The audit sets your phase-by-phase work order for the rest of the week.
Optional — get help from your agent:
Here are my current Flask routes. Help me spot any missing pieces to hit my MVP (add / list / mark complete / edit / delete). Just list what's missing, no code yet.
Phase 3: Edit Route
Mixed phase. The route itself is Flask code (agent-assisted OK). The validation call reuses YOUR
validate_assignment_formfromhelpers.py— that's your business logic doing the real work.
Objective
Users can add but not edit. Add a flow: GET shows form pre-filled, POST updates the row.
Instructions
GET /assignments/<int:assignment_id>/edit— fetch row, render form pre-filledPOST /assignments/<int:assignment_id>/edit— validate,UPDATErow, redirect
Hints
GET handler:
@app.get("/assignments/<int:assignment_id>/edit")
def assignments_edit_form(assignment_id: int):
db = get_db()
row = db.execute(
"SELECT id, title, due_date FROM assignments WHERE id = ?",
(assignment_id,),
).fetchone()
if row is None:
return "Not found", 404
return render_template(
"assignment_form.html",
title="Edit Assignment",
form=dict(row),
errors={},
assignment_id=assignment_id,
)
POST handler:
@app.post("/assignments/<int:assignment_id>/edit")
def assignments_update(assignment_id: int):
form = dict(request.form)
cleaned, errors = validate_assignment_form(form) # ← reuses YOUR logic
if errors:
return render_template("assignment_form.html",
title="Edit Assignment",
form=form, errors=errors,
assignment_id=assignment_id), 400
db = get_db()
db.execute(
"UPDATE assignments SET title = ?, due_date = ? WHERE id = ?",
(cleaned["title"], cleaned["due_date"], assignment_id),
)
db.commit()
return redirect(url_for("assignments_list"))
Make the form action conditional in the template:
<form method="post"
action="{% if assignment_id %}/assignments/{{ assignment_id }}/edit{% else %}/assignments/new{% endif %}">
Optional — get help from your agent:
Read my assignment_form.html and app.py. Help me adapt the form to work for both "new" and "edit" based on whether assignment_id is passed in. Show me the diff only.
Phase 4: Delete Route
Agent-assisted is fine here. Single SQL
DELETE+ one button. Near-identical to the mark-complete pattern.
Objective
One POST route, no GET (prevents accidental deletion via a link).
Instructions
Hints
The route:
@app.post("/assignments/<int:assignment_id>/delete")
def assignments_delete(assignment_id: int):
db = get_db()
db.execute("DELETE FROM assignments WHERE id = ?", (assignment_id,))
db.commit()
return redirect(url_for("assignments_list"))
Delete button with confirmation:
<form action="/assignments/{{ a.id }}/delete" method="post" style="display: inline"
onsubmit="return confirm('Delete this assignment?')">
<button type="submit">🗑</button>
</form>
Optional — get help from your agent:
Skip — mirrors the mark-complete pattern you already have.
Phase 5: Show/Hide Completed Toggle
Mixed phase. The SQL conditional is business logic (you're deciding what the list contains). The URL-param plumbing is library code.
Objective
Your list currently filters WHERE completed = 0. Let the user flip to showing completed.
Instructions
Hints
Route with the toggle:
@app.get("/assignments")
def assignments_list():
show_completed = request.args.get("show_completed") == "1"
db = get_db()
if show_completed:
sql = "SELECT id, title, due_date, completed FROM assignments ORDER BY due_date ASC"
else:
sql = "SELECT id, title, due_date, completed FROM assignments WHERE completed = 0 ORDER BY due_date ASC"
rows = db.execute(sql).fetchall()
return render_template("assignments_list.html",
title="Assignments",
assignments=[dict(r) for r in rows],
show_completed=show_completed)
Toggle link in the template:
{% if show_completed %}
<a href="/assignments">Hide completed</a>
{% else %}
<a href="/assignments?show_completed=1">Show completed</a>
{% endif %}
Optional — get help from your agent:
My assignments_list page filters out completed items. Walk me through adding a URL-param-based toggle. I've sketched the route — check my logic before I commit.