Skip to content

About

Resources

Security policy

Stars

10 stars

Watchers

0 watching

Forks

Repository files navigation

Student Analytics logo

Student Analytics Platform

Sign up with a college address, get approved, and watch your class's GitHub activity
sync itself into eight role-based dashboards — no spreadsheet surgery.

last commit license: All rights reserved CodeQL: passing platform: Windows | macOS | Linux Python: 3.11+ Vercel: Hosting platform

Features · Architecture · Tech stack · Preview · Get Started · Installation · Troubleshooting · Status · Contributing · License

Live deployment: https://student-analytics-iota.vercel.app


Features

Authentication and roles

  • Email/password sign-up restricted to college addresses, plus Continue with Google when OAuth credentials are configured; GitHub and LinkedIn sign-in are wired for linking a profile to an existing session.
  • Three roles — student / faculty / admin — enforced by a route-level auth gate, not by hiding UI. Sessions are HMAC-signed cookies.
  • Domain allowlist lives in ALLOWED_OAUTH_DOMAINS (default mitwpu.edu.in) and is re-checked server-side on every signup and OAuth callback.

Onboarding and registrar review

  • Students submit PRN, degree, division, practical batch (lab section), semester, and main batch from the Onboarding page; faculty/admin approve, reject, edit, or remove entries from a pending ledger.
  • Approval promotes the student's linked GitHub handle into their account identity — that handle is what every later sync reads.

Self-updating GitHub data

  • Nightly Vercel cron against /sync/accounts, a Sunday /sync/weekly announcement run, and a GitHub Actions Heavy Sync daily at 00:00 UTC (deep commits/PRs/team repos, one student at a time, resumable and rate-limit aware).
  • Students also self-sync on login, and admins can force a fleet refresh from /debug/force_sync_all.
  • Rate-limit aware: GitHub token rotation (GITHUB_TOKEN + extra GITHUB_TOKEN_* vars), waits for the published reset epoch, and surfaces friendly errors instead of tracebacks.

Dashboards and submissions, one sidebar

  • Navigation includes Overview, Onboarding, Students, Repositories, AutoCAD, Leaderboards, Support, Settings, and My Profile — plus the same profile panel behind the sidebar avatar at /me. Admins also have the certificate Verification page.
  • AutoCAD currently accepts student .dwg uploads up to 4 MB and gives faculty/admin a recent-submissions download inbox. Browser previews, assignment grouping, grades, and remarks are planned follow-up work; see documentation/AutoCAD Submissions.md.
  • Pages render from the synced account fleet — no upload required — and the Students and Repositories tables export CSV or XLSX.
  • Repos and followers are also reported per year of account age (Repos_Per_Account_Year, Followers_Per_Account_Year) so newer accounts are not punished.

Leaderboards with moderation

  • Six boards — most active repositories, most commits, most stars, top starred repositories, HackerRank problems solved, and top practice scores — across a filtered cohort (division, batch, semester, time window), with a GitHub/HackerRank platform switcher.
  • Faculty/admin can blacklist students or hide repositories from a board; the moderation state persists for the whole fleet.

Support tickets

  • Students raise tickets with optional image attachments (validated by magic bytes, not just the file extension); staff reply and move them through Open → In Progress → Follow up → Resolved.

Notifications

  • Server-sent events bell at /api/notifications/stream — students see their own issue alerts, staff see ticket alerts, every role gets the Sunday top-committer announcement, each with a deep link.

HackerRank insights

  • Lazy profile tab backed by a vendored, unauthenticated HackerRank client (fresh fetch per open, display-only): tier-colored hex skill badges, stars, solved counts, last-active, and a paginated solved-questions list.
  • Two leaderboard boards — Most Problems Solved and Top Practice Scores — rank snapshots stored in Postgres hackerrank_snapshots (in-memory fallback); unsynced profiles show as awaiting sync, and both boards support the admin blacklist like every other board.
  • Leaderboard data refreshes daily at midnight (IST) via .github/workflows/hackerrank-sync.yml: the GitHub Actions runner (not Vercel) pulls the stale-first queue from GET /api/hackerrank/handles, fetches profiles at a polite pace, and posts results to POST /api/hackerrank/snapshot. Profile opens never sync leaderboards — the daily run owns freshness. Needs the same APP_URL + CRON_SECRET repo secrets as heavy-sync.yml.
  • Manual backfill still exists: admins/faculty hit Sync HackerRank on the Leaderboards page (or POST /sync/hackerrank), which syncs a few of the stalest profiles per call on a light sequential path (3 requests/profile, 1s pacing, aborts at the first 429).

