# Mülakat Duygu Analizi Platformu — Plan Spesifikasyonu

---

## 1. Ön Yüz (Frontend)

### 1.1 User-Login Ekranı (kod ile)

**Detay:** Django'nun built-in `django.contrib.auth` sistemi ile username/password login.

| Olası Pürüz | Açıklama | Çözüm Önerisi |
|---|---|---|
| Session güvenliği | CSRF, session hijacking riski | Django `{% csrf_token %}` + `SESSION_COOKIE_HTTPONLY = True` + `SECURE_SSL_REDIRECT` (prod) |
| Şifre politikası | Zayıf şifre kabul edilebilir | `AUTH_PASSWORD_VALIDATORS` ayarlarını aktif et |
| Başarısız giriş saldırısı | Brute-force denemeleri | `django-axes` veya basit rate-limiting middleware ekle |

> [!TIP]
> **Alternatif:** İleride OAuth2 (Google/Microsoft) eklemek isterseniz `django-allauth` paketini şimdiden entegre etmek kolaydır. Şu an için gerek yok ama DB şemasını buna uyumlu tutmak avantajlı olur.

---

### 1.2 Mülakat Başlat / Bitir Butonları

**Detay:** Tek sayfa üzerinde "Başlat" ve "Bitir" butonları. Başlat → kamerayı aç + WebRTC bağlantısını kur + soruları göster + backend'e session başlat sinyali gönder. Bitir → WebRTC bağlantısını kapat + her şeyi durdur + session'ı kapat.

| Olası Pürüz | Açıklama | Çözüm Önerisi |
|---|---|---|
| Kamera izni reddedilirse | Kullanıcı tarayıcıda izin vermezse akış kırılır | `getUserMedia` hata yakalama ile kullanıcıya uyarı göster, başlat butonunu devre dışı bırak |
| Çift tıklama / race condition | "Başlat"a iki kez basılırsa birden fazla session açılır | Butonu tıklandığında disable et, backend'den cevap gelince tekrar aktif et |
| Sayfa yenileme / kaza kapanması | Mülakat ortasında sekme kapanırsa veri kaybı | `beforeunload` event'i ile uyarı göster + backend tarafında "orphan session" temizleme mekanizması (cron/celery task) |
| Video akışı kesintisi | Internet veya kamera kopması | WebRTC `oniceconnectionstatechange` event'i ile bağlantı durumu takibi, kesilirse kullanıcıya bildirim + otomatik yeniden bağlanma (ICE restart) |

> [!IMPORTANT]
> **Öneri:** Butonların durumları (idle → recording → finished) bir state machine ile yönetilmeli. Basit bir `enum` + JavaScript state yönetimi karmaşıklığı azaltır.

---

### 1.3 TR-ENG Content (Çok dilli destek)

**Detay:** Arayüz metinleri Türkçe ve İngilizce olarak sunulacak.

| Olası Pürüz | Açıklama | Çözüm Önerisi |
|---|---|---|
| Hardcoded stringler | Metinler template'lere gömülürse dil ekleme zorlaşır | Django `i18n` framework'ü kullan: `{% trans %}` tag'leri + `.po` dosyaları |
| Dil değiştirme UX | Dil değiştiğinde sayfa yenilenmesi gerekir | `django.middleware.locale.LocaleMiddleware` + URL prefix (`/tr/`, `/en/`) veya session-based dil tercihi |
| Mülakat soruları dili | Sorular DB'de ise her sorunun iki dil versiyonu lazım | Sorular tablosunda `question_tr` + `question_en` alanları veya ayrı `Translation` tablosu |

> [!TIP]
> **Avantajlı Alternatif:** Eğer sadece 2 dil olacaksa (TR/EN), Django `i18n` yerine basit bir JSON-based dictionary yaklaşımı da yeterli olabilir — daha az overhead, daha hızlı geliştirme. Ama ileride 3+ dil planlıyorsanız `i18n` tercih edin.

---

