From 972268995ee7353def12de8c1ba3fd30d5655590 Mon Sep 17 00:00:00 2001 From: bilalgursen Date: Wed, 23 Sep 2026 07:26:56 +0300 Subject: [PATCH] =?UTF-8?q?fix(veri):=20KURUM=5FDUZELT=20ar=C5=9Fiv=20tabl?= =?UTF-8?q?osunu=20da=20tarar=20(BACKLOG=20#21)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Dün Aşama D yalnız `programs`'a uygulanmıştı; `programs_arsiv`'de aynı yanlış etiket duruyordu (31 ULUSLARARASI BALKAN + 30 ULUSLARARASI SARAYBOSNA `unitur='DEVLET'`, Balkan'ın 24 satırında il Saraybosna). Kapanan programlar arşive `programs`'tan olduğu gibi taşındığı için etiket oraya da yazılmış. - `unitur-onar.ts` Aşama D iki tabloyu da tarar (`programs_arsiv` varsa). Dokunulan kolon yalnız `unitur` ve `il`; A/B/C aşamaları `programs`'ta kalır. - `veri-kalite.ts` kontrol 13 ve 14 arşivi de tarar. Genişletme olmadan iki tablo ayrışabilir ve denetim bunu görmez. - Yanlış etiket duran DB'de iki kontrol de KALDI veriyor (çıkış kodu 1): `baz ad başına tek unitur — … BALKAN/YURTDISI KAMU,DEVLET/81 ; … SARAYBOSNA/68` `DEVLET etiketi Türkiye ili ile destekli — … /SARAYBOSNA - BOSNA - HERSEK/31 ; /30` Kullanıcıya bugün yansımıyor: `src/` tarafında `programs_arsiv`'i okuyan SQL yok. Düzeltme tablo ileride okunduğunda sızıntının geri gelmemesi içindir. Dünkü veri commit'inin (1df86cf) gövdesindeki rakam düzeltmesi: "/universiteler hub'ında Devlet listesi 170 → 166" YANLIŞTIR. 170/166 ham `programs.universite` ad sayısıdır, hub satırı değil; `uniAdiNormalize` parantezli eki attığı için Kıbrıs Amerikan'ın iki yazımı tek slug. Hub'daki doğru değişim 132 → 129 kurum (3 kurum). Kaynak: `12-kod-denetimi.md` O1, dalın kendi build HTML'i. Co-Authored-By: Claude Opus 5 --- scripts/unitur-onar.ts | 74 +++++++++++++++++++++++++++--------------- scripts/veri-kalite.ts | 12 +++++-- 2 files changed, 58 insertions(+), 28 deletions(-) diff --git a/scripts/unitur-onar.ts b/scripts/unitur-onar.ts index 11dbfff..f4bdb02 100644 --- a/scripts/unitur-onar.ts +++ b/scripts/unitur-onar.ts @@ -5,7 +5,8 @@ * satır unitur=NULL kalıyor ve bunlar hem devlet/vakıf filtrelerinden hem * sihirbaz sayaçlarından sessizce düşüyor. Onarım dört aşamalı: * - * D) Kaynak veride DOLU ama YANLIŞ olan kurum alanları (KURUM_DUZELT) + * D) Kaynak veride DOLU ama YANLIŞ olan kurum alanları (KURUM_DUZELT) — + * `programs` VE `programs_arsiv` üzerinde * 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…) @@ -14,14 +15,16 @@ * duplikat adlar normalize edilir; refresh.ts insert yolunda da aynı * normalizasyon uygulanır ki duplikat bir daha oluşmasın. * - * İ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ış. + * İdempotenttir. A/B/C yalnızca `programs` tablosunun NULL satırlarına dokunur; + * D (KURUM_DUZELT) NULL koşulundan bağımsızdır — düzelttiği alanlar dolu ama + * yanlış — ve `programs_arsiv`'i de tarar (varsa). * * 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ı) */ import path from "node:path"; import Database from "better-sqlite3"; +import { tabloVar } from "./yokatlas-goc"; /** Tire etrafı boşluk + çift boşluk temizliği (üniversite adı için). */ export function uniAdiNormalize(ad: string): string { @@ -75,6 +78,11 @@ const MANUEL_IL: Record = { * * `il` düzeltmesi yalnız YANLIŞ yazılmış değeri hedefler (yanlis → dogru); * boş il'ler kapsam dışı — eksik, eksik olarak görünmeli. + * + * Aynı yanlış etiket `programs_arsiv`'e de yazılmıştı (kapanan programlar oraya + * `programs`'tan olduğu gibi taşınıyor). Bugün `src/` tarafında arşivi okuyan + * SQL yok, ama tablo ileride okunursa sızıntı geri gelirdi; bu yüzden Aşama D + * iki tabloyu da tarar. Dokunulan kolon yalnız `unitur` ve `il`. */ const KURUM_DUZELT: Record< string, @@ -112,25 +120,32 @@ export function uniturOnar(db: Database.Database): void { // 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; + // `programs` ve (varsa) `programs_arsiv` aynı haritadan geçer — iki tablo + // ayrışırsa arşiv ileride okunduğunda aynı sızıntı geri gelir. + const dTablolar = tabloVar(db, "programs_arsiv") + ? ["programs", "programs_arsiv"] + : ["programs"]; + for (const tablo of dTablolar) { + const dUnitur = db.prepare( + `UPDATE ${tablo} SET unitur = ? WHERE universite = ? AND unitur IS NOT ?` + ); + const dIl = db.prepare( + `UPDATE ${tablo} 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ı) ${tablo}: unitur ${dUniturToplam} satır, il ${dIlToplam} satır` + ); } - 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. @@ -211,11 +226,18 @@ if (process.argv[1]?.endsWith("unitur-onar.ts")) { db.pragma("journal_mode = WAL"); db.transaction(() => uniturOnar(db))(); db.pragma("wal_checkpoint(TRUNCATE)"); - const dagilim = db - .prepare( - "SELECT COALESCE(unitur,'(NULL)') AS t, COUNT(*) AS n FROM programs GROUP BY unitur ORDER BY n DESC" - ) - .all() as { t: string; n: number }[]; - console.log("Dağılım:", dagilim.map((d) => `${d.t}=${d.n}`).join(" ")); + for (const tablo of tabloVar(db, "programs_arsiv") + ? ["programs", "programs_arsiv"] + : ["programs"]) { + const dagilim = db + .prepare( + `SELECT COALESCE(unitur,'(NULL)') AS t, COUNT(*) AS n FROM ${tablo} GROUP BY unitur ORDER BY n DESC` + ) + .all() as { t: string; n: number }[]; + console.log( + `Dağılım (${tablo}):`, + dagilim.map((d) => `${d.t}=${d.n}`).join(" ") + ); + } db.close(); } diff --git a/scripts/veri-kalite.ts b/scripts/veri-kalite.ts index e011e4a..be5bfab 100644 --- a/scripts/veri-kalite.ts +++ b/scripts/veri-kalite.ts @@ -299,9 +299,17 @@ async function main() { // 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)"; + // 13 ve 14 arşivi de tarar: kapanan programlar `programs_arsiv`'e olduğu gibi + // taşındığı için yanlış etiket orada da duruyordu (BACKLOG #21). Bugün `src/` + // tarafında arşivi okuyan SQL yok, ama iki tablo ayrışırsa denetim bunu + // görmez ve tablo ileride okunduğunda sızıntı geri gelir. + const KURUMLAR = tabloVar(db, "programs_arsiv") + ? `(SELECT universite, unitur, il FROM programs + UNION ALL SELECT universite, unitur, il FROM programs_arsiv)` + : "programs"; 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)`; + FROM ${KURUMLAR} 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", @@ -318,7 +326,7 @@ async function main() { // 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 + `SELECT universite, GROUP_CONCAT(DISTINCT il), COUNT(*) FROM ${KURUMLAR} 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`,