Storage that fits the host

  • DATABASE_URL → Neon Postgres (schema idempotently initialised at startup); no DATABASE_URL → the bundled SQLite files, so local dev needs zero setup.
  • GitHub responses cached for 1 h in Upstash Redis when configured, in-process otherwise.

Architecture

Server-rendered, deliberately thin: FastAPI routes in app/main.py handle HTTP, page payload builders live in app/views.py, and everything that talks to the internet or does math stays in app/services.py + app/sync.py. Jinja2 templates render HTML; ECharts option objects are composed server-side and drawn client-side (Overview only); live updates arrive over Server-Sent Events.

sign-up / OAuth → onboarding approval → sync (cron + Actions + on login)
      → account snapshots (Postgres or SQLite) → views.py payloads → 8 pages
app/            FastAPI application
  main.py         routes, auth gate, cron endpoints
  services.py     GitHub API + aggregation (ported, logic frozen)
  sync.py         snapshot compute + fleet sweep
  views.py        page payload builders
  charts.py       legacy Plotly builders (unused — charts are ECharts)
  auth.py         sessions, roles, domain gate
  db.py           Postgres query layer (SQLite fallback)
  templates/      Jinja2 pages + partials
static/         CSS, JS, logo (Favicon.png)
tests/          pytest suite (two-mode parity harness)
scripts/        perf_loop.py (page-timing harness)
documentation/  Bug Tracker, Features List, Taskflow, ANALYSIS, AutoCAD Submissions
vercel.json     ASGI entry + cron schedule

Tech stack

Layer Stack
Language Python
Web framework FastAPI Uvicorn
Templating Jinja
Interactivity SSE
Charts ECharts
Data pandas openpyxl
OAuth Authlib
Cache Upstash
Database PostgreSQL SQLite
CI GitHub Actions
Tests pytest
Hosting Vercel