### 1.4 Dummy Mülakat Soruları

**Detay:** Geliştirme/demo aşamasında kullanılacak sabit soru seti.

| Olası Pürüz | Açıklama | Çözüm Önerisi |
|---|---|---|
| Sorular nerede tutulacak? | Kod içinde mi, DB'de mi, JSON dosyasında mı? | **Öneri:** DB'de tutun (`Question` modeli). Fixture/seed script ile dummy dataları yükleyin (`python manage.py loaddata`). İleride gerçek sorularla değiştirmek sorunsuz olur |
| Soru sıralaması | Herkese aynı sırada mı, rastgele mi? | Şimdilik sıralı, ama modelde `order` alanı bırakın. İleride `random` veya `adaptive` gibi stratejiler eklenebilir |
| Soru tipi çeşitliliği | Sadece metin mi, yoksa seçenekli/video sorular da olacak mı? | Şimdilik sadece metin. Ama modele `question_type` (text, multiple_choice, video) alanı ekleyin — bozmaz, genişletir |

---

### 1.5 WebRTC — Gerçek Zamanlı Video Akışı

**Detay:** Kullanıcının kamerasından alınan video akışını WebRTC ile sunucuya (veya doğrudan model servisine) gerçek zamanlı iletmek. Bu sayede frame'ler anlık olarak emotion inference'a gönderilebilir ve video kaydı sunucu tarafında yapılabilir.

**Akış:**
```
Browser (getUserMedia) → WebRTC PeerConnection → Signaling (Django Channels) → Media Server → Model Service
```

| Olası Pürüz | Açıklama | Çözüm Önerisi |
|---|---|---|
| Signaling sunucusu | WebRTC bağlantısı kurmak için SDP/ICE mesaj alışverişi gerekir | **Django Channels** (WebSocket) ile signaling endpoint'i oluşturun. `ws://host/ws/signaling/<session_id>/` |
| NAT traversal | Kullanıcı NAT/firewall arkasındaysa doğrudan bağlantı kurulamaz | **STUN sunucusu** (ücretsiz: Google STUN `stun:stun.l.google.com:19302`) + gerektiğinde **TURN sunucusu** (self-hosted: `coturn`) |
| TURN sunucusu maliyeti | TURN relay trafiği bant genişliği tüketir | Önce STUN ile dene, sadece STUN başarısız olursa TURN'e düş. Geliştirmede localhost'ta TURN gerekmez |
| Media server ihtiyacı | WebRTC peer-to-peer'dır, sunucunun videoyu alması için bir media endpoint lazım | **Basit yol:** `aiortc` (Python WebRTC kütüphanesi) ile sunucu tarafında peer oluşturun. **Gelişmiş yol:** Janus/mediasoup gibi bir SFU kullanın |
| Tarayıcı uyumluluğu | Bazı eski tarayıcılar WebRTC desteklemez | `adapter.js` shim kütüphanesini ekleyin. Modern tarayıcılarda (Chrome, Firefox, Edge, Safari 11+) sorun yok |
| Ses vs sadece video | Mülakatda ses de kaydedilecek mi? | `getUserMedia({ video: true, audio: true })` ile hem video hem ses alın. WebRTC her ikisini de aynı PeerConnection üzerinden taşır |

> [!IMPORTANT]
> **WebRTC vs MediaRecorder Karşılaştırması:**
>
> | | WebRTC | MediaRecorder (mevcut plan) |
> |---|---|---|
> | Real-time streaming | ✅ Sunucuya anlık akış | ❌ Sadece client-side kayıt |
> | Sunucu tarafı kayıt | ✅ Media server ile | ❌ Upload gerekli |
> | Real-time inference | ✅ Frame'ler anında sunucuya | ⚠️ Batch POST gerekli |
> | Karmaşıklık | Yüksek (signaling, ICE, TURN) | Düşük |
> | Bant genişliği | Sürekli stream | Sadece upload anında |
>
> **Öneri:** İkisini birlikte kullanın — **WebRTC** real-time emotion inference için frame stream'i, **MediaRecorder** yedek video kaydı (client-side, WebRTC kesilirse kayıp olmaz).

