/** * Güncel tercih kılavuzunda OLMAYAN programları `programs` → `programs_arsiv` * tablosuna taşır (silmez; aynı şema, aynı değerler). Kodu değişen programların * çift satırı (eski kod + yeni kod) böylece sorgu koşuluyla değil DB inşasında * çözülür: site yalnız bu yıl tercih edilebilen programları görür. * * `netler.program_id`: arşive giden eski kodun TEK bir güncel karşılığı varsa * (programs.eski_kod) yeni koda çevrilir; yeni kodda o yıl zaten net satırı * varsa eski satır olduğu gibi bırakılır (veri silinmez). * * ÖNCE `--kuru` ile koş: hiçbir şey yazmaz; kaç satır gideceğini ve hangi bölüm * / üniversite sayfalarının (slug) tamamen kapanacağını adlarıyla listeler. * Gerçek koşu [BİLAL] onayı ister — sayfalar siteden kalkar. * * Kullanım: * pnpm eski-arsivle --yil 2026 --db /tmp/deneme.db --kuru * pnpm eski-arsivle --yil 2026 --db /tmp/deneme.db */ import { bolumBazAdi, turkishSlugify, uniSayfaSlug } from "../src/lib/slug"; import { arsivTablosunuGarantiEt, bayrakVar, calistir, dbAc, dbArgumani, dur, kolonAdlari, metaOku, metaYaz, tabloVar, yilArgumani, } from "./yokatlas-goc"; type Ad = { ad: string; n: number }; async function main() { const yil = yilArgumani(); const dbYolu = dbArgumani(); const kuru = bayrakVar("kuru"); const db = dbAc(dbYolu, { saltOkunur: kuru }); console.log(`DB: ${dbYolu}${kuru ? "\nKURU KİP: hiçbir şey yazılmayacak" : ""}`); if (!kolonAdlari(db, "programs").includes("son_kilavuz_yili")) { dur("son_kilavuz_yili kolonu yok: önce refresh koşulmalı."); } // ── Emniyet: refresh bu yıl için tam koşmuş olmalı ───────────────────── const apiToplam = Number(metaOku(db, `refresh_${yil}_api_toplam`) ?? NaN); const atlanan = Number(metaOku(db, `refresh_${yil}_atlanan`) ?? NaN); const guncel = ( db.prepare("SELECT COUNT(*) AS n FROM programs WHERE son_kilavuz_yili = ?").get(yil) as { n: number } ).n; if (!Number.isFinite(apiToplam) || guncel !== apiToplam - atlanan) { dur( `Güncel satır sayısı (${guncel}) refresh kaydıyla (API ${apiToplam} − atlanan ${atlanan}) uyuşmuyor. ` + "Yarım bir refresh'in üstüne arşivleme yapılmaz — güncel programlar da gidebilir." ); } // NOT: `<>` NULL'ları kaçırır; son_kilavuz_yili bilinmeyen (NULL) satırlar da eskidir. const ESKI = "son_kilavuz_yili IS NOT ?"; const eskiSayi = ( db.prepare(`SELECT COUNT(*) AS n FROM programs WHERE ${ESKI}`).get(yil) as { n: number } ).n; console.log(`\nGüncel (${yil} kılavuzunda): ${guncel} Arşivlenecek: ${eskiSayi}`); if (eskiSayi === 0) { console.log("Arşivlenecek satır yok."); db.close(); return; } const kirilim = db .prepare( `SELECT COALESCE(CAST(son_kilavuz_yili AS TEXT), 'bilinmiyor (≤2024)') AS y, COUNT(*) AS n, COUNT(sira${yil - 1}) AS onceki_sirali FROM programs WHERE ${ESKI} GROUP BY son_kilavuz_yili ORDER BY son_kilavuz_yili` ) .all(yil) as { y: string; n: number; onceki_sirali: number }[]; for (const k of kirilim) { console.log(` son kılavuz yılı ${k.y}: ${k.n} satır (${k.onceki_sirali} tanesinde sira${yil - 1} dolu)`); } const halefli = ( db .prepare( `SELECT COUNT(*) AS n FROM programs e WHERE e.${ESKI} AND EXISTS (SELECT 1 FROM programs g WHERE g.eski_kod = e.id AND g.son_kilavuz_yili = ?)` ) .get(yil, yil) as { n: number } ).n; console.log( ` yeni kodla devam eden (eski_kod eşleşmesi): ${halefli}; karşılığı olmayan (kapanmış/ad değişmiş): ${eskiSayi - halefli}` ); // ── SEO: tamamen kaybolan bölüm / üniversite sayfaları ───────────────── const eskiIsimler = db .prepare(`SELECT isim AS ad, COUNT(*) AS n FROM programs WHERE ${ESKI} GROUP BY isim`) .all(yil) as Ad[]; const guncelIsimler = db .prepare("SELECT DISTINCT isim AS ad FROM programs WHERE son_kilavuz_yili = ?") .all(yil) as { ad: string }[]; const guncelHamIsim = new Set(guncelIsimler.map((r) => r.ad)); const guncelBolumSlug = new Set(guncelIsimler.map((r) => turkishSlugify(bolumBazAdi(r.ad)))); const kaybolanHamIsim = eskiIsimler.filter((r) => !guncelHamIsim.has(r.ad)); const kaybolanBolum = new Map(); for (const r of eskiIsimler) { const baz = bolumBazAdi(r.ad); const slug = turkishSlugify(baz); if (!slug || guncelBolumSlug.has(slug)) continue; const k = kaybolanBolum.get(slug) ?? { ad: baz, n: 0 }; k.n += r.n; kaybolanBolum.set(slug, k); } const eskiUniler = db .prepare(`SELECT universite AS ad, COUNT(*) AS n FROM programs WHERE ${ESKI} GROUP BY universite`) .all(yil) as Ad[]; const guncelUniler = db .prepare("SELECT DISTINCT universite AS ad FROM programs WHERE son_kilavuz_yili = ?") .all(yil) as { ad: string }[]; const guncelHamUni = new Set(guncelUniler.map((r) => r.ad)); const guncelUniSlug = new Set(guncelUniler.map((r) => uniSayfaSlug(r.ad))); const kaybolanHamUni = eskiUniler.filter((r) => !guncelHamUni.has(r.ad)); const kaybolanUni = new Map(); for (const r of eskiUniler) { const slug = uniSayfaSlug(r.ad); if (!slug || guncelUniSlug.has(slug)) continue; const k = kaybolanUni.get(slug) ?? { ad: r.ad, n: 0 }; k.n += r.n; kaybolanUni.set(slug, k); } console.log( `\nTamamen kaybolan ham 'isim' değeri: ${kaybolanHamIsim.length} ` + `(çoğu burs/dil parantezi varyantı; sayfa kaybı aşağıdaki slug listesidir)` ); console.log(`KAPANACAK /bolum/ sayfası: ${kaybolanBolum.size}`); for (const [slug, k] of [...kaybolanBolum].sort((a, b) => a[0].localeCompare(b[0]))) { console.log(` /bolum/${slug} — ${k.ad} (${k.n} satır)`); } console.log(`Tamamen kaybolan ham 'universite' değeri: ${kaybolanHamUni.length}`); for (const r of kaybolanHamUni) console.log(` ${r.ad} (${r.n} satır)`); console.log(`KAPANACAK /universite/ sayfası: ${kaybolanUni.size}`); for (const [slug, k] of [...kaybolanUni].sort((a, b) => a[0].localeCompare(b[0]))) { console.log(` /universite/${slug} — ${k.ad} (${k.n} satır)`); } // ── netler ───────────────────────────────────────────────────────────── const netlerVar = tabloVar(db, "netler"); if (netlerVar) { const net = db .prepare( `SELECT COUNT(*) AS satir, COUNT(DISTINCT n.program_id) AS program FROM netler n JOIN programs e ON e.id = n.program_id WHERE e.${ESKI}` ) .get(yil) as { satir: number; program: number }; console.log(`\nnetler: arşive gidecek programlara bağlı ${net.satir} satır (${net.program} program)`); } if (kuru) { console.log("\nKURU KİP bitti — DB değişmedi."); db.close(); return; } // ── Gerçek taşıma: tek transaction ───────────────────────────────────── db.transaction(() => { arsivTablosunuGarantiEt(db); const kolonlar = kolonAdlari(db, "programs"); const saglama = () => ( db .prepare( `SELECT COUNT(*) || '|' || TOTAL(sira${yil - 1}) || '|' || TOTAL(puan${yil - 1}) AS s FROM (SELECT * FROM programs UNION ALL SELECT ${kolonlar.join(", ")} FROM programs_arsiv)` ) .get() as { s: string } ).s; const once = saglama(); if (netlerVar) { // Tek halefli eski kodlar → yeni koda. OR IGNORE: yeni kodda o yıl varsa dokunma. const r = db .prepare( `UPDATE OR IGNORE netler SET program_id = ( SELECT g.id FROM programs g WHERE g.eski_kod = netler.program_id AND g.son_kilavuz_yili = @yil ) WHERE program_id IN (SELECT id FROM programs WHERE son_kilavuz_yili IS NOT @yil) AND (SELECT COUNT(*) FROM programs g WHERE g.eski_kod = netler.program_id AND g.son_kilavuz_yili = @yil) = 1` ) .run({ yil }); console.log(`\nnetler: ${r.changes} satır yeni koda çevrildi`); } const tasinan = db .prepare( `INSERT INTO programs_arsiv (${kolonlar.join(", ")}) SELECT ${kolonlar.join(", ")} FROM programs WHERE ${ESKI}` ) .run(yil).changes; const cikan = db.prepare(`DELETE FROM programs WHERE ${ESKI}`).run(yil).changes; if (tasinan !== eskiSayi || cikan !== eskiSayi) { dur(`Taşınan (${tasinan}) / çıkarılan (${cikan}) ≠ beklenen (${eskiSayi}). Geri alındı.`); } const sonra = saglama(); if (once !== sonra) { dur(`programs ∪ programs_arsiv sağlaması değişti. Geri alındı.\nönce : ${once}\nsonra: ${sonra}`); } metaYaz(db, `arsiv_${yil}_tarih`, new Date().toISOString()); metaYaz(db, `arsiv_${yil}_satir`, tasinan); console.log(`Arşivlendi: ${tasinan} satır → programs_arsiv. Sağlama aynı: ${sonra}`); })(); if (netlerVar) { const yetim = ( db .prepare("SELECT COUNT(*) AS n FROM netler WHERE program_id NOT IN (SELECT id FROM programs)") .get() as { n: number } ).n; console.log(`netler: programs'ta karşılığı olmayan satır (arşivdeki programlara ait): ${yetim}`); } db.pragma("wal_checkpoint(TRUNCATE)"); db.close(); console.log("\nSıradaki adım: pnpm db:index --db --vacuum sonra pnpm veri-kalite …"); } if (process.argv[1]?.endsWith("eski-arsivle.ts")) calistir(main);