Fink.io
AI Quiz Platform за ФИНКИ
A full-stack educational platform where ФИНКИ students upload study materials (PDF, DOCX, PPTX) and AI generates quality quizzes in Macedonian. Built with Django + Channels, React + Vite, and Google Gemini AI. Features real-time WebSocket chat, friends system with notifications, badges, leaderboards, and instructor analytics.
SCREENSHOTS
FEATURES
AI Quiz Generation
Upload PDF/DOCX/PPTX → Google Gemini extracts text and generates quality Macedonian quizzes with multiple-choice, multi-select, and essay questions.
Real-time Chat
WebSocket chat between friends with typing indicators, online presence, and optimistic UI (no message duplication).
Friends System
Facebook-style requests → accept/reject. Search users, view public profiles, see online status, with bell notifications for activity.
Badges & Leaderboards
14 badge types × 4 tiers (bronze/silver/gold/diamond). Streaks, points, rank within role. Auto-awarded via Django signals.
Instructor Analytics
Per-quiz dashboard with average score, score distribution histogram, completion rate. Clickable stat cards switch between drafts/published/games tabs.
Light & Dark Themes
Toggle between 3 modes: Light / Dark / System. Glassmorphism design with CSS variables. Anti-flash on page load.
Role-based Access
5 roles: guest, student, instructor, moderator, admin. JWT auth with 24h access + 30d refresh tokens.
170 Subjects Seeded
Pre-populated with all FINKI subjects across 4 years × 8 semesters, mandatory + elective groups. Personalized by student's current year.
ARCHITECTURE
The backend serves both HTTP (Django REST Framework) and WebSocket (Channels + Daphne) on the same ASGI app. WebSocket connections use a JWT token in the query string, authenticated by a custom middleware. Presence (online/offline) is tracked in-memory with a thread-safe counter — multi-tab support per user.
TECH STACK
BACKEND
- Python
3.12 - Django
5.x - Django REST Framework
3.15 - Django Channels
4.1+ - Daphne (ASGI server)
4.1+ - simplejwt
5.x - google-genai SDK
1.0+ - pdfplumber / python-docx / python-pptx
- django-cors-headers
FRONTEND
- React
18.x - Vite
5.x - Tailwind CSS
3.x - Zustand
4.x - React Router
6.x - Axios
- Lucide React (icons)
- Native WebSocket API
INFRASTRUCTURE
- PostgreSQL
16(production) - SQLite (development)
- Render.com (hosting)
- Google Gemini API (free tier)
- Multi-model fallback: 2.5-flash-lite → 2.5-flash → 2.0-flash → 1.5-flash
- In-memory channel layer (single instance)
DESIGN SYSTEM
- Fraunces (display serif)
- Manrope (UI sans)
- JetBrains Mono (meta)
- Accent:
#ff5b1f(бренд оранж) - CSS variables for theming
- Glassmorphism + backdrop-blur
API ENDPOINTS
AUTH
POST /api/auth/token/ login
POST /api/auth/token/refresh/ refresh
POST /api/accounts/register/ register
ACCOUNTS & FRIENDS
GET /api/accounts/me/ мојот профил
PATCH /api/accounts/me/ уреди (multipart)
GET /api/accounts/me/subjects/ мои предмети
GET /api/accounts/me/friend-requests/ pending барања
GET /api/accounts/users/<id>/profile/ јавен профил
POST /api/accounts/users/<id>/friend-request/ испрати
POST /api/accounts/friend-requests/<id>/respond/ accept/reject
DELETE /api/accounts/users/<id>/friend/ отстрани
GET /api/accounts/search/?q=... пребарувај
MATERIALS (БАЗИ)
GET /api/materials/databases/ јавен каталог
POST /api/materials/ прикачи
POST /api/materials/<id>/like/ like
POST /api/materials/<id>/save/ bookmark
POST /api/materials/<id>/download/ track
GET /api/materials/saved/ мои зачувани
QUIZZES
GET /api/quizzes/ листа + филтри
GET /api/quizzes/mine/ мои
GET /api/quizzes/saved/ зачувани
POST /api/quizzes/create/ мануелно
POST /api/quizzes/generate/ AI генерирај од material
POST /api/quizzes/<id>/publish/
POST /api/quizzes/<id>/like/
POST /api/quizzes/<id>/save/
GET /api/quizzes/<id>/play/ играj
POST /api/quizzes/<id>/submit/ одговори
GET /api/quizzes/attempts/ моја историја
NOTIFICATIONS
GET /api/notifications/ листа
GET /api/notifications/count/ непрочитани
POST /api/notifications/<id>/read/
POST /api/notifications/read-all/
CHAT (REST + WS)
GET /api/chat/conversations/ разговори
POST /api/chat/conversations/start/ {user_id}
GET /api/chat/conversations/<id>/messages/
POST /api/chat/conversations/<id>/read/
GET /api/chat/unread-count/
GET /api/chat/online-friends/
WS: wss://api/ws/chat/?token=<jwt>
ROLES & PERMISSIONS
| Role | Capabilities |
|---|---|
guest |
View public quizzes (no login) |
student |
Play quizzes, upload materials, create quizzes, chat, friends |
instructor |
+ Command dashboard, per-quiz analytics |
moderator |
+ Review reported content queue |
admin |
+ User management, role changes, platform-wide control |
CHALLENGES SOLVED
WebSocket message duplication
Initially, sent messages appeared twice (optimistic UI + WS echo). Solved by giving each message a temp_id from the client; the server replies only to the sender with message_confirmed containing that temp_id, allowing replacement of the optimistic entry without dupes.
Gemini API 503 — model overload
The free tier of Gemini frequently returns "model overloaded" errors. Implemented a fallback chain that tries 4 models with 2 retries each (2s backoff): 2.5-flash-lite → 2.5-flash → 2.0-flash → 1.5-flash. Success rate jumped from ~40% to ~95%.
Online presence across multiple tabs
A user with 2 tabs open should stay online if they close one. Built a thread-safe in-memory counter that tracks active connections per user — only broadcast "offline" event when the count hits zero. Friends get notified via WS group messages.
Dark theme — hardcoded colors everywhere
67 hardcoded color classes (bg-cream, text-ink-900, etc.) made some pages invisible in dark mode. Migrated to semantic CSS variables (bg-bg, text-fg, text-muted) with a batch sed script across all 14 pages.
Render free tier sleep
Free tier instances spin down after 15min idle. WebSocket reconnects automatically with exponential backoff; users see a "Се поврзува…" indicator. First request after sleep takes 30-60s to wake up — acceptable for a student platform.