> [!TIP]
> **`aiortc` ile Basit Entegrasyon:** Python'da WebRTC peer oluşturmak için `aiortc` kütüphanesi idealdir. Django Channels ile aynı async event loop üzerinde çalışır. Video frame'lerini doğrudan `VideoStreamTrack` üzerinden alıp model servisine iletebilirsiniz.

---

## 2. Arka Yüz (Backend)

### 2.1 User-Login (kod ile)

**Detay:** Frontend ile aynı auth sistemi. Django `auth` backend, session management.

| Olası Pürüz | Açıklama | Çözüm Önerisi |
|---|---|---|
| API vs Template-based auth | Kamera erişimi JS gerektirdiğinden, sayfalar ne kadar SPA olacak? | **İki yol:** (A) Klasik Django template + AJAX çağrıları (basit), (B) DRF + JWT ile tam API (esnek ama karmaşık). **Öneri:** Şu an için (A) yeterli |
| User modeli genişletme | İleride kullanıcıya özel alanlar lazım olabilir (departman, pozisyon vb.) | `AbstractUser`'dan miras alan custom User modeli şimdiden oluşturun — sonradan değiştirmek çok zor |
| Admin paneli | Sorular ve session'lar yönetilmeli | Django Admin'e `Question`, `InterviewSession`, `EmotionRecord` modellerini kaydedin |

> [!CAUTION]
> **Kritik:** Django projesi başlarken `AUTH_USER_MODEL` ayarını custom user modeline yönlendirin. Sonradan değiştirmek migration cehennemine yol açar.

---

### 2.2 User + Session Based Duygu Kayıtları

**Format:** `[timestamp, "mutlu":0.2, "üzgün":0.6, ...]` + video kaydı

| Olası Pürüz | Açıklama | Çözüm Önerisi |
|---|---|---|
| Yüksek frekanslı veri yazma | Her frame'de DB'ye yazarsanız performans düşer | WebRTC ile gelen frame'leri sunucu tarafında buffer'layın (Redis/in-memory queue) → periyodik DB flush |
| Video kayıt — çift katman | Hem sunucu hem client tarafında kayıt stratejisi | **WebRTC stream:** `aiortc` ile sunucu tarafında kayıt (birincil). **MediaRecorder:** Client-side yedek kayıt (fallback). Format: **WebM (VP9)** |
| Video saklama | Dosya sistemi mi, object storage mı? | Geliştirme: `MEDIA_ROOT` ile local. Prodüksiyon: S3/MinIO. Django `FileField` her ikisini de destekler |
| Veri modeli tasarımı | Emotion verileri nasıl saklanmalı? | Önerilen model yapısı aşağıda |
| Zaman senkronizasyonu | Video timestamp ile emotion timestamp uyuşmazlığı | WebRTC RTP timestamp'larını kullanın — frame ve emotion timestamp'ları doğal olarak senkron olur |
| Django Channels altyapısı | WebRTC signaling ve WebSocket için ASGI gerekir | `daphne` veya `uvicorn` ASGI server kullanın. `ASGI_APPLICATION` ayarını yapılandırın |

**Önerilen DB Modeli:**

```python
class InterviewSession(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    started_at = models.DateTimeField(auto_now_add=True)
    ended_at = models.DateTimeField(null=True, blank=True)
    video_file = models.FileField(upload_to='interviews/videos/', null=True)
    language = models.CharField(max_length=2, choices=[('tr','TR'),('en','EN')])
    status = models.CharField(max_length=20, default='in_progress')  # in_progress, completed, abandoned

class EmotionRecord(models.Model):
    session = models.ForeignKey(InterviewSession, on_delete=models.CASCADE, related_name='emotions')
    timestamp = models.FloatField()  # saniye cinsinden, session başlangıcına göre
    happy = models.FloatField(default=0)
    sad = models.FloatField(default=0)
    angry = models.FloatField(default=0)
    surprised = models.FloatField(default=0)
    fearful = models.FloatField(default=0)
    disgusted = models.FloatField(default=0)
    neutral = models.FloatField(default=0)
    # Alternatif: emotions = models.JSONField()  → daha esnek ama sorgulaması zor
```

