Skip to content
varunanand2006Public

About

AI-powered UMD GenEd discovery and schedule planner

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

GenTerp

Course search and schedule planner for UMD. Built it because picking GenEds every semester was annoying and I wanted something that could actually filter by requirement combinations.

Live: genterp.vercel.app


What it does

  • Boolean GenEd search — type something like DSNS OR (DVUP AND DSHS) and it actually parses it correctly
  • Department / level filtering — filter by dept code (CMSC), level pattern (CMSC4XX), or exact course (CMSC131)
  • AI summaries — pros/cons and a vibe check per course, generated from PlanetTerp student reviews using Gemini
  • Live seat counts — fetches real-time availability from umd.io when you expand a card, caches it back to Supabase
  • Schedule builder — weekly calendar with conflict detection; star a section to add it, click the grid to add manual blocks
  • Course tracker — pin sections across semesters, works without an account (everything is in localStorage)
  • Dark mode — persisted, no flash on load

Stack

Framework Next.js 16 (App Router)
Database Supabase (PostgreSQL)
Styling Tailwind CSS 4 + shadcn/ui
State Zustand + localStorage
Language TypeScript strict mode
AI Google Gemini 2.5 Flash Lite
Deployment Vercel + cron jobs
Package manager pnpm

How the search works

GenEd tags from umd.io come as a nested string[][] (array of requirement groups). I store two versions:

  • gen_ed_raw JSONB — the original nested structure, used for AND co-occurrence checks
  • gen_ed_tags TEXT[] — flattened and GIN-indexed for fast overlap queries

The search bar has a small recursive-descent parser that converts boolean expressions into parameterized SQL. A JS post-filter on the results enforces the AND semantics precisely (both tags have to be in the same group, not just anywhere in the course).

Meeting times are stored as military integers (1045 = 10:45am) so conflict detection is just an integer range comparison.


Live data

  • Vercel cron jobs sync courses at 6am and sections at 7am daily
  • When you expand a card, it fetches fresh seat counts from umd.io and writes them back to Supabase in the background
  • Falls back to cached DB data if umd.io is down

Project structure

genterp/
├── app/
│   ├── api/
│   │   ├── search/           # main search endpoint
│   │   ├── course/[id]/      # course detail + live sections
│   │   └── sync/             # cron sync endpoints
│   ├── course/[id]/          # course detail page
│   └── page.tsx              # search + calendar layout
├── components/
│   ├── search/               # SearchBar, DeptFilter, CourseCard, etc.
│   ├── calendar/             # WeekView, PinnedList
│   └── icons/                # custom SVG components
├── lib/
│   ├── search/parse-boolean.ts   # the boolean parser
│   ├── conflict-detection.ts
│   └── semester.ts
├── store/schedule.ts         # Zustand store (pins, stars, blocks)
└── scripts/                  # admin scripts for AI summary generation

External APIs

  • umd.io — course catalog and sections (no auth)
  • PlanetTerp — GPA and student reviews (no auth)
  • Google Gemini — AI summary generation (admin script only)

Local dev

pnpm install

cp .env.example .env.local
# fill in: NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY,
#          SUPABASE_SERVICE_ROLE_KEY, GOOGLE_API_KEY, CRON_SECRET

pnpm dev

To regenerate AI summaries for a department:

pnpm tsx scripts/generate-ai-summaries.ts --dept CMSC --skip-existing

# dry run first if you want
pnpm tsx scripts/generate-ai-summaries.ts --dry-run --limit 5

Deployment

Vercel, with two cron jobs in vercel.json:

{
  "crons": [
    { "path": "/api/sync/courses",  "schedule": "0 6 * * *" },
    { "path": "/api/sync/sections", "schedule": "0 7 * * *" }
  ]
}

Both require a CRON_SECRET Bearer token.

About

AI-powered UMD GenEd discovery and schedule planner

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages