feat(veri): Program tipi yılsız — db eşleyicisi, lib ve snapshot ilkYil (P-B a)

Program artık siraGecmisi (YILLAR ile hizalı) + efektifSira + siraSon/puanSon/
kontenjanSon/yerlesenSon taşır. db.ts kolonları YILLAR'dan üretip takma adla
seçer, programSatiri eşleyicisi ham satırı Program'a çevirir; SELECT_COLS'un
üç tüketicisi de eşleyiciden geçer. EFEKTIF_SIRA veri-yillari'dan gelir.

- trend: ILK_YIL tek kaynaktan, opsiyonel ilkYil parametresi
- rapor snapshot'ına opsiyonel ilkYil yazılır (eski kayıtlar: okurken ILK_YIL)
- rapor promptu son 4 yılı göndermeye devam eder, anahtarlar dinamik
- tadımlık "son N yıl" filtresi genel: N != VERI_YIL_SAYISI ise servis edilmez
- veri-kapsami VERI_YIL_SAYISI'nı veri-yillari'dan türetir

Bileşenler bir sonraki commit'te yeni tipe geçer (ikisi birlikte derlenir).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-09-21 17:00:39 +03:00
parent 38e02e0ed9
commit ce40b7c5b3
14 changed files with 195 additions and 106 deletions

View File

@@ -1,5 +1,11 @@
import "server-only";
import { getDb, SELECT_COLS, EFEKTIF_SIRA } from "./db";
import {
getDb,
programSatiri,
SELECT_COLS,
EFEKTIF_SIRA,
type ProgramHamSatiri,
} from "./db";
import type { Program } from "@/types/yokatlas";
import { kategoriBul, type Kategori } from "./kategoriler";
import { uniAdiNormalize } from "./harita";
@@ -24,7 +30,7 @@ export type BolumOzet = {
kategoriler: Kategori[];
lisans: boolean;
onlisans: boolean;
/** MIN(COALESCE(sira2025, sira2024)) — en iyi (en küçük) taban sıralaması */
/** MIN(efektif sıra) — en iyi (en küçük) taban sıralaması */
enIyiSira: number | null;
};
@@ -131,13 +137,15 @@ export function getBolumBySlug(slug: string): BolumDetay | null {
if (!kayit) return null;
const placeholders = kayit.hamIsimler.map(() => "?").join(",");
const programlar = getDb()
.prepare(
`SELECT ${SELECT_COLS} FROM programs
WHERE isim IN (${placeholders})
ORDER BY (${EFEKTIF_SIRA} IS NULL), ${EFEKTIF_SIRA} ASC`,
)
.all(...kayit.hamIsimler) as (Program & { onlisans?: number })[];
const programlar = (
getDb()
.prepare(
`SELECT ${SELECT_COLS} FROM programs
WHERE isim IN (${placeholders})
ORDER BY (${EFEKTIF_SIRA} IS NULL), ${EFEKTIF_SIRA} ASC`,
)
.all(...kayit.hamIsimler) as ProgramHamSatiri[]
).map(programSatiri);
// onlisans SELECT_COLS içinde yok; tur üzerinden ayır (TYT = önlisans)
const lisansProgramlari = programlar.filter((p) => p.tur !== "TYT");
@@ -156,14 +164,14 @@ export function getBolumBySlug(slug: string): BolumDetay | null {
if (p.unitur === "DEVLET") devlet += 1;
else if (p.unitur) vakif += 1;
else bilinmeyenTur += 1;
const efektif = p.sira2025 ?? p.sira2024;
const efektif = p.efektifSira;
if (efektif != null) {
if (minSira == null || efektif < minSira) minSira = efektif;
if (maxSira == null || efektif > maxSira) maxSira = efektif;
}
if (p.puan2025 != null) {
if (minPuan == null || p.puan2025 < minPuan) minPuan = p.puan2025;
if (maxPuan == null || p.puan2025 > maxPuan) maxPuan = p.puan2025;
if (p.puanSon != null) {
if (minPuan == null || p.puanSon < minPuan) minPuan = p.puanSon;
if (maxPuan == null || p.puanSon > maxPuan) maxPuan = p.puanSon;
}
}
@@ -357,13 +365,15 @@ export function getUniversiteBySlug(slug: string): UniDetay | null {
if (!kayit) return null;
const placeholders = kayit.hamAdlar.map(() => "?").join(",");
const programlar = getDb()
.prepare(
`SELECT ${SELECT_COLS} FROM programs
WHERE universite IN (${placeholders})
ORDER BY fakulte, (${EFEKTIF_SIRA} IS NULL), ${EFEKTIF_SIRA} ASC`,
)
.all(...kayit.hamAdlar) as Program[];
const programlar = (
getDb()
.prepare(
`SELECT ${SELECT_COLS} FROM programs
WHERE universite IN (${placeholders})
ORDER BY fakulte, (${EFEKTIF_SIRA} IS NULL), ${EFEKTIF_SIRA} ASC`,
)
.all(...kayit.hamAdlar) as ProgramHamSatiri[]
).map(programSatiri);
const gruplar = new Map<string, Program[]>();
let minSira: number | null = null;
@@ -373,11 +383,11 @@ export function getUniversiteBySlug(slug: string): UniDetay | null {
const liste = gruplar.get(anahtar);
if (liste) liste.push(p);
else gruplar.set(anahtar, [p]);
const efektif = p.sira2025 ?? p.sira2024;
const efektif = p.efektifSira;
if (efektif != null && (minSira == null || efektif < minSira)) {
minSira = efektif;
}
toplamKontenjan += p.kontenjan2025 ?? 0;
toplamKontenjan += p.kontenjanSon ?? 0;
}
const ozet = getAllUniversiteler().find((u) => u.slug === slug)!;