> [!NOTE]
> **Trade-off:** `JSONField` ile emotion'ları tek alanda tutmak esnektir ama SQL ile filtreleme/aggregation zorlaşır. Ayrı float alanları query dostu ama model değişikliği gerektirir. **Öneri:** Emotion sayısı sabit kalacaksa ayrı alanlar; değişecekse `JSONField`.

---

## 3. Perception (Model Katmanı)

### 3.1 Model Doğruluğu Artırılması

| Olası Pürüz | Açıklama | Çözüm Önerisi |
|---|---|---|
| Model-Django entegrasyonu | PyTorch/TF modeli Django process'inde çalıştırmak bellek sorunlarına yol açar | **Öneri:** Modeli ayrı bir microservice olarak çalıştırın (FastAPI + model server). `aiortc` → frame alır → model servisine iletir |
| GPU vs CPU inference | Sunucuda GPU yoksa inference yavaş olur | ONNX Runtime ile CPU optimizasyonu veya TensorRT ile GPU hızlandırma. Client-side inference (TF.js/ONNX.js) alternatif ama model boyutuna bağlı |
| Model güncelleme | Yeni model eğitildiğinde deployment nasıl olacak? | Model dosyasını versiyonlayın (`model_v1.onnx`, `model_v2.onnx`). Config ile hangi versiyonun aktif olduğunu belirleyin |
| Real-time vs batch inference | Her frame'i anında mı analiz edeceksiniz? | WebRTC ile gelen frame'lerden her 2-3 FPS'te birini model'e gönderin. `aiortc` `VideoStreamTrack` üzerinde frame skip mantığı uygulayın |

> [!TIP]
> **Avantajlı Alternatif — Client-Side Inference:** Eğer model yeterince küçükse (~5-20MB), **TensorFlow.js** veya **ONNX.js** ile emotion detection'ı doğrudan tarayıcıda çalıştırabilirsiniz. Avantajları:
> - Sunucu yükü sıfır
> - Latency çok düşük
> - Video verisi sunucuya gönderilmeden işlenir (gizlilik)
> 
> Dezavantaj: Düşük donanımlı client'larda performans sorunu.

---

### 3.2 Time-Based Solution'ların Tekrardan Eklenmesi