Badges carry the app palette instead of per-brand colors: label eee9df (the --bg paper cream from static/theme.css) and message be342b (the --red behind every .btn-primary — the same vermilion family as the logo's wordmark), with dark ink icons (142235, the --text color) on the cream label and white text on the red message. The CodeQL badge is GitHub-rendered and keeps its own colors; logo= is omitted where simple-icons has no slug (uvicorn, openpyxl).


Preview

No screenshots yet — add captures under docs/screenshots/ following the comment above.


Get Started

1. Clone

git clone https://github.com/Horrid-12/Student-Analytics.git
cd Student-Analytics

2. Install

python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Windows (PowerShell)
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -r requirements.txt

3. Configure secrets — one file

Everything goes in .env.local (gitignored, auto-loaded at startup by app/env.py, and the file vercel env pull writes — shell environment variables always win over it). There is no .env.example; the variables below are exactly what the code reads.

Verified: import app.main succeeds with an empty environment and no .env.local — nothing here is required for the server to start. The Required column says what degrades.

Variable Required What it controls
GITHUB_TOKEN Recommended 5,000 GitHub requests/hour instead of 60; without it the nightly sync rate-limits on any real fleet. Extra GITHUB_TOKEN_* vars are picked up as rotation tokens.
AUTH_SECRET Recommended HMAC key for session cookies; absent → server warns and derives a per-process secret, so logins drop on every restart.
GOOGLE_CLIENT_ID + GOOGLE_CLIENT_SECRET Optional Shows Continue with Google; both absent → button hidden, email/password still works.
GITHUB_OAUTH_CLIENT_ID + GITHUB_OAUTH_CLIENT_SECRET Optional GitHub sign-in used to link a profile to an existing session.
LINKEDIN_CLIENT_ID + LINKEDIN_CLIENT_SECRET Optional LinkedIn profile linking.
ALLOWED_OAUTH_DOMAINS Optional (default mitwpu.edu.in) College-domain allowlist for signup and OAuth, comma- or space-separated.
OAUTH_REDIRECT_BASE_URL Optional (defaults to the request origin) Canonical origin for OAuth callbacks — set it to the deployed origin in production.
CRON_SECRET Required for automation Authorizes POST /sync/accounts, /sync/weekly, POST /api/sync/single/{email}, and GET /api/users/approved when no admin session is present; absent → 403 for Vercel Cron and the GitHub Action.
DATABASE_URL Optional (aliases POSTGRES_URL, TEST_DATABASE_URL) Neon Postgres storage; absent → bundled SQLite files (analytics_history.db, users.db, …).
DATABASE_URL_UNPOOLED / UNPOOLED Optional Direct (non-pooled) connection used for schema DDL.
UPSTASH_REDIS_REST_URL + UPSTASH_REDIS_REST_TOKEN Optional Cross-instance GitHub response cache; absent → in-process cache.
ADMIN_EMAILS, FACULTY_EMAILS, ADMIN_PASSWORD_HASH, ADMIN_NAME Optional Staff roles for password logins without a pre-seeded database row.
FACULTY_INVITE_EMAIL + FACULTY_INVITE_PASSWORD_HASH (or FACULTY_INVITE_PASSWORD) Optional One-time pre-saved faculty login — first sign-in forces /faculty-setup (welcome popup → new email + password), then the invite stops working. DB invites via python -m app.seed_users EMAIL PASS faculty --invite.
SYNC_TTL_SECONDS Optional (default 3600) Snapshot freshness window — younger snapshots are skipped during a sweep.
APP_URL GitHub Actions secret Base URL the Heavy Sync workflow calls; not read by the app itself.
TEST_DATABASE_URL Test only Enables tests/test_db_layer.py.

Minimal .env.local to get real data flowing:

GITHUB_TOKEN="ghp_pasteYourTokenHere"
AUTH_SECRET="a-long-random-string"
ALLOWED_OAUTH_DOMAINS="your-college.edu"
CRON_SECRET="another-long-random-string"

A free classic token from https://github.com/settings/tokens with no extra scopes is enough — only public data is read.

4. Run

uvicorn app.main:app --port 8001 --reload
Windows (PowerShell)
.\.venv\Scripts\python.exe -m uvicorn app.main:app --port 8001 --reload

Or just .\run.bat (macOS/Linux: ./run.sh) — both pick a usable interpreter, pick port 8001, and open the browser.

Open http://localhost:8001.

5. Daily workflow

  1. Create an account with a college address (or sign in with Google).
  2. Submit the onboarding form (PRN, degree, division).
  3. A faculty/admin account approves it — bootstrap staff once with:
    .\.venv\Scripts\python.exe -m app.seed_users admin@your-college.edu "secret" admin
  4. Data arrives on its own: nightly cron, the 6-hourly Heavy Sync Action, and self-sync on login. Admins can force /debug/force_sync_all.
  5. Explore the sidebar pages; export any table to CSV/XLSX.

Optional: Postgres

.\.venv\Scripts\python.exe -m app.init_db             # create tables (idempotent)
.\.venv\Scripts\python.exe -m app.seed_users admin@col.edu "secret" admin

Without DATABASE_URL everything transparently falls back to SQLite — skip this section for local dev.

Installation

Platform Prerequisites Install Run
Windows (PowerShell) Python 3.11+ on PATH python -m venv .venv then .\.venv\Scripts\python.exe -m pip install -r requirements.txt .\run.bat
macOS Python 3.11+ (brew install python) python3 -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt ./run.sh
Linux Python 3.11+ via your package manager python3 -m venv .venv && . .venv/bin/activate && pip install -r requirements.txt ./run.sh

Every platform serves the same thing: uvicorn app.main:app --port 8001. Prefer manual control? Use the raw uvicorn command from Get Started → 4. Run.


Troubleshooting

Symptom Fix
ModuleNotFoundError: No module named 'pandas' Activate the venv first, then pip install -r requirements.txt
"GitHub API rate limit reached" Set GITHUB_TOKEN in .env.local and restart, or wait for the reset time shown in the error
"Continue with Google" button missing GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET are absent — add them to .env.local and restart
Cron or Heavy Sync returns 403 CRON_SECRET is unset or differs between Vercel env and the GitHub Actions secret
Pages show a placeholder screen Approve at least one onboarding submission and let a sync run (or hit /debug/force_sync_all as admin)
Works locally but not on Vercel Push the same values as Vercel project env vars — secret values download as empty stubs, so vercel env pull alone never restores them
Port 8001 already in use uvicorn app.main:app --port 8002
App breaks after pulling changes Re-run pip install -r requirements.txt — dependencies may have changed

Testing

Run from the repo root. The suite exercises both the frozen legacy services.py and the ported app/services.py:

# All tests vs the frozen legacy module
.\.venv\Scripts\python.exe -m pytest tests\ -q

# Same suite vs the port
$env:MODULE_UNDER_TEST="app.services"; .\.venv\Scripts\python.exe -m pytest tests\ -q; Remove-Item Env:\MODULE_UNDER_TEST

# Focused runs
.\.venv\Scripts\python.exe -m pytest tests\test_github_client.py -q   # transport/cache, no network
.\.venv\Scripts\python.exe -m pytest tests\test_pages_36.py -q        # every page renders/exports

# Postgres path (skipped unless set)
$env:TEST_DATABASE_URL="postgresql://..."; .\.venv\Scripts\python.exe -m pytest tests\test_db_layer.py -q; Remove-Item Env:\TEST_DATABASE_URL

Deploying (maintainers)

The app runs on Vercel with the Python runtime — vercel.json points builds/routes at app/main.py directly (no Mangum wrapper):

  1. Push to main; the linked Vercel project deploys on every push.
  2. Set project env vars: at minimum GITHUB_TOKEN, AUTH_SECRET, CRON_SECRET, ALLOWED_OAUTH_DOMAINS, and OAUTH_REDIRECT_BASE_URL (the canonical deployment origin, no trailing path).
  3. Register …/auth/google/callback and …/auth/github/callback on that origin with each OAuth provider.
  4. Optional: DATABASE_URL (then run python -m app.init_db once), UPSTASH_REDIS_REST_URL + UPSTASH_REDIS_REST_TOKEN, and the Google/GitHub/LinkedIn client pairs.
  5. Cron is already wired: vercel.json schedules /sync/accounts nightly and /sync/weekly on Sundays — they 403 until CRON_SECRET matches.
  6. For the Heavy Sync GitHub Action, add repository secrets APP_URL (production origin) and CRON_SECRET (same value as Vercel).

Status

Area State
Production Live at https://student-analytics-iota.vercel.app
CI CodeQL on push/PR to main plus a weekly run; Dependabot weekly for pip and GitHub Actions
Scheduled jobs Vercel crons (nightly accounts + weekly announcement), GitHub Actions Heavy Sync daily (00:00 UTC) and HackerRank sync daily (00:00 IST)
Test suite Green — 734 passed / 10 skipped (default mode), 729 passed / 15 skipped (MODULE_UNDER_TEST=app.services), 2026-10-22. The Postgres layer (tests/test_db_layer.py) stays skipped without TEST_DATABASE_URL.

Contributing

  1. Pick an open item from documentation/Bug Tracker.md, or an unbuilt row in documentation/Features List.md, and claim it.
  2. Work on a branch, not straight on main.
  3. After every change: keep documentation/Features List.md in sync with what shipped, and log any fixed bug in documentation/Bug Tracker.md.
  4. Never commit student data files, tokens, or .streamlit/secrets.toml — rosters (*.xlsx) and env files are gitignored on purpose.
  5. Report security issues per SECURITY.md — not through public issues.

License

No license file is published yet — all rights reserved by the authors. If you want to use or fork this beyond the terms your institution grants, open an issue to request a license.

About

Resources

Security policy

Stars

10 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages