first (part 2: other directories)
Deploy Iddaai Backend / build-and-deploy (push) Failing after 18s

This commit is contained in:
2026-04-16 15:11:25 +03:00
parent 7814e0bc6b
commit 2f0b85a0c7
203 changed files with 59989 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
# Derin Teknik Düzeltmeler ve Analiz Günlüğü
**Dosya:** `02_deep_fixes_log.md`
**Amaç:** Son geliştirme döngüsünde çözülen karmaşık sorunların teknik detaylarını belgelemek.
---
## 1. Live Match Synchronization & DB Safety
### Sorun Tanımı
Canlı maçlar (Live Matches), ana `matches` tablosuna henüz işlenmemiş olabiliyor. Ancak sistem bunları analiz etmeye çalıştığında:
1. `match_player_participation` tablosuna oyuncu eklemeye çalışıyor -> `matches` tablosunda ID olmadığı için **Foreign Key Constraint Error** alıyordu.
2. Veri tabana yazılamadığı için Python scripti "Lineup not found" hatasıyla patlıyordu.
### Uygulanan Çözüm
**Dual-Persistence Strategy (Çift Yazma Stratejisi):**
- **Logic:** `FeederPersistenceService.saveLineups` ve `saveOdds` metodları artık önce `matches` tablosunda `matchId` var mı diye kontrol ediyor.
- **Varsa:** Hem ilişkisel tablolara (`match_player_participation`) hem de `live_matches` JSON kolonuna yazıyor.
- **Yoksa (Sadece Canlı):** İlişkisel tabloları tamamen pas geçiyor (SKIP), veriyi sadece `live_matches.lineups` ve `live_matches.odds` JSON kolonlarına strüktüre edilmiş olarak yazıyor.
**Fallback Mechanism (Python & NestJS):**
- **NestJS:** `getPlayerCount` metodu önce ilişkisel tabloyu sayıyor. Sayı 0 ise `live_matches` JSON'ını parse edip oradaki oyuncu sayısını dönüyor.
- **Python:** `_run_model` içinde önce SQL sorgusu ile kadro çekmeye çalışıyor. Liste boşsa, `live_matches` tablosundaki JSON kolonunu çekip manuel parse ediyor.
---
## 2. Model Score & Context Mapping (Kritik)
### Sorun Tanımı
Kullanıcı, modelin skor tahminlerinin ve maç sonucu (1/X/2) tercihlerinin tutarsız olduğundan şikayetçiydi ("Home win diyor ama skor 0-1" gibi).
### Tespit Edilen Kök Nedenler
1. **HT/FT Sıralaması:** Model eğitimi `ht*3 + ft` (0=X, 1=1, 2=2) mantığıyla yapılmıştı. Ancak tahmin scripti etiketleri `1/1, 1/X...` gibi rastgele bir sırayla diziyordu. Bu yüzden `1/1` tahmini `X/X` gibi görünüyordu.
2. **Beraberlik (Draw) Körlüğü:** Data Feeder, beraberlik oranını `X` etiketiyle kaydediyordu (Mackolik verisi). Python scripti ise sadece `0` etiketini "Beraberlik" olarak kabul ediyordu. Sonuç olarak model "Beraberlik Oranı: 0.0" görüyordu (yani oran yok). Bu, modelin maçın dengesini yanlış anlamasına neden oluyordu.
3. **Away Bias (Deplasman Yanlılığı):** Backtest verileri, modelin sistematik olarak deplasman takımına fazla gol yazdığını (%15-20 fazla) gösterdi.
### Uygulanan Çözüm
1. **Etiket Düzeltmesi:** `htft_labels` listesi `[X/X, X/1, X/2, 1/X, 1/1, 1/2, 2/X, 2/1, 2/2]` olarak, eğitim verisiyle %100 uyumlu hale getirildi.
2. **Oran Normalizasyonu:** Python scripti artık hem `0` hem `X` etiketlerini beraberlik oranı olarak kabul ediyor.
3. **Skor Kalibrasyonu:** Backtest sonrası optimizasyon katsayıları eklendi.
- `HOME_GOAL_SCALE = 1.00`
- `AWAY_GOAL_SCALE = 0.85`
---
## 3. Akıllı Kupon Servisi (Smart Coupon)
### Yapı
NestJS tarafında `SmartCouponService` ve Python tarafında `smart_coupon_service.py` (CLI) işbirliği ile çalışır.
### Akış
1. **Request:** POST `/api/coupon/analyze-match` { matchId }
2. **Pre-Check 1 (Kadro):** DB'de kadro var mı?
- Yoksa -> `FeederService.refreshMatch(lineups)` -> Tekrar kontrol -> Hâlâ yoksa Hata Fırlat ("Yetersiz Veri").
3. **Pre-Check 2 (Oran):** DB'de oran var mı?
- Yoksa -> `FeederService.refreshMatch(odds)` -> Log bas (Engelleyici değil).
4. **Prediction:** Python scripti çalıştırılır (`--analyze --json`).
- Script: DB veya JSON Fallback'ten veriyi okur.
- Script: Modeli çalıştırır, kalibrasyon katsayılarını uygular.
- Script: JSON döner.
5. **Response:** NestJS JSON'ı parse edip kullanıcıya döner.
---
## Gelecek İçin Notlar
- **Database:** `live_matches` tablosundaki JSON kolonları artık kritik öneme sahip. Bunların şeması `Prisma` tarafında `Json` olarak tanımlı ama iç yapısı kod içinde (`FeederPersistence`) belirleniyor. Yapıyı değiştirirken dikkatli olunmalı.
- **Model:** V17 modeli şu an stabil. V18'e geçilirse `player_model_v17.py` değiştirilmeli ve `smart_coupon_service.py` içindeki scale faktörleri sıfırlanıp tekrar backtest yapılmalı.