The 50-click gradebook problem
Why bulk-importing grades from Google Sheets to Google Classroom is so annoying — and how Sheet2Classroom fixes it.
Every semester, teachers export a CSV from Google Sheets, open Google Classroom, wait for the upload parser to decide whether today's column headers match — they don't — and give up. Then grades go in one student at a time: click name, type grade, tab, click next. For a 50-student class, that's 200+ clicks and fifteen minutes of careful focus for something that should have taken ten seconds.
Why CSV imports break
Google Classroom's CSV upload tolerates a narrow window of formats. Column names must match exactly. Grades have to be integers in one known range. If a cell says 85/100, the import fails silently for that row. Fractions, percentages, letter grades, bonus points — all rejected. By the time you figure out which twelve rows got skipped, it's faster to just enter them manually.
The personal-Gmail problem
The deeper issue is Gmail students. Google treats personal @gmail.comaccounts as individuals, not as members of an institution. One consequence: when a teacher's app asks the Classroom API "what's this student's email address", the API returns null. This is a privacy feature, not a bug. But it means no external tool can match personal-Gmail students by email — you get back a roster full of students whose emails you can't see.
Existing bulk-import solutions paper over this in unsafe ways. Apps Script recipes from 2019 blog posts paste emails into the spreadsheet directly — a violation of Classroom's Limited Use policy that can get a teacher flagged by IT. Third-party add-ons of uncertain pedigree ask for full Drive and Classroom write access, far beyond what a grade-push tool actually needs.
How Sheet2Classroom matches students
We match in three stages, falling back only if the previous stage didn't give us an answer:
- Email: exact match between the sheet's email column and the student's Classroom profile email — works for Workspace accounts.
- Roll number / student ID: match against the email prefix before
@. A sheet column of21B0123matches a student with email21B0123@uni.edu. - Name: fuzzy match against the class roster's
fullName. We use exact-normalized equality first, then token-subset ("Alice Chen" matches "Alice May Chen"). When two students share a name we returnambiguous-nameso the teacher can pick the right one manually.
Because we use Classroom's classroom.rosters.readonly scope, step 3 works even for personal Gmail students whose emails are hidden — names are visible in the roster, which is exactly enough information to close the gap.
Grade formats we parse
Teachers format grades six different ways in the same semester: integers (85), decimals (85.5), fractions (85/100), percentages (85%), trailing units (85 pts), and letter grades. We parse all of them, scale correctly relative to the assignment's max points, and flag cells we can't parse (empty, N/A, -) as invalid — the teacher sees exactly which rows will skip before the push.
Why drafts first
Every push lands as a draft grade — visible to the teacher in the gradebook, invisible to students until the teacher explicitly publishes. This is the single highest safety net in the whole workflow. A typo in the sheet, a column mapping mistake, a curve applied to the wrong assignment — none of it escapes to students unless the teacher clicks Publish.
Every destructive update — overwriting an existing assignment's grades — first takes a full snapshot of the previous state. If you pushed the wrong column, one click within 30 days restores every student's grade to the pre-push value. Restore shows a full diff: current grade, what it reverts to, per-student checkbox to opt out of individual rows. The goal is that nothing a teacher does in this tool is ever permanent in a scary way.
Try it
Sheet2Classroom is free. Right now we're in Google OAuth verification window, which caps us at 100 testers — drop your email on the home page and we'll add you.