Compute remaining late tokens per student from a local file or Google Sheet, then write a Canvas-importable CSV.
- Reads token requests from
.xlsx,.csv, or Google Sheets URLs - Supports public sheets (no auth), service account auth, and OAuth user sign-in
- Keeps only the latest request per student+assignment, then sums across assignments
- Writes Canvas-compatible output (default file:
canvas_late_tokens.csv)
python late_token_tracker.py <input_file_or_sheet_url> [options]# Public Google Sheet (no auth)
python late_token_tracker.py "https://docs.google.com/spreadsheets/<SPREADSHEET_ID>" --preview
# Protected Google Sheet with service account
python late_token_tracker.py "https://docs.google.com/spreadsheets/d/<SPREADSHEET_ID>/edit" \
--service-account ~/Downloads/service-account.json --preview
# Protected Google Sheet with OAuth
python late_token_tracker.py "https://docs.google.com/spreadsheets/d/<SPREADSHEET_ID>/edit" \
--oauth-client ~/Downloads/client_secrets.json --preview
# Local file
python late_token_tracker.py tokens.xlsx --preview--service-account PATH: service account key for protected sheets--oauth-client PATH: OAuth desktop client file for interactive sign-in--token-file PATH: OAuth token cache location (default:token.json)--total-tokens N: starting tokens per student (default:4)--score-column NAME: Canvas score column name (default:Late Submission Tokens)--output PATH: output CSV path (default:canvas_late_tokens.csv)--grades-sheet PATH: used exported Canvas grades CSV to validate netids (default:cs380_canvas_grades_sheet.csv)--unmatched-report PATH: manual-review CSV for netids that do not match Canvas (default:unmatched_netids_review.csv)--preview: show per-student summary before writing
- Installation: Installation
- Full setup by auth method: Detailed Setup Guide
- Input requirements: Input Sheet Format
- More command examples: Usage Examples
- Common errors and fixes: Troubleshooting
Install Python dependencies:
pip install -r requirements.txtIf the Google Sheet is publicly accessible and is shared as "Anyone with the link":
python late_token_tracker.py "https://docs.google.com/spreadsheets/d/<SPREADSHEET_ID>/edit" --previewPros: Non-interactive, secure, best for scripts and CI/CD pipelines.
Cons: Requires Google Cloud project setup.
- Go to Google Cloud Console.
- Create a new project or select an existing one (e.g.,
class-late-token-tracker). - Enable the Google Sheets API:
- APIs & Services → Library
- Search "Google Sheets API"
- Click Enable
- Create a service account:
- IAM & Admin → Service Accounts
- Click "Create Service Account"
- Name: e.g.,
late-token-reader - Click Create
- Click Done
- In Service Accounts, click the service account you just created.
- Go to the "Keys" tab.
- Click "Add Key" → "Create new key" → select "JSON" → click "Create".
- A JSON file downloads automatically. Save it locally (e.g.,
~/Downloads/service-account.json).
-
Extract the service account email from the JSON:
grep '"client_email"' ~/Downloads/service-account.json
You'll see something like:
"client_email": "late-token-reader@my-project.iam.gserviceaccount.com" -
Open your Google Sheet.
-
Click Share (top right).
-
Paste the service account email.
-
Give it Viewer access (or Editor if you plan to modify the sheet).
-
Click Share.
python late_token_tracker.py "https://docs.google.com/spreadsheets/d/..." \
--service-account ~/Downloads/service-account.json --previewPros: Any authorized user can sign in with their own Google account.
Cons: Requires OAuth consent screen setup; interactive sign-in each time (or reuse cached token).
- Go to Google Cloud Console.
- APIs & Services → Library → Search "Google Sheets API" → Enable.
- APIs & Services → OAuth consent screen.
- Choose External (or Internal if available for your organization).
- Fill in:
- App name:
Late Token Tracker - User support email: your email
- Developer contact: your email
- App name:
- Click Save & Continue.
- On "Scopes" → Add or Remove Scopes:
- Search for and select:
https://www.googleapis.com/auth/spreadsheets.readonly - Click Update → Save & Continue
- Search for and select:
- On "Test users", click Add users:
- Add emails:
student_name_year@u.northwestern.edu(or any authorized users) - Click Save & Continue
- Add emails:
- Review and click Back to Dashboard.
- APIs & Services → Credentials.
- Click "Create Credentials" → OAuth Client ID.
- Choose Desktop app.
- Name it (e.g.,
Late Token Tracker CLI). - Click Create.
- Click Download (or the download icon).
- Save as
client_secrets.json(e.g.,~/Downloads/client_secrets.json).
python late_token_tracker.py "https://docs.google.com/spreadsheets/d/..." \
--oauth-client ~/Downloads/client_secrets.json --previewA browser window opens; sign in with a Google account that has access to the sheet. Credentials are saved to token.json (reused on next run).
python late_token_tracker.py "<SHEET_URL>" \
--oauth-client ~/Downloads/client_secrets.json \
--token-file ~/.tokens/late-token.json --previewYour token request sheet must include:
Required columns (case-insensitive):
- Student ID: Column named
netid,username,student id, orname - Token usage: Column containing
token,late, orextension - Assignment (optional): Group requests by assignment name
- Timestamp (optional):
timestamp,submitted,date, ortime
Cell values:
- Token count:
1,2.5,1 token,2 tokens, etc. - Multiple rows per student/assignment: only the latest (by timestamp) is used
- Multiple assignments per student: tokens are summed together
Example sheet:
| Student ID | Assignment | Tokens | Timestamp |
|---|---|---|---|
| alice.smith | Essay 1 | 1 | 2025-01-15 |
| alice.smith | Essay 1 | 2 | 2025-01-20 |
| bob.jones | Project A | 1 token | 2025-01-18 |
Output: Alice gets 2 tokens (latest for Essay 1), Bob gets 1 token.
python late_token_tracker.py --helppython late_token_tracker.py "https://docs.google.com/spreadsheets/d/<ID>/edit" --previewpython late_token_tracker.py tokens.xlsx --total-tokens 10 --previewpython late_token_tracker.py tokens.xlsx \
--score-column "Late Tokens Available" --previewpython late_token_tracker.py tokens.xlsx --output my_grades.csvpython late_token_tracker.py tokens.xlsx \
--grades-sheet cs380_canvas_grades_sheet.csv \
--unmatched-report unmatched_netids_review.csv --previewThis checks the extracted netid against the Canvas export's SIS Login ID column using the raw value from the token sheet. Any netid that does not match is written to the manual-review report with its occurrence count and source rows so a grader can debug it before importing into Canvas.
python late_token_tracker.py tokens.xlsx --output canvas_late_tokens.csv
# Then import canvas_late_tokens.csv into Canvas| Error | Cause | Solution |
|---|---|---|
Service account key file not found |
Wrong path to JSON | Use full path: ~/Downloads/service-account.json or /Users/you/Downloads/... |
OAuth client_secrets file not found |
Wrong path to client_secrets.json | Same as above; verify path with ls -la |
Access Denied |
Service account/OAuth user not shared the sheet | Share the sheet with the service account email or OAuth user email |
Sheet is empty |
No data in the sheet | Check the sheet has headers and data rows |
No student ID column found |
Column name mismatch | Ensure column name contains student id, netid, username, or name |
No token usage columns found |
Column name mismatch | Ensure column name contains token, late, or extension |
- Test with
--previewfirst to see results before writing. - Use service accounts for automation (scripts, scheduled jobs).
- Use OAuth only for interactive user access where users sign in themselves.
- Keep credentials private: add
*.jsonandtoken.jsonto.gitignore. - Reuse OAuth tokens: once signed in,
token.jsonis cached and reused (no re-login on next run). - Group by latest assignment: if a student requests multiple times for the same assignment, only the latest request is counted.
File: canvas_late_tokens.csv (or custom with --output)
Format: Canvas-compatible gradebook import
| SIS Login ID | Late Submission Tokens |
|---|---|
| alice.smith | 3 |
| bob.jones | 4 |