From 0a11b1530460c677e2da1291e10efa43b825ea8c Mon Sep 17 00:00:00 2001 From: bilalgursen Date: Mon, 5 Oct 2026 01:17:01 +0300 Subject: [PATCH] =?UTF-8?q?fix(havuz):=20B2=20=E2=80=94=20ba=C5=9Far=C4=B1?= =?UTF-8?q?=20s=C4=B1ras=C4=B1=20baraj=C4=B1n=C4=B1=20ge=C3=A7emeyen=20ada?= =?UTF-8?q?ya=20o=20b=C3=B6l=C3=BCm=20=C3=B6nerilmesin=20[KARAR]?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Havuz yalnız taban sırasına bakıyordu; hayal dilimi "adayın üstünde kapanan her şey" olduğu için barajın hemen dışındaki adaya tam da yazamayacağı bölümler düşüyordu (52.000 sıralı adaya tıp, 310.000'e mühendislik). Kaynak uydurma tablo değil: program_ozellik.min_basari_sirasi (YÖK Atlas API, 2026 kılavuzu; katalogdaki "baraj … sıra" satırı ve danışmanın tercihBaraji alanı da aynı kolonu okur). db.havuzAdaylari artık min_basari_sirasi < adayın sırası olan programları SQL'de çıkarır; barajı boş program süzülmez. Kanıt (havuzdaki baraj dışı program, önce → sonra): a04 4 → 0, a06 11 → 0, a08 11 → 0, p03 17 → 0; 40 profilin hepsinde 0. a07 (SÖZ 310.000, eğitim): alandaki programların tamamı barajlı olduğu için alan artık gerçekten gevşiyor (24 hayal → 5/13/6). Bilinen sınır: kolon bazı barajlı bölümlerde boş (ör. 16 tıp, 11 hukuk satırı) — veri işi, raporda. /meraklisina şeması (aday havuzu düğümü), 4. bölüm anlatımı, aria-label ve figcaption aynı kuralla güncellendi. Co-Authored-By: Claude Fable 5.1 --- .../pazarlama/components/huni-semasi.tsx | 8 +-- .../components/meraklisina-icerik.tsx | 5 +- src/lib/db.ts | 52 +++++++++++++++---- src/lib/rapor-havuzu.ts | 6 ++- 4 files changed, 55 insertions(+), 16 deletions(-) diff --git a/src/features/pazarlama/components/huni-semasi.tsx b/src/features/pazarlama/components/huni-semasi.tsx index 33579d9..763522e 100644 --- a/src/features/pazarlama/components/huni-semasi.tsx +++ b/src/features/pazarlama/components/huni-semasi.tsx @@ -17,7 +17,7 @@ flowchart TD A --> B("2. adım · İl seçimi
programı olan tüm iller, seçtiğin alanlardaki program sayısına göre yeniden sayılır") B --> C("3. adım · Devlet / Vakıf
sayılar hem alanı hem ili hesaba katar") C --> P("Danışmana söylediklerin
öğretim dili, uzaktan/örgün, burs, yıllık ücret sınırı, evden uzaklık — sohbette söyleyip kaydettiklerin varsa havuzu bunlar da süzer") - P --> H("Aday havuzu
elde kalan gerçek programlar — yalnızca en son tercih kılavuzunda yer alanlar
kapanan ya da kodu değişip eski kaydı kalan programlar önerilmez
yalnızca KKTC uyruklulara ya da meslek lisesi mezunlarına ayrılmış kontenjanlar da havuza girmez") + P --> H("Aday havuzu
elde kalan gerçek programlar — yalnızca en son tercih kılavuzunda yer alanlar
kapanan ya da kodu değişip eski kaydı kalan programlar önerilmez
yalnızca KKTC uyruklulara ya da meslek lisesi mezunlarına ayrılmış kontenjanlar da havuza girmez
sıralaman bir bölümün başarı sırası barajının gerisindeyse (tıp, hukuk, mühendislik, öğretmenlik gibi) o bölüm de girmez") H --> S{"Havuzda 24'ten az program mı kaldı?"} S -- "evet" --> G("Filtreler adım adım gevşetilir
önce danışmana söylediklerin, sonra il, sonra alan — listede açıkça belirtilir
seçimine uyan programlar havuzda kalır, boşluk sıralamana en yakın diğer programlarla dolar") G --> H @@ -45,13 +45,13 @@ export function HuniSemasi() { return ( Havuza yalnızca en son tercih kılavuzunda yer alan programlar girer; kapanan ya da kodu değişip eski kaydı kalan programlar, yalnızca KKTC - uyruklulara ya da meslek lisesi mezunlarına ayrılmış kontenjanlar - önerilmez. Her + uyruklulara ya da meslek lisesi mezunlarına ayrılmış kontenjanlar ve + başarı sırası barajını geçemediğin bölümler önerilmez. Her adım havuzu biraz daha daraltır; danışmana söyleyip kaydettiğin tercihler de (dil, ücret, uzaklık gibi) havuzu süzer. Yapay zekâ listeyi bu havuzun{" "} diff --git a/src/features/pazarlama/components/meraklisina-icerik.tsx b/src/features/pazarlama/components/meraklisina-icerik.tsx index 3925cb0..bb73787 100644 --- a/src/features/pazarlama/components/meraklisina-icerik.tsx +++ b/src/features/pazarlama/components/meraklisina-icerik.tsx @@ -127,7 +127,10 @@ export function MeraklisinaIcerik() { kalmış bir program sana önerilmez. Adında “KKTC Uyruklu” ya da “M.T.O.K.” (meslek lisesi mezunlarına ayrılmış) yazan kontenjanlar da havuza girmez; onlar - herkesin yazabileceği tercihler değildir. Aşağıdaki şema akışın + herkesin yazabileceği tercihler değildir. Tıp, hukuk, mühendislik + ya da öğretmenlik gibi başarı sırası barajı olan bölümlerde + sıralaman barajın gerisindeyse o bölümü tercih edemezsin; bu + yüzden o da havuza girmez. Aşağıdaki şema akışın tamamını gösterir — havuz 24'ün altına düşerse filtrelerin hangi sırayla gevşetildiği (önce danışmana söylediklerin, sonra il, sonra alan), yine de yetmezse listenin eldeki programlarla, sana diff --git a/src/lib/db.ts b/src/lib/db.ts index ef55926..3ddfa16 100644 --- a/src/lib/db.ts +++ b/src/lib/db.ts @@ -89,6 +89,16 @@ export type OzellikSuzgeci = { let ozellikTablosuVar: boolean | null = null; +function ozellikTablosuVarMi(): boolean { + if (ozellikTablosuVar == null) { + ozellikTablosuVar = + getDb() + .prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='program_ozellik'") + .get() !== undefined; + } + return ozellikTablosuVar; +} + function ozellikFiltreSql(o?: OzellikSuzgeci): { sql: string; args: (string | number)[] } { if (!o) return { sql: "", args: [] }; const kosul: string[] = []; @@ -104,19 +114,27 @@ function ozellikFiltreSql(o?: OzellikSuzgeci): { sql: string; args: (string | nu args.push(o.ucretUst); } if (kosul.length === 0) return { sql: "", args: [] }; - if (ozellikTablosuVar == null) { - ozellikTablosuVar = - getDb() - .prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name='program_ozellik'") - .get() !== undefined; - } - if (!ozellikTablosuVar) return { sql: "", args: [] }; + if (!ozellikTablosuVarMi()) return { sql: "", args: [] }; return { sql: `AND id IN (SELECT id FROM program_ozellik WHERE ${kosul.join(" AND ")})`, args, }; } +/** + * Başarı sırası barajı: `program_ozellik.min_basari_sirasi` programı tercih + * edebilmek için gereken en düşük sıradır (YÖK Atlas'tan, kılavuz yılına ait; + * ör. tıp 50.000). Adayın sırası bundan kötüyse o programı yazamaz. Barajı + * boş olan program (barajsız ya da veride eksik) süzülmez. + */ +function barajFiltreSql(sira: number): { sql: string; args: number[] } { + if (!ozellikTablosuVarMi()) return { sql: "", args: [] }; + return { + sql: "AND id NOT IN (SELECT id FROM program_ozellik WHERE min_basari_sirasi < ?)", + args: [sira], + }; +} + function uniturFiltreSql(grup?: UniturGrubu): { sql: string; args: string[] } { if (grup === "devlet") return { sql: "AND unitur = ?", args: ["DEVLET"] }; if (grup === "vakif") @@ -155,6 +173,8 @@ export function dilimAra( iller?: string[]; uniturGrubu?: UniturGrubu; ozellik?: OzellikSuzgeci; + /** Adayın sırasıyla yazamayacağı (baraj dışı) programları çıkar */ + barajDisiHaric?: boolean; limit?: number; offset?: number; } = {} @@ -176,16 +196,27 @@ export function dilimAra( : ""; const unitur = uniturFiltreSql(opts.uniturGrubu); const ozellik = ozellikFiltreSql(opts.ozellik); + const baraj = opts.barajDisiHaric + ? barajFiltreSql(sira) + : { sql: "", args: [] }; const { alt, ust } = dilimAraligi(sira, dilim); if (ust != null && ust < alt) return { programlar: [], toplam: 0 }; const siraKosulu = ust != null ? `${EFEKTIF_SIRA} BETWEEN ? AND ?` : `${EFEKTIF_SIRA} >= ?`; const siraArgs = ust != null ? [alt, ust] : [alt]; - const args = [tur, onlisans, ...illerListe, ...unitur.args, ...ozellik.args, ...siraArgs]; + const args = [ + tur, + onlisans, + ...illerListe, + ...unitur.args, + ...ozellik.args, + ...baraj.args, + ...siraArgs, + ]; const base = `FROM programs - WHERE tur = ? AND onlisans = ? ${ilFiltre} ${unitur.sql} ${ozellik.sql} + WHERE tur = ? AND onlisans = ? ${ilFiltre} ${unitur.sql} ${ozellik.sql} ${baraj.sql} AND ${EFEKTIF_SIRA} IS NOT NULL AND ${siraKosulu}`; // hayal: sınıra en yakın (en büyük taban) önce; diğerleri en yakından uzağa @@ -246,6 +277,7 @@ export function searchByRank( * alandan karışık geldiği için seçilen alan olduğundan az görünüyordu. Bu * yüzden havuz dilimin tamamını alır, alan/il süzgecini kendisi uygular ve * en son kırpar (bkz. lib/rapor-havuzu.ts). En büyük puan türü ~9 bin satır. + * Adayın başarı sırası barajını geçemediği programlar buraya hiç girmez. */ export function havuzAdaylari( sira: number, @@ -253,7 +285,7 @@ export function havuzAdaylari( opts: { uniturGrubu?: UniturGrubu; ozellik?: OzellikSuzgeci } = {} ): Record { // LIMIT -1: SQLite'ta "sınırsız" - const ortak = { ...opts, limit: -1 }; + const ortak = { ...opts, barajDisiHaric: true, limit: -1 }; return { hayal: dilimAra(sira, turKey, "hayal", ortak).programlar, dengeli: dilimAra(sira, turKey, "dengeli", ortak).programlar, diff --git a/src/lib/rapor-havuzu.ts b/src/lib/rapor-havuzu.ts index 2876199..a7df302 100644 --- a/src/lib/rapor-havuzu.ts +++ b/src/lib/rapor-havuzu.ts @@ -214,6 +214,9 @@ function dilimEkle(r: { * "24'ten az" kararı o alanda/ilde gerçekten kaç program olduğuna bakar. * Bir filtre gevşediğinde ona uyan programlar havuzdan düşmez: önce onlar * alınır, boşluk sıraya en yakın diğer programlarla dolar. + * + * Adayın yazamayacağı programlar hiçbir basamakta havuza girmez: başarı + * sırası barajının gerisinde kaldığı bölümler ve özel kontenjan satırları. */ export function havuzOlustur( params: RaporParams, @@ -242,7 +245,8 @@ export function havuzOlustur( // Bırakmazsa profil gevşetilir ve aşağıdaki sihirbaz akışı aynen sürer; // liste yine kurulur, gevşetme yapay zekâya ve adaya söylenir. const suzgec = profilSuzgeci(profil, iller.length > 0); - // Dilimin tamamı; özel kontenjan satırları (KKTC uyruklu, M.T.O.K.) hariç + // Dilimin tamamı; adayın barajını geçemediği programlar (SQL) ve özel + // kontenjan satırları (KKTC uyruklu, M.T.O.K.) hariç const adaylar = (ozellik?: OzellikSuzgeci) => dilimEkle( havuzAdaylari(params.sira, params.tur, { uniturGrubu, ozellik }),