| Olası Pürüz | Açıklama | Çözüm Önerisi |
|---|---|---|
| Temporal smoothing | Anlık tahminler gürültülü olur (frame'den frame'e zıplama) | Sliding window averaging (son 5-10 frame'in ortalaması) veya exponential moving average (EMA) |
| Temporal model entegrasyonu | LSTM/Transformer gibi modeller session-level state tutar | Session başına bir model state tutulmalı — memory management önemli. Her session kapanınca state temizlenmeli |
| Senkronizasyon | Time-based prediction'lar ile soru geçişleri eşleşmeli | Her soru geçişinde bir "marker" timestamp kaydedin → analiz aşamasında soru bazlı emotion grafiği çıkarılabilir |

---

## 4. Genel Mimari Önerileri

```
┌─────────────────────────────────────────────────────────────┐
│                      BROWSER (Client)                       │
│  ┌──────────┐  ┌──────────┐  ┌─────────────────────────┐   │
│  │  Login   │  │ Mülakat  │  │  getUserMedia            │   │
│  │  Formu   │  │ UI/Sorular│ │  + RTCPeerConnection    │   │
│  └────┬─────┘  └────┬─────┘  │  + MediaRecorder (yedek)│   │
│       │              │        └────────┬────────────────┘   │
│       │              │                 │                    │
│       │              │         WebRTC Media Stream           │
│       │              │         + Signaling (WebSocket)       │
└───────┼──────────────┼─────────────────┼────────────────────┘
        │              │                 │
        ▼              ▼                 ▼
┌─────────────────────────────────────────────────────────────┐
│              DJANGO BACKEND (ASGI — Daphne/Uvicorn)         │
│  ┌─────────┐  ┌────────────┐  ┌──────────────────────────┐  │
│  │  Auth   │  │  Session   │  │  Django Channels         │  │
│  │  Views  │  │  Manager   │  │  (WebSocket Signaling)   │  │
│  └─────────┘  └──────┬─────┘  └────────────┬─────────────┘  │
│                      │                     │                │
│               ┌──────▼──────┐    ┌─────────▼────────────┐   │
│               │  PostgreSQL │    │  aiortc (Python peer) │  │
│               │  (DB)       │    │  Video frame alımı    │  │
│               └─────────────┘    └─────────┬────────────┘   │
│                                            │                │
│                               ┌────────────▼────────────┐   │
│                               │  STUN/TURN (coturn)     │   │
│                               │  NAT traversal          │   │
│                               └─────────────────────────┘   │
└────────────────────────────────────────┬────────────────────┘
                                         │ Frame → HTTP/gRPC
                                         ▼
                               ┌────────────────────┐
                               │  Model Service     │
                               │  (FastAPI)         │
                               │  Emotion Inference │
                               └────────────────────┘
```

> [!WARNING]
> **SQLite vs PostgreSQL:** Django varsayılan olarak SQLite kullanır. Geliştirme için yeterli ama concurrent write sorunları yaşatır. Eğer birden fazla kullanıcı aynı anda mülakat yapacaksa **PostgreSQL** kullanın. Proje başından itibaren PostgreSQL ayarlarsanız migration sorunları yaşamazsınız.

---

## 5. Öncelik Sıralaması Önerisi

| Sıra | İş | Neden? |
|------|-----|--------|
| 1 | Custom User Model + DB setup (PostgreSQL) | Sonradan değiştirmek çok zor |
| 2 | Login ekranı (frontend + backend) | Temel altyapı |
| 3 | Django Channels + ASGI kurulumu | WebRTC signaling altyapısı, erken kurulmalı |
| 4 | Question modeli + dummy data fixture | Mülakatın iskeletini oluşturur |
| 5 | Mülakat sayfası + başlat/bitir butonları | Akışın çekirdeği |
| 6 | WebRTC entegrasyonu (signaling + `aiortc` peer) | Real-time video akışı |
| 7 | Kamera erişimi + MediaRecorder (yedek kayıt) | Fallback video kaydı |
| 8 | Session + EmotionRecord modelleri | Backend veri katmanı |
| 9 | Model servisi entegrasyonu | Perception katmanı |
| 10 | Time-based smoothing | Model kalitesi |
| 11 | TR/EN çoklu dil desteği | Polish aşaması |

---

## 6. WebRTC Gerekli Paketler Özeti

| Paket | Amaç | Kurulum |
|---|---|---|
| `channels` | Django WebSocket/ASGI desteği | `pip install channels` |
| `channels-redis` | Channel layer backend (prod) | `pip install channels-redis` |
| `daphne` | ASGI HTTP/WebSocket server | `pip install daphne` |
| `aiortc` | Python tarafında WebRTC peer | `pip install aiortc` |
| `adapter.js` | Tarayıcı WebRTC uyumluluk shim | CDN veya npm |
| `coturn` | TURN/STUN sunucusu (prod) | System package / Docker |


## EK
```
def add_data_to_session(request, session_id, conf_array):
    session = Session.objects.get(id=session_id)

    # 🔥 kritik kontrol
    if session.user != request.user:
        raise Exception("Bu session sana ait değil")

    data = Data.objects.create(
        session=session,
        timestamp=timezone.now(),
        conf=conf_array
    )
```
    return data
