fix(veri): yanlış DEVLET etiketi kalıcı onarım + baz ad çelişki denetimi
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 <noreply@anthropic.com>
This commit is contained in:
@@ -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<string, string> = {
|
||||
"İ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
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user