Noah Ross, Gia Mazza, Jasmina Frederico, and Alex Villalba
A complete assignment tracking system that syncs Moodle assignments to a mobile app with AI-powered push notifications. Built with React Native (Expo), Express.js, Chrome Extension, and OpenAI GPT-4o.
hackathon/
├── api/ # Express API server
│ ├── server.js # Express server with SQLite, OpenAI GPT-4o, Expo push
│ ├── package.json # Server dependencies
│ ├── package-lock.json # Locked dependency versions
│ ├── .env # Server environment variables (not committed)
│ ├── hackathon.db # SQLite database (auto-generated)
│ ├── public/ # Admin web dashboard
│ │ └── dashboard.html # Web-based admin interface
│ └── README.md # API documentation
├── CLAUDE.md # Complete technical documentation
├── README.md # This file
├── hackathon-app/ # Expo React Native app
│ ├── app/ # App routes (Expo Router)
│ │ ├── index.tsx # Entry point & auth routing
│ │ ├── onboarding.tsx # Email registration screen
│ │ └── (tabs)/
│ │ ├── index.tsx # Dashboard - assignment list
│ │ ├── explore.tsx # Explore tab
│ │ └── settings.tsx # Settings - notifications & sign out
│ ├── hooks/
│ │ └── useNotifications.ts # Push notification hook
│ ├── .env # Environment variables (not committed)
│ ├── .env.example # Environment template
│ └── package.json # App dependencies
└── chrome-extension/ # Chrome extension for Moodle scraping
├── manifest.json # Extension configuration (Manifest V3)
├── content.js # Scrapes assignments from Moodle DOM
├── popup.html # Extension popup UI
├── popup.js # Popup logic & API communication
└── icons/ # Extension icons
- 📱 Mobile App - Beautiful React Native app with SafeAreaView support for modern iPhones
- 🤖 AI-Powered Notifications - GPT-4o generates Duolingo-style witty reminders every 60 seconds
- 📚 Moodle Integration - Chrome extension scrapes assignments from Lafayette's Moodle calendar
- 🔔 Real-time Sync - Extract and sync assignments with one click
- 💾 SQLite Database - Persistent storage of users and assignments
- ⚙️ Settings Screen - Toggle notifications and sign out functionality
- 📊 Admin Dashboard - Web-based dashboard to view all users and assignments
- 🌐 ngrok Tunneling - Develop locally with public HTTPS URLs
- 🎯 Pull-to-Refresh - Easy assignment syncing in mobile app
- 🔐 Email-based Auth - Simple onboarding with email registration
- Node.js (v16 or higher)
- npm or yarn
- ngrok account (free tier works)
- OpenAI API key (for GPT-4o notifications)
- Physical iOS or Android device (notifications don't work in simulators)
- Expo Go app or EAS development build
- Google Chrome browser (for extension)
- Lafayette College Moodle account (for assignment scraping)
cd api
npm installCreate a .env file in the api directory:
cd api
cat > .env << EOF
PORT=4000
OPENAI_API_KEY=sk-proj-your-api-key-here
EOFGet your OpenAI API key from platform.openai.com
cd api
npm run devThe server will run on http://localhost:4000
The server will:
- Create
hackathon.dbSQLite database automatically - Start cron job for AI-powered notifications (every 60 seconds)
- Serve admin dashboard at
http://localhost:4000
ngrok creates a secure tunnel to your local server, allowing your mobile app to communicate with localhost over the internet.
# macOS (using Homebrew)
brew install ngrok
# Or download from https://ngrok.com/download- Sign up at ngrok.com (free account works)
- Get your auth token from the dashboard
- Configure:
ngrok config add-authtoken YOUR_AUTH_TOKENIn a new terminal window:
ngrok http 4000You'll see output like:
Forwarding https://apparent-stinkbug-moral.ngrok-free.app -> http://localhost:4000
Copy the HTTPS URL - you'll need it for the next step!
cd hackathon-app
npm install
cp .env.example .envEdit .env and add your ngrok URL:
EXPO_PUBLIC_API_URL=https://your-ngrok-url.ngrok-free.app
npx expo start- Scan QR code with Expo Go app on your physical device
- Or press
ifor iOS simulator /afor Android emulator (limited notification support)
Important: Push notifications only work on physical devices!
User Flow:
- Enter your email on the onboarding screen
- Grant notification permissions
- View the dashboard (empty initially)
- Use Chrome extension to sync assignments
- Pull to refresh to see your assignments
- Receive AI-powered notifications every 60 seconds
See Chrome Extension README for detailed instructions.
Quick setup:
- Open Chrome and go to
chrome://extensions/ - Enable "Developer mode" (top right toggle)
- Click "Load unpacked" and select the
chrome-extension/directory - Navigate to https://moodle.lafayette.edu/calendar/view.php?view=upcoming
- Click the extension icon in your Chrome toolbar
- Enter your email and ngrok URL (e.g.,
https://abc123.ngrok-free.app) - Click "Sync Assignments"
- Pull to refresh in the mobile app to see your assignments!
Register a user with email and/or push token.
Request Body:
{
"email": "student@lafayette.edu",
"pushToken": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]"
}Response:
{
"success": true,
"message": "User registered successfully"
}Get all registered users.
Response:
{
"count": 5,
"users": [
{
"email": "student@lafayette.edu",
"push_token": "ExponentPushToken[...]",
"created_at": "2025-10-18T20:00:00.000Z",
"last_seen": "2025-10-18T20:30:00.000Z"
}
]
}Send a push notification to specific user(s).
Request Body:
{
"email": "student@lafayette.edu", // Optional: specific user
"title": "Notification Title",
"body": "Notification message body",
"data": {
"customKey": "customValue"
}
}Response:
{
"success": true,
"message": "Notifications sent successfully",
"tickets": [...]
}Sync assignments from Chrome extension (replaces all existing assignments for user).
Request Body:
{
"email": "student@lafayette.edu",
"assignments": [
{
"id": "3911473",
"courseId": "30874",
"title": "Lab 5 is due",
"course": "ECE 433.01-Fall 2025",
"date": "Tuesday, October 21",
"time": "7:00 AM",
"description": "No late submissions accepted",
"actionUrl": "https://moodle.lafayette.edu/...",
"type": "due",
"component": "mod_assign"
}
],
"extractedAt": "2025-10-18T20:00:00.000Z"
}Response:
{
"success": true,
"message": "Stored 4 assignments for student@lafayette.edu"
}GET /api/assignments?email=student@lafayette.edu
Get assignments for a specific user.
Response:
{
"email": "student@lafayette.edu",
"count": 4,
"assignments": [...]
}-
User Onboarding:
- User enters email on mobile app
- App saves email to AsyncStorage
- App requests notification permissions
- App generates Expo Push Token and registers with server
-
Assignment Syncing:
- User opens Chrome extension on Moodle calendar page
- Extension scrapes assignment data from DOM
- Extension sends assignments to server with user email
- Server stores assignments in SQLite database (replaces old ones)
- Server sends push notification to user
-
AI-Powered Notifications:
- Cron job runs every 60 seconds
- Server queries users with push tokens
- For each user with assignments:
- Sends assignment data to GPT-4o
- GPT-4o generates witty, Duolingo-style reminder
- Server sends personalized push notification
- User receives notification on their device
-
Mobile App:
- Dashboard displays assignments from server
- Pull-to-refresh syncs latest data
- Settings screen to toggle notifications and sign out
- SafeAreaView ensures proper display on modern iPhones
GPT-4o receives:
Assignments:
- Philosophy Essay (Due: Tomorrow, 11:59 PM)
- Math Homework (Due: Friday, 9:00 AM)
- Lab Report (Due: Monday, 5:00 PM)
GPT-4o generates:
"Still working on that Philosophy essay? The deadline won't extend itself... 📝"
Access the web dashboard at your server URL (e.g., http://localhost:4000 or https://your-ngrok-url.ngrok-free.app)
Features:
- View all registered users with push tokens
- See all assignments across all users
- Filter assignments by email
- Send custom notifications to specific users or everyone
- Auto-refreshes every 30 seconds
- Real-time stats (user count, assignment count, active tokens)
Note: No authentication - keep the URL private in production!
- Open mobile app and complete onboarding with your email
- Open Chrome extension on Moodle calendar page
- Enter same email and server URL in extension
- Click "Sync Assignments"
- Pull to refresh in mobile app - assignments should appear
- Wait up to 60 seconds - you should receive AI-generated notification
- Check admin dashboard to see your user and assignments
Send a custom notification:
curl -X POST https://your-ngrok-url.ngrok-free.app/api/send-notification \
-H "Content-Type: application/json" \
-H "ngrok-skip-browser-warning: true" \
-d '{
"email": "student@lafayette.edu",
"title": "Test Notification",
"body": "This is a test message!"
}'Get all users:
curl https://your-ngrok-url.ngrok-free.app/api/users \
-H "ngrok-skip-browser-warning: true"Get assignments for a user:
curl "https://your-ngrok-url.ngrok-free.app/api/assignments?email=student@lafayette.edu" \
-H "ngrok-skip-browser-warning: true"When developing locally, your server runs on localhost:4000, which is only accessible from your computer. Mobile devices can't reach localhost - they need a real URL. ngrok solves this by:
- Creating a public HTTPS URL
- Tunneling all traffic to your local server
- Providing a web interface to inspect requests at
http://localhost:4040
- Free tier URLs change every time you restart ngrok - update your
.envwhen this happens - The tunnel expires when you close ngrok
- For production, use a static domain (paid) or deploy to a cloud server
- ngrok includes browser warnings by default - the app adds
ngrok-skip-browser-warningheader to bypass this
Keep these running in separate terminals:
- Terminal 1:
cd api && npm run dev(server) - Terminal 2:
ngrok http 4000(tunnel) - Terminal 3:
cd hackathon-app && npx expo start(app)
If ngrok restarts and the URL changes:
- Copy the new URL
- Update
hackathon-app/.env - Restart the Expo app
- Email input field
- Registration button
- Automatic navigation to dashboard after registration
- Header with user email
- Assignment cards with course, date, time, description
- Pull-to-refresh functionality
- Empty state with Chrome extension instructions
- Push notification status indicator
- SafeAreaView for notch support
- Account section showing email
- Notification toggle switch
- Sign out button with confirmation dialog
- App version footer
- Example screen with app documentation
- Check that
OPENAI_API_KEYis set correctly in.env - Verify OpenAI API key is valid and has credits
- Check server logs for OpenAI errors
- Fallback messages will be used if GPT-4o fails
- Grant notification permissions in Settings screen
- Use a physical device (simulators don't support push)
- On iOS, close app completely to see banners
- Verify push token is registered via admin dashboard
- Check server is running on port 4000
- Verify ngrok is running:
curl https://your-ngrok-url.ngrok-free.app/ - Update
hackathon-app/.envwith correct ngrok URL - Restart Expo app after changing
.env - Ensure
ngrok-skip-browser-warningheader is included
- Use same email in mobile app and Chrome extension
- Navigate to Moodle calendar page before clicking extension
- Check server logs for POST /api/assignments errors
- Pull to refresh in mobile app after syncing
- Check admin dashboard to verify assignments were stored
- Enable Developer Mode in chrome://extensions
- Reload extension after making changes
- Check browser console for errors
- Ensure you're on Moodle calendar page
- Extension auto-opens correct page if needed
The server uses SQLite with the following schema:
users table:
email(TEXT PRIMARY KEY)push_token(TEXT)created_at(DATETIME)last_seen(DATETIME)
assignments table:
id(TEXT PRIMARY KEY)email(TEXT, FOREIGN KEY)course_id,title,course,date,timedescription,action_url,type,componentextracted_at,created_at
Access database:
cd api
sqlite3 hackathon.db
.tables
SELECT * FROM users;
SELECT * FROM assignments;- Deploy server to cloud provider
- Set environment variables:
PORT=4000OPENAI_API_KEY=sk-proj-...
- SQLite database will persist on disk
- For scale, consider PostgreSQL instead of SQLite
- Add authentication to admin dashboard
- Implement rate limiting
- Update
.envwith production server URL - Build for iOS:
eas build --platform ios - Build for Android:
eas build --platform android - Test thoroughly on physical devices
- Submit to App Store:
eas submit --platform ios - Submit to Play Store:
eas submit --platform android
- Create icons (16x16, 48x48, 128x128)
- Update server URL in extension
- Zip extension folder
- Submit to Chrome Web Store Developer Dashboard
- Fill out store listing
- Submit for review
Server:
- Express.js
- better-sqlite3 (SQLite database)
- expo-server-sdk (push notifications)
- openai (GPT-4o integration)
- node-cron (scheduled jobs)
- dotenv, cors
Mobile App:
- React Native
- Expo (SDK 52+)
- Expo Router (file-based routing)
- expo-notifications (push notifications)
- TypeScript
- AsyncStorage
Chrome Extension:
- Manifest V3
- Vanilla JavaScript
- DOM scraping
Dev Tools:
- ngrok (tunneling)
- nodemon (auto-restart)
- Lines of Code: ~1,800 lines
- Technologies: 15+
- Components: 4 (Server, Mobile App, Chrome Extension, Admin Dashboard)
- AI Integration: GPT-4o for personalized notifications
- Database: SQLite with 2 tables