Student/Parent Accountability App

The premise

My daughter is in 8th grade. Her school runs on Google Classroom for daily work and a student information system called Sycamore for grades and attendance. As a parent I had a login to Sycamore, a vague sense that Classroom existed, and no idea what was actually due this week.

What I wanted was boring and specific: every assignment with a due date on my calendar with alerts, one dashboard I could open on my phone, a way to throw in the stuff that arrives as a paper flyer or a WhatsApp screenshot, and a morning email telling me what’s late, what’s due today, and what’s coming.

That’s it. No AI tutor, no gamification. Just visibility.

The stack

  • Python for everything server-side. A SQLite table is the whole database.
  • Sycamore’s parent API for homework, missing work, grades, attendance, and the school calendar. This turned out to be the star of the show.
  • Google Calendar API to write events with popup reminders at three days, one day, and eight hours before.
  • Gmail API to send the daily digest from my own account, and to read any guardian emails the school sends.
  • Claude (Opus 5) to turn pasted text, PDFs, and photos of handouts into structured assignments with dates.
  • Flask for a small dashboard: overdue, today, this week, later, grades needing attention, attendance, school events, and an upload box.
  • Caddy as the HTTPS front door, with a Let’s Encrypt certificate on a subdomain of this site.
  • systemd timers on my VPS: sync hourly, digest at 7am.
  • Tailscale for the private admin side, and for one lesson I’ll get to below.

Total code is about 600 lines across six files. Most of the evening went into finding out which doors were locked.

Challenge 1: the school locked every door

My first plan was the obvious one: use the Google Classroom API. Register an app, have my daughter sign in once, poll her coursework. Ten minutes in, her sign-in returned “Your institution’s admin needs to review this app.” The school blocks all third-party apps on student accounts.

Fine, plan B: parents can get a daily “guardian summary” email from Classroom. Except the school has to enroll you as a guardian, and they hadn’t.

Plan C: every Classroom course has a Google Calendar. She could share those with me. Except the school restricts external sharing to free/busy only, and the course calendars are owned by Classroom, not by her, so she couldn’t share them anyway. The public iCal feed returned a 404 for anyone outside the school domain.

Three sanctioned routes, three dead ends, all by administrative policy rather than technical limitation. By this point I was ready to run a script inside her Google account to push the data out, which is a legitimate workaround but felt like going around the school rather than through it.

Solution 1: read the training you skipped

The school had emailed a two-hour parent training recording weeks earlier. I fed the transcript to Claude and asked what tools were in play. Buried in the list was Sycamore, described as the “source of truth” for grades and attendance.

Sycamore has a documented REST API, and parents can generate their own access token from their own account. No school approval, no admin review, no sharing from my daughter. Five minutes after creating a token I had her twelve classes, six current homework items, and eight assignments her teachers had marked missing. Eight. I’d known about zero of them.

The lesson isn’t “read the docs,” it’s “read the school’s docs.” The parent-facing system had an official API the whole time. I’d spent the evening trying to pry open the student-facing one.

Challenge 2: the homework feed is only as good as the teachers

Sycamore’s homework endpoint only shows assignments teachers bother to enter there. Some do, some post only in Classroom. So the homework list was partial.

The fix was to also pull the grades endpoint. Every assignment eventually gets graded, and Sycamore records “Missing” and “Absent” as grade values. That gave a second, more complete signal for missing work, plus real scores. Now anything marked missing, absent, not yet graded, or scoring under 70 percent gets flagged on the dashboard and in the digest.

Attendance came along for the ride: four absences in two classes that I also hadn’t been tracking.

Challenge 3: making it reachable from a phone on home Wi-Fi

The dashboard lived on my VPS behind Tailscale, which is great for me and useless for my wife’s phone. Tailscale Funnel promises a public HTTPS URL with one command, so I used it. It worked from my laptop. It did not work from her phone.

The reason took a while to find: Funnel’s public entry point publishes only IPv6 addresses. Our home Wi-Fi is IPv4-only and we have almost no cell coverage in the house. So the “public” URL was unreachable from the one place we’d use it.

The solution was the traditional one. Caddy on the VPS, bound to its public IPv4 address, an A record for a subdomain of this site, and Let’s Encrypt handles the certificate. A shared password on the dashboard, with the usual secure cookie settings and a brute-force slowdown, since it’s now genuinely on the internet.

One more wrinkle: my registrar took about an hour to publish the DNS record, and their zone tells resolvers to cache “this name doesn’t exist” for 24 hours. Every device that tried the URL during that hour kept refusing it after the record went live. Airplane mode toggles all around. I stood up a temporary sslip.io hostname on the same server so nobody was stuck waiting.

Challenge 4: the paper flyer problem

Not everything comes through a system. Field trip forms, picture day, a note from a teacher in a WhatsApp group. The dashboard has an upload box that accepts pasted text, a PDF, or a photo. Claude extracts each item with a course, title, due date, and a “kind” (assignment, test, event), resolves phrases like “next Monday” against today’s date, and lists anything ambiguous in a notes field. I review the table, uncheck anything wrong, and add the rest. They go to the calendar like everything else.

Structured output made this trivial. The model returns a typed object that validates against a schema, so there’s no parsing of free text on my side.

What runs now

Every hour the VPS reads Sycamore, updates the database, and pushes anything dated to a dedicated Google Calendar. Every morning at seven I get an email: missing and overdue work grouped by class, what’s due today and this week, new grades since yesterday, anything flagged, and school events for the week. Mondays add an attendance line. The dashboard is a bookmark on two phones.

Marking an item done on the dashboard removes its calendar event. Anything that disappears from Sycamore because a teacher graded or deleted it gets dismissed automatically.

What I’d tell another parent

  1. Ask what student information system the school uses, then check whether it has a parent API. Sycamore does. PowerSchool, Infinite Campus, and others have varying degrees of parent access. This is the front door, and it’s usually unlocked.
  2. Don’t fight the Google Workspace admin. If the school locks student accounts down, the sanctioned data lives elsewhere.
  3. The morning email is the feature. The dashboard is nice, the calendar alerts are nice, but a short email that says “three things are missing” is what actually changes behavior, mine and hers.

The eight missing assignments are now a conversation instead of a surprise at report card time. That was the whole point.