From b66c720b32beb6363b268f4bc5c8b2da173a71ce Mon Sep 17 00:00:00 2001 From: bilalgursen Date: Wed, 23 Sep 2026 00:24:42 +0300 Subject: [PATCH] =?UTF-8?q?fix(veri):=20yanl=C4=B1=C5=9F=20DEVLET=20etiket?= =?UTF-8?q?i=20kal=C4=B1c=C4=B1=20onar=C4=B1m=20+=20baz=20ad=20=C3=A7eli?= =?UTF-8?q?=C5=9Fki=20denetimi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 3 kurum (Kıbrıs Amerikan, Uluslararası Balkan, Uluslararası Saraybosna) kaynak CSV'den `unitur='DEVLET'` geliyordu. `db.ts:79-82` filtreleri yüzünden "devlet üniversitesi istiyorum" diyen öğrencinin aday havuzuna 27 KKTC/yurt dışı programı sızıyor, "vakıf" diyende aynı 27'si eksik kalıyordu. Kullanıcının fark edemeyeceği bir doğruluk hatasıydı. unitur-onar.ts — Aşama D (KURUM_DUZELT): A/B/C yalnız `unitur IS NULL` satırlara dokunuyordu; bu satırlar NULL değil, DOLU ve YANLIŞ. D koşulsuz çalışır ve Aşama A'dan ÖNCE koşar ki yanlış değer başka satırlara kopyalanmasın. İdempotent (2. koşu: 0/0). Her düzeltmenin kanıtı kurumun YÖK Atlas'taki kendi parantezli ikinci yazımı: "(ÜSKÜP-MAKEDONYA)" → YURTDISI KAMU, "(KKTC-LEFKOŞA)" → KKTC. refresh.ts:519 `uniturSec` DB'deki değeri kopyaladığı için düzeltme buraya yazılınca her tazelemede kalıcı olur. veri-kalite.ts — iki yeni ZORUNLU kontrol (dosyanın sonuna): 13. "baz ad başına tek unitur" — kök neden buydu: unitur-onar.ts'in eski çelişki koruması GROUP BY universite (TAM ad) ile çalıştığı için aynı kurumun iki yazımı arasındaki DEVLET↔YURTDISI KAMU çelişkisini hiç görmüyordu. 14. "DEVLET etiketi Türkiye ili ile destekli" — 13'ün kör noktası: Kıbrıs Amerikan'ın İKİ yazımı da yanlış etiketli olduğu için ortada çelişki yok. 13 tek başına 100 satırın yalnız 58'ini koruyordu. Şartnamenin ötesinde, gerekçesi raporun §4'ünde. Doğrulama: düzeltme öncesi DB'de iki kontrol de KALDI (çıkış kodu 1); sonrasında 0 ZORUNLU kaldı, 3 uyarı (üçü de önceden vardı). Tek satır bilerek bozulduğunda ikisi de yakaladı. tsc + eslint temiz. Veri (126 satır) ayrı commit'te. Rapor: docs/gece-vardiyasi/2026-09-22/ 09-p2-unitur.md Co-Authored-By: Claude Opus 5 --- .../gece-vardiyasi/2026-09-22/09-p2-unitur.md | 147 ++++++++++++++++++ scripts/unitur-onar.ts | 69 +++++++- scripts/veri-kalite.ts | 41 +++++ 3 files changed, 254 insertions(+), 3 deletions(-) create mode 100644 docs/gece-vardiyasi/2026-09-22/09-p2-unitur.md diff --git a/docs/gece-vardiyasi/2026-09-22/09-p2-unitur.md b/docs/gece-vardiyasi/2026-09-22/09-p2-unitur.md new file mode 100644 index 0000000..21353b9 --- /dev/null +++ b/docs/gece-vardiyasi/2026-09-22/09-p2-unitur.md @@ -0,0 +1,147 @@ +# 09 · P2 — Sihirbaz havuzundaki sessiz sızıntı kapatıldı + +Dal: `gece/0922-unitur-kacagi` · Şartname: `06-ceo-kararlari.md` §3 P2 · Teşhis: `05-urap.md` §6 +Commit düzeni: (1) kod + bu rapor, (2) `data/yokatlas.db` binary'si. + +## Özet (5 madde) + +1. **Sızıntı kapandı.** "Devlet üniversitesi istiyorum" diyen öğrencinin aday havuzuna giren 27 KKTC/yurt dışı programı artık devlet filtresinde **0**, vakıf filtresinde **27**. Ölçüldü, çıkarım değil. +2. **126 satır düzeldi**, tam şartnamedeki sayı: 100 `unitur` + 26 `il`. Değişen kolon **yalnız bu ikisi**; `programs` tablosunun 45 kolonu tek tek karşılaştırıldı. +3. **2021–2026 puan/sıra/kontenjan/yerleşen kolonlarında değişen satır: 0.** 34 yıl kolonu, 21.493 satır, öncesi/sonrası birebir. `programs_arsiv`, `netler`, `veri_meta` de dokunulmadı. +4. **Kök neden kapandı, tekrarı denetleniyor.** `unitur-onar.ts`'e NULL koşulundan bağımsız **Aşama D** (`KURUM_DUZELT`) eklendi; `veri-kalite.ts`'e iki yeni ZORUNLU kontrol geldi. Kontroller düzeltme öncesi DB'de **KALDI**, sonrasında **GEÇTİ** verdi; ayrıca tek satır bilerek bozulduğunda ikisi de yakaladı. +5. **Bir kapsam genişletmesi var, bilerek:** şartnamedeki "baz ad başına tek `unitur`" kontrolü Kıbrıs Amerikan'ı (100 satırın 42'si) **yakalayamıyor** — o kurumun iki yazımı da yanlış etiketli olduğu için ortada çelişki yok. Bu yüzden ikinci bir 6 satırlık kontrol eklendi (§4). Onaysız bırakılırsa "kök neden kapandı" cümlesi yalan olurdu. + +## 1. Ne değişti + +| Dosya | Değişiklik | +|---|---| +| `scripts/unitur-onar.ts` | `KURUM_DUZELT` haritası + **Aşama D** (Aşama A'dan önce koşar). `KKTC_ILLERI` dışa aktarıldı (tek kaynak). Başlık yorumu "yalnızca NULL satırlara dokunur" → A/B/C NULL, D koşulsuz. | +| `scripts/veri-kalite.ts` | Kontrol 13 (baz ad başına tek `unitur`) + kontrol 14 (DEVLET etiketi Türkiye ili ile destekli). Dosyanın **en sonuna** eklendi; mevcut satırlar yeniden düzenlenmedi (P3 ile çakışmayı küçültmek için). | +| `data/yokatlas.db` | 126 satır (ayrı commit). | + +`KURUM_DUZELT` içeriği ve her satırın kanıtı — kurumun YÖK Atlas'taki **kendi parantezli ikinci yazımı**: + +| Ad (DB'de) | `unitur` önce → sonra | `il` | +|---|---|---| +| KIBRIS AMERİKAN ÜNİVERSİTESİ | DEVLET → **KKTC** (36) | değişmedi | +| KIBRIS AMERİKAN ÜNİVERSİTESİ (KKTC-LEFKOŞA) | DEVLET → **KKTC** (6) | değişmedi | +| ULUSLARARASI BALKAN ÜNİVERSİTESİ | DEVLET → **YURTDISI KAMU** (34) | SARAYBOSNA - BOSNA - HERSEK → **ÜSKÜP-MAKEDONYA** (26) | +| ULUSLARARASI SARAYBOSNA ÜNİVERSİTESİ | DEVLET → **YURTDISI KAMU** (24) | değişmedi (kurum gerçekten Saraybosna'da) | + +`ÜSKÜP-MAKEDONYA` yazımı uydurma değil: kurumun kendi adındaki parantezli ek, DB'deki diğer yurt dışı illerin biçimiyle (`BİŞKEK-KIRGIZİSTAN`, `TÜRKİSTAN-KAZAKİSTAN`, `BAKÜ-AZERBAYCAN`) aynı. + +`unitur` dağılımı: `DEVLET 12142→12042`, `KKTC 1145→1187`, `YURTDISI KAMU 99→157`. Toplam sabit. + +## 2. Fark raporu (öncesi/sonrası) + +Yöntem: düzeltme öncesi DB `sqlite3 .backup` ile scratchpad'e alındı, sonra `ATTACH` ile kolon kolon karşılaştırıldı. + +``` +programs: önce 21493 → sonra 21493 (satır kaybı/ekleme yok) +programs_arsiv: önce 3735 → sonra 3735 (unitur/il değişen satır: 0) +netler: önce 62975 → sonra 62975 +veri_meta: önce 37 → sonra 37 + +programs, 45 kolon tarandı — DEĞİŞEN: unitur=100, il=26 +yıl kolonları (34 kolon: sıra/puan/kontenjan/yerleşen + 2025 ek kontenjanları, +2021-2026) değişen satır: 0 +``` + +`pnpm veri-kalite --onceki` kendi bağımsız kontrolüyle aynı sonucu verdi: + +``` +GEÇTİ önceki DB: satır kaybı — 0 +GEÇTİ önceki DB: geçmiş yıllar korunmuş — 20 kolonda dolu değeri değişen/silinen satır: 0 + 2025 sağlaması (count | Σsira | Σpuan | Σkontenjan | Σyerlesen) + önceki DB : 21493 | 11099796362 | 5433214.11872 | 756453 | 740586 + şimdi, önceki DB'deki aynı id'ler : 21493 | 11099796362 | 5433214.11872 | 756453 | 740586 +``` + +## 3. Kabul kriterleri — tek tek + +**(1) `pnpm veri-kalite` temiz ve yeni kontrol çelişkiyi gerçekten yakalıyor.** ✅ + +``` +pnpm veri-kalite --yil 2026 --db data/yokatlas.db --onceki <öncesi> +… +GEÇTİ baz ad başına tek unitur — aynı kurumun iki yazımı devlet/vakıf filtresini bölmüyor +bilgi baz ad unitur farkı (devlet/vakıf ayrımını bölmeyen) — HOCA AHMET YESEVİ …/YURTDISI KAMU,YURTDISI VAKIF/17 ; KIRGIZİSTAN-TÜRKİYE MANAS …/29 +GEÇTİ DEVLET etiketi Türkiye ili ile destekli — DEVLET etiketli her kurumun en az bir programı Türkiye ilinde +Sonuç: 0 ZORUNLU kontrol kaldı, 3 uyarı. (çıkış kodu 0) +``` + +3 uyarı **düzeltme öncesiyle birebir aynı**, hiçbiri bu işten doğmadı: `il boş` 110 satır (kapsam dışı), `kimlik ATLANDI` (`data/ham` repoda yok), `yetim netler` 2066. + +Yakalama iki yoldan kanıtlandı: + +- *Düzeltme öncesi DB'de* (gerçek kusur): `KALDI baz ad başına tek unitur — … ULUSLARARASI BALKAN ÜNİVERSİTESİ/DEVLET,YURTDISI KAMU/48 ; ULUSLARARASI SARAYBOSNA ÜNİVERSİTESİ/DEVLET,YURTDISI KAMU/38` ve `KALDI DEVLET etiketi Türkiye ili ile destekli — KIBRIS AMERİKAN ÜNİVERSİTESİ/LEFKOŞA/36 ; … (KKTC-LEFKOŞA)/KIBRIS/6 ; ULUSLARARASI BALKAN …/34 ; ULUSLARARASI SARAYBOSNA …/24` → `Sonuç: 2 ZORUNLU kontrol kaldı`, çıkış kodu 1. +- *Bilerek bozma* (düzeltilmiş DB'nin atılabilir kopyasında **tek** satır `DEVLET`'e döndürüldü): ikisi de yakaladı → `Sonuç: 2 ZORUNLU kontrol kaldı`. Kopya silindi; gerçek DB'ye dokunulmadı. + +**(2) `db.ts` devlet filtresi 27 programı artık döndürmüyor, vakıf filtresi döndürüyor.** ✅ Ölçüldü. + +`src/lib/db.ts:79-82` filtreleri + `EFEKTIF_SIRA = COALESCE(sira2026, sira2025)` (havuza ancak efektif sırası olan satır girer): + +```sql +-- devlet: AND unitur = 'DEVLET' +-- vakıf : AND unitur IS NOT NULL AND unitur <> 'DEVLET' +SELECT COUNT(*) FROM programs + WHERE universite IN ('KIBRIS AMERİKAN ÜNİVERSİTESİ', + 'KIBRIS AMERİKAN ÜNİVERSİTESİ (KKTC-LEFKOŞA)', + 'ULUSLARARASI BALKAN ÜNİVERSİTESİ', + 'ULUSLARARASI SARAYBOSNA ÜNİVERSİTESİ') + AND AND COALESCE(sira2026, sira2025) IS NOT NULL; +``` + +| | önce | sonra | +|---|---|---| +| devlet havuzu | **27** | **0** | +| vakıf havuzu | **0** | **27** | + +**(3) `/universiteler` hub'ında üç kurum "Devlet" listesinden çıkıyor.** ✅ (kısmen koddan çıkarım — tarayıcı açılmadı) + +`getAllUniversiteler` (`src/lib/katalog.ts:268-271`) `MAX(unitur) GROUP BY universite` okur; `universite-listesi.tsx:44-47` `u.unitur === "DEVLET"` olanı devlet listesine, `VAKIF`/`VAKIF MYO` olanı vakfa, kalanı `digerUniler`'e koyar. Aynı SQL DB'de koşturuldu: + +``` +önce : KIBRIS AMERİKAN …|DEVLET … (KKTC-LEFKOŞA)|DEVLET ULUSLARARASI BALKAN …|DEVLET ULUSLARARASI SARAYBOSNA …|DEVLET +sonra: KIBRIS AMERİKAN …|KKTC … (KKTC-LEFKOŞA)|KKTC ULUSLARARASI BALKAN …|YURTDISI KAMU ULUSLARARASI SARAYBOSNA …|YURTDISI KAMU +Devlet listesindeki kurum sayısı: 170 → 166 +``` + +166 = 170 − **4 yazım** (3 kurum; Kıbrıs Amerikan'ın iki yazımı bugün iki ayrı slug — `kategoriBul` baz ad birleştirmesi kapsam dışı, BACKLOG #10). +Dördü de `KKTC`/`YURTDISI KAMU` olduğu için vakıf listesine **girmiyor**, "Diğer" grubuna düşüyor — hub'da sayı dengesi bozulmuyor. + +**(4) 2021–2026 puan/sıra kolonlarında hiçbir değişiklik yok.** ✅ §2. + +Ayrıca: `npx next typegen && npx tsc --noEmit` temiz, `npx eslint scripts/unitur-onar.ts scripts/veri-kalite.ts` temiz. Betik **idempotent**: ikinci koşu `unitur 0 satır, il 0 satır`. + +## 4. Kapsam genişletmesi ve gerekçesi (şeffaflık) + +Şartname `veri-kalite.ts`'e tek kontrol istiyordu: "baz ad başına tek `unitur`" (~8 satır). Uygulandı — ama düzeltme öncesi DB'de koşturunca **Kıbrıs Amerikan'ı hiç görmüyor**: o kurumun her iki yazımı da `DEVLET` etiketli, yani baz ad içinde çelişki yok. Bu kontrol tek başına 100 satırın yalnız 58'ini koruyor; 42'si (öğrenciye sızan 27 programın 12'si) denetimsiz kalıyordu. + +Bu yüzden **kontrol 14** eklendi (6 satır): *DEVLET etiketli bir kurumun en az bir programı Türkiye ilinde olmalı.* Türkiye illerinde tire geçmiyor, yurt dışı iller tireli, KKTC illeri `KKTC_ILLERI`'nde; hiç ili olmayan kurumlar (MSB/İçişleri) hariç tutuldu. Düzeltme öncesi DB'de yanlış 4 yazımın **dördünü birden**, sıfır yanlış pozitifle yakalıyor. + +Kapsam **dışında bırakılanlara dokunulmadı**: `il` boş 19 satır, burs/dil ekli adlar (B4), `kategoriBul` düzeltmesi. + +## 5. Yeni bulgular (bu geceye alınmadı — backlog önerisi) + +| # | Bulgu | Sorgu / kanıt | Önem | +|---|---|---|---| +| A | **`programs_arsiv`'de aynı yanlış etiket sürüyor**: 31 satır ULUSLARARASI BALKAN + 30 satır ULUSLARARASI SARAYBOSNA `unitur='DEVLET'`, 24 satırda il Saraybosna. CEO §6'nın veri mühendisine sorduğu soru buydu. | `SELECT universite, unitur, il, COUNT(*) FROM programs_arsiv WHERE universite LIKE '%BALKAN%' OR universite LIKE '%SARAYBOSNA%' GROUP BY 1,2,3` | **Düşük — kullanıcıya yansımıyor.** `src/` tarafında `programs_arsiv`'i okuyan hiçbir SQL yok (tablo yalnızca `src/lib/kapanan-sluglar.ts`'te bir yorumda geçer); `db.ts` havuzu yalnız `programs`'tan besleniyor. Yine de bir sonraki denetimde yeniden "bulgu" olarak çıkacak; `KURUM_DUZELT`'i arşive de uygulamak tek satırlık iş. | +| B | **`data/yokatlas.db-shm` ve `-wal` git'te izleniyor** (32 KB + 0 bayt). Bunlar SQLite'ın geçici dosyaları: DB her açılıp kapandığında oluşup siliniyor, dolayısıyla `git status` kendiliğinden kirleniyor. | `git ls-files data/` | **Düşük.** Uygulamanın `better-sqlite3` ile `readonly` açılışı eski `-shm` yanındayken de sorunsuz çalışıyor (ölçtüm); ama `sqlite3 -readonly` CLI'ı bu durumda "unable to open database file (14)" veriyor — denetim sorgusu koşarken yanıltır. Öneri: `git rm --cached` + `.gitignore`. **Bu commit'e dahil etmedim**: `.gitignore`'da Bilal'in commit'lenmemiş değişikliği var. | +| C | Baz ad çelişkisi kalan 2 kurum: Manas ve Hoca Ahmet Yesevi (`YURTDISI KAMU` ↔ `YURTDISI VAKIF`). | kontrol 13'ün `bilgi` satırı | **Yok.** İkisi de `<> 'DEVLET'` olduğu için devlet/vakıf filtresini bölmüyor; kurum gerçekten ortak. Bilerek ZORUNLU yapılmadı, `bilgi` olarak görünür duruyor. | + +## 6. Kullanıcıya yansıyan etki (düzelen) + +| Yüzey | Önce | Sonra | +|---|---|---| +| Sihirbaz → "Devlet üniversitesi" | Havuzda 27 KKTC/Bosna/Makedonya programı | 0 | +| Sihirbaz → "Vakıf" | Aynı 27 program eksik | 27'si havuzda | +| `/universiteler` hub'ı | 3 kurum (4 yazım) "Devlet üniversiteleri" listesinde | "Diğer" grubunda | +| `/universite/uluslararasi-balkan-universitesi` | "Tür: Devlet", şehir Saraybosna (Bosna) | Tür: Yurt dışı kamu, şehir Üsküp-Makedonya | +| Tadımlık satırı (`tadimlik-satiri.tsx:37`) | Aynı semantik, aynı sızıntı | Düzeldi (aynı `unitur` alanını okur) | + +## Bilal'den istenen + +1. **Dalı incele ve merge kararını ver** (`gece/0922-unitur-kacagi`, 2 commit: kod+rapor, veri). Fark raporu §2'de ve veri commit'inin mesajında; geri dönüş için `veri-2025-son` etiketi duruyor. **~10 dk.** +2. **Elindeki ham CSV'de tek `grep`** — `data/ham/` repoda yok: bu üç kurum kaynak dosyada gerçekten `DEVLET` mi yazıyor? Doğrularsa teşhis kapanır, `ingest.ts`'e aynı harita eklenir. **~5 dk.** +3. **Karar:** §5-A (arşiv tablosundaki aynı yanlış etiket) ve §5-B (`-shm`/`-wal` git'ten çıkarılsın mı) backlog'a mı, yoksa bu dala ek commit mi? İkisi de kullanıcıya yansımıyor. **~2 dk.** diff --git a/scripts/unitur-onar.ts b/scripts/unitur-onar.ts index f192d52..11dbfff 100644 --- a/scripts/unitur-onar.ts +++ b/scripts/unitur-onar.ts @@ -3,8 +3,9 @@ * * YÖK Atlas API'si bazı kayıtlarda universiteTuru döndürmüyor; DB'de ~2.081 * satır unitur=NULL kalıyor ve bunlar hem devlet/vakıf filtrelerinden hem - * sihirbaz sayaçlarından sessizce düşüyor. Onarım üç aşamalı: + * sihirbaz sayaçlarından sessizce düşüyor. Onarım dört aşamalı: * + * D) Kaynak veride DOLU ama YANLIŞ olan kurum alanları (KURUM_DUZELT) * A) Aynı üniversitenin dolu satırından kopyala (Nişantaşı, Beykent…) * B) Kalanlarda il KKTC yerleşimiyse unitur='KKTC' * C) Küçük manuel eşleme (Cerrahpaşa, MSB/İçişleri, Alanya, Manas…) @@ -13,7 +14,8 @@ * duplikat adlar normalize edilir; refresh.ts insert yolunda da aynı * normalizasyon uygulanır ki duplikat bir daha oluşmasın. * - * İdempotenttir; yalnızca NULL satırlara dokunur. + * İdempotenttir. A/B/C yalnızca NULL satırlara dokunur; D (KURUM_DUZELT) + * NULL koşulundan bağımsızdır — düzelttiği alanlar dolu ama yanlış. * * Kullanım: npx tsx scripts/unitur-onar.ts (dev sunucu KAPALIYKEN — * uygulama DB'yi readonly açar ama WAL checkpoint için tek yazar olmalı) @@ -26,7 +28,8 @@ export function uniAdiNormalize(ad: string): string { return ad.replace(/\s*-\s*/g, "-").replace(/\s{2,}/g, " ").trim(); } -const KKTC_ILLERI = [ +/** KKTC il değerleri — tek kaynak; veri-kalite.ts de bunu okur. */ +export const KKTC_ILLERI = [ "GİRNE", "LEFKOŞA", "GAZİMAĞUSA", @@ -51,6 +54,44 @@ const MANUEL_IL: Record = { "İSTANBUL ÜNİVERSİTESİ-CERRAHPAŞA": "İSTANBUL", }; +/** + * Aşama D — kaynak veride DOLU ama YANLIŞ olan kurum alanları. + * + * MANUEL_UNITUR'dan farkı: burada değer NULL değil, yanlış. Bu yüzden bu + * harita NULL koşulu OLMADAN çalışır. Her satırın kanıtı kurumun YÖK Atlas'ta + * geçen parantezli ikinci yazımıdır (aynı kurum, doğru etiketli): + * + * "ULUSLARARASI BALKAN ÜNİVERSİTESİ (ÜSKÜP-MAKEDONYA)" → YURTDISI KAMU + * "ULUSLARARASI SARAYBOSNA ÜNİVERSİTESİ (SARAYBOSNA-…)" → YURTDISI KAMU + * "KIBRIS AMERİKAN ÜNİVERSİTESİ (KKTC-LEFKOŞA)" → ili KKTC + * + * Yanlış etiket ilk CSV ingest'inden (scripts/ingest.ts) geliyor; refresh.ts + * `uniturSec` DB'deki değeri sadakatle kopyaladığı için her tazelemede + * yeniden üretiliyordu. Düzeltme buraya yazılınca kalıcı olur. + * + * Etki: `src/lib/db.ts` devlet filtresi (`unitur = 'DEVLET'`) bu kurumların + * programlarını sihirbaz havuzuna alıyordu; vakıf filtresi (`unitur <> + * 'DEVLET'`) ise dışarıda bırakıyordu. + * + * `il` düzeltmesi yalnız YANLIŞ yazılmış değeri hedefler (yanlis → dogru); + * boş il'ler kapsam dışı — eksik, eksik olarak görünmeli. + */ +const KURUM_DUZELT: Record< + string, + { unitur?: string; il?: { yanlis: string; dogru: string } } +> = { + "KIBRIS AMERİKAN ÜNİVERSİTESİ": { unitur: "KKTC" }, + "KIBRIS AMERİKAN ÜNİVERSİTESİ (KKTC-LEFKOŞA)": { unitur: "KKTC" }, + "ULUSLARARASI BALKAN ÜNİVERSİTESİ": { + unitur: "YURTDISI KAMU", + // Kurum Üsküp'te; 26 satırda ili Saraybosna yazıyor. Doğru değerin yazımı + // DB'deki diğer yurt dışı illerin biçimini izler (BİŞKEK-KIRGIZİSTAN…) + // ve kurumun kendi parantezli adından alınmıştır. + il: { yanlis: "SARAYBOSNA - BOSNA - HERSEK", dogru: "ÜSKÜP-MAKEDONYA" }, + }, + "ULUSLARARASI SARAYBOSNA ÜNİVERSİTESİ": { unitur: "YURTDISI KAMU" }, +}; + export function uniturOnar(db: Database.Database): void { // 0) Üniversite adı normalizasyonu (Cerrahpaşa duplikatını birleştirir) const adlar = db @@ -69,6 +110,28 @@ export function uniturOnar(db: Database.Database): void { } if (duzeltilen > 0) console.log(`Ad normalizasyonu: ${duzeltilen} kurum`); + // D) Dolu ama yanlış kurum alanları. A'dan ÖNCE koşar: yanlış değer başka + // satırlara kopyalanmadan düzelsin. NULL koşulu yok; değer zaten dolu. + const dUnitur = db.prepare( + "UPDATE programs SET unitur = ? WHERE universite = ? AND unitur IS NOT ?" + ); + const dIl = db.prepare( + "UPDATE programs SET il = ? WHERE universite = ? AND il = ?" + ); + let dUniturToplam = 0; + let dIlToplam = 0; + for (const [ad, duzelt] of Object.entries(KURUM_DUZELT)) { + if (duzelt.unitur) { + dUniturToplam += dUnitur.run(duzelt.unitur, ad, duzelt.unitur).changes; + } + if (duzelt.il) { + dIlToplam += dIl.run(duzelt.il.dogru, ad, duzelt.il.yanlis).changes; + } + } + console.log( + `Aşama D (yanlış kurum alanı): unitur ${dUniturToplam} satır, il ${dIlToplam} satır` + ); + // A) Aynı üniversitenin dolu satırından kopyala. Birden fazla FARKLI dolu // değer varsa sessizce seçim yapma — kaynak veride tutarsızlık demektir. const celiskiler = db diff --git a/scripts/veri-kalite.ts b/scripts/veri-kalite.ts index 862e848..e011e4a 100644 --- a/scripts/veri-kalite.ts +++ b/scripts/veri-kalite.ts @@ -21,6 +21,7 @@ import { EFEKTIF_SIRA_SQL, ILK_YIL } from "../src/lib/veri-yillari"; import { EFEKTIF_INDEKS, efektifSorguPlani } from "./db-index"; import { kimlikFarklari, onarimKayitlari, yilKolonlari, type DbKimlik } from "./kimlik"; import { hamOku, sonHamDosya, TUR_MAP } from "./refresh"; +import { KKTC_ILLERI } from "./unitur-onar"; import { argOku, bayrakVar, @@ -291,6 +292,46 @@ async function main() { console.log(` şimdi, tüm satırlar (yeni dahil) : ${toplamlar(simdi)}`); } + // 13. Aynı kurumun iki yazımı arasında unitur çelişkisi — BAZ AD üzerinden. + // unitur-onar.ts'in eski çelişki koruması GROUP BY universite (TAM ad) ile + // çalıştığı için "X ÜNİVERSİTESİ" ve "X ÜNİVERSİTESİ (ÜSKÜP-MAKEDONYA)" iki + // ayrı kurum sayılıyor, aralarındaki DEVLET↔YURTDISI KAMU çelişkisi hiç + // görünmüyordu. Baz ad = sondaki parantezli ek atılmış hâl. + const BAZ_AD = + "TRIM(CASE WHEN instr(universite, '(') > 0 THEN substr(universite, 1, instr(universite, '(') - 1) ELSE universite END)"; + const bazAdCeliskileri = `(SELECT ${BAZ_AD} AS bazAd, GROUP_CONCAT(DISTINCT unitur) AS turler, COUNT(*) AS adet, + MAX(unitur = 'DEVLET') <> MIN(unitur = 'DEVLET') AS devletBolunmus + FROM programs WHERE unitur IS NOT NULL GROUP BY 1 HAVING COUNT(DISTINCT unitur) > 1)`; + const bolunmus = ornek(`SELECT bazAd, turler, adet FROM ${bazAdCeliskileri} WHERE devletBolunmus`); + zorunlu( + "baz ad başına tek unitur", + bolunmus === "", + bolunmus === "" + ? "aynı kurumun iki yazımı devlet/vakıf filtresini bölmüyor" + : `devlet filtresine giren, vakıf filtresinden düşen kurum: ${bolunmus} → scripts/unitur-onar.ts KURUM_DUZELT` + ); + const esdegerFark = ornek(`SELECT bazAd, turler, adet FROM ${bazAdCeliskileri} WHERE NOT devletBolunmus`); + bilgi("baz ad unitur farkı (devlet/vakıf ayrımını bölmeyen)", false, esdegerFark || "yok"); + + // 14. DEVLET etiketli ama hiçbir programı Türkiye ilinde olmayan kurum. + // 13'ün kör noktası: kurumun İKİ yazımı da yanlış etiketliyse çelişki yoktur. + // Türkiye illerinde tire geçmez; yurt dışı iller tireli (BİŞKEK-KIRGIZİSTAN…), + // KKTC illeri KKTC_ILLERI'nde. İli hiç olmayan kurumlar (MSB/İçişleri) hariç. + const yurtDisiDevlet = ornek( + `SELECT universite, GROUP_CONCAT(DISTINCT il), COUNT(*) FROM programs + WHERE unitur = 'DEVLET' GROUP BY universite + HAVING SUM(il IS NOT NULL) > 0 + AND SUM(il NOT LIKE '%-%' AND il NOT IN (${KKTC_ILLERI.map(() => "?").join(",")})) = 0`, + ...KKTC_ILLERI + ); + zorunlu( + "DEVLET etiketi Türkiye ili ile destekli", + yurtDisiDevlet === "", + yurtDisiDevlet === "" + ? "DEVLET etiketli her kurumun en az bir programı Türkiye ilinde" + : `tüm programları KKTC/yurt dışında olduğu hâlde DEVLET: ${yurtDisiDevlet}` + ); + db.close(); console.log(`\nSonuç: ${basarisiz} ZORUNLU kontrol kaldı, ${uyari} uyarı.`); if (basarisiz > 0) process.exit(1);