fix(havuz): B3 — alan süzgeci kırpmadan önce uygulansın [KARAR]
Havuz her dilimden sıraya en yakın 60 programı alıp alan (kategori) süzgecini ondan sonra uyguluyordu; il seçmeyen adayın alanı, o alanda onlarca program varken "yeterli program yok" diye gevşiyordu (matris B3: 318 denemenin 206'sı). Artık dilimin tamamı okunur (db.havuzAdaylari), il + alan süzgeci ona uygulanır, 24 eşiği gerçek sayıya bakar, kırpma en sonda yapılır. Gevşetme sırası ve kırpma kuralı değişmedi. Kanıt (önce → sonra, havuzda seçili alanla eşleşen): a16 EA 200.000 Turizm 1/60 → 33/33; p03 8/60 → 60/60; a08 5/60 → 60/60; hiçbiri artık alan gevşetmiyor. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -239,6 +239,28 @@ export function searchByRank(
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Aday havuzunun ham girdisi: üç dilimin TAMAMI, yakından uzağa sıralı
|
||||
* (limit yok). Alan (kategori) DB'de kolon değil, JS'te isim+fakülteden
|
||||
* türüyor; süzgeç limitten SONRA uygulanınca sıraya en yakın satırlar her
|
||||
* 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.
|
||||
*/
|
||||
export function havuzAdaylari(
|
||||
sira: number,
|
||||
turKey: PuanTuruKey,
|
||||
opts: { uniturGrubu?: UniturGrubu; ozellik?: OzellikSuzgeci } = {}
|
||||
): Record<DilimKey, Program[]> {
|
||||
// LIMIT -1: SQLite'ta "sınırsız"
|
||||
const ortak = { ...opts, limit: -1 };
|
||||
return {
|
||||
hayal: dilimAra(sira, turKey, "hayal", ortak).programlar,
|
||||
dengeli: dilimAra(sira, turKey, "dengeli", ortak).programlar,
|
||||
garanti: dilimAra(sira, turKey, "garanti", ortak).programlar,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Programa yerleşen son kişinin yıl bazlı netleri, yeniden eskiye sıralı.
|
||||
* Veri scripts/detay.ts ile YÖK Atlas /api/netler/search'ten doldurulur.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// server-only guard'ı bilinçli yok: scripts/tadimlik-uret.ts bu modülü düz
|
||||
// node (tsx) altında import ediyor; server-only paketi orada fırlar.
|
||||
import { searchByRank, type OzellikSuzgeci } from "./db";
|
||||
import { havuzAdaylari, type OzellikSuzgeci } from "./db";
|
||||
import type { DanismanProfili } from "./danisman-profil";
|
||||
import { yakinIller } from "./mesafe";
|
||||
import {
|
||||
@@ -70,7 +70,6 @@ export function profilSuzgeci(
|
||||
}
|
||||
|
||||
const HEDEF = 24;
|
||||
const LIMIT = 60;
|
||||
|
||||
export type DilimHedef = Record<Dilim, number>;
|
||||
|
||||
@@ -111,8 +110,8 @@ export function dilimHedefiHesapla(havuz: AdayProgram[]): DilimHedef {
|
||||
}
|
||||
return hedef;
|
||||
}
|
||||
// Prompt'a giden nihai havuz: dilim başına aday sayısı. Arama LIMIT ile geniş
|
||||
// yapılır (filtre/gevşetme zenginliği için), filtre sonrası buraya kırpılır —
|
||||
// Prompt'a giden nihai havuz: dilim başına aday sayısı. Arama dilimin tamamını
|
||||
// alır (filtre/gevşetme gerçek sayıya baksın diye), filtre sonrası buraya kırpılır —
|
||||
// 20×3=60 program ≈ 8k token; 180 program 24k token tutuyor ve lokal/ücretsiz
|
||||
// modellerde prefill + isabet maliyeti yüksek (ölçüm: 6 Ağu 2026).
|
||||
const PROMPT_LIMIT_DILIM = 20;
|
||||
@@ -141,6 +140,9 @@ function dilimEkle(r: {
|
||||
* profiline (dil, öğretim türü, burs, ücret, km) göre filtreler; havuz 24'ün
|
||||
* altına düşerse kademeli gevşetir (önce profil, sonra il, sonra kategori) ki
|
||||
* liste her zaman kurulabilsin. /meraklisina şeması bu sırayı anlatır.
|
||||
*
|
||||
* Süzgeçlerin hepsi dilimin TAMAMINA uygulanır, kırpma en sonda yapılır:
|
||||
* "24'ten az" kararı o alanda/ilde gerçekten kaç program olduğuna bakar.
|
||||
*/
|
||||
export function havuzOlustur(
|
||||
params: RaporParams,
|
||||
@@ -160,6 +162,10 @@ export function havuzOlustur(
|
||||
kategoriler.length > 0
|
||||
? havuz.filter((p) => kategoriEslesir(p.isim, kategoriler, p.fakulte))
|
||||
: havuz;
|
||||
const ilFiltre = (havuz: AdayProgram[], liste: string[] | undefined) =>
|
||||
liste && liste.length > 0
|
||||
? havuz.filter((p) => p.il != null && liste.includes(p.il))
|
||||
: havuz;
|
||||
|
||||
// 0. Profil süzgeci (danışmana söylenenler) — yalnız yeterli havuz bırakırsa.
|
||||
// Bırakmazsa profil gevşetilir ve aşağıdaki sihirbaz akışı aynen sürer;
|
||||
@@ -167,13 +173,14 @@ export function havuzOlustur(
|
||||
const suzgec = profilSuzgeci(profil, iller.length > 0);
|
||||
const profilGevsetildi: Gevsetilen[] = [];
|
||||
if (suzgec.ozellik || suzgec.iller) {
|
||||
const profilliHam = dilimEkle(
|
||||
searchByRank(params.sira, params.tur, {
|
||||
iller: iller.length > 0 ? iller : suzgec.iller,
|
||||
const profilliHam = ilFiltre(
|
||||
dilimEkle(
|
||||
havuzAdaylari(params.sira, params.tur, {
|
||||
uniturGrubu,
|
||||
ozellik: suzgec.ozellik,
|
||||
limitPerBucket: LIMIT,
|
||||
}),
|
||||
),
|
||||
iller.length > 0 ? iller : suzgec.iller,
|
||||
);
|
||||
const profilliFiltreli = kategoriFiltre(profilliHam);
|
||||
if (profilliFiltreli.length >= HEDEF) {
|
||||
@@ -185,14 +192,11 @@ export function havuzOlustur(
|
||||
profilGevsetildi.push("profil");
|
||||
}
|
||||
|
||||
// 1. Tam filtre: iller + üniversite tipi (SQL), kategori (JS)
|
||||
const tamHam = dilimEkle(
|
||||
searchByRank(params.sira, params.tur, {
|
||||
iller: iller.length > 0 ? iller : undefined,
|
||||
uniturGrubu,
|
||||
limitPerBucket: LIMIT,
|
||||
}),
|
||||
// 1. Tam filtre: üniversite tipi (SQL), iller + kategori (JS)
|
||||
const ilsizHam = dilimEkle(
|
||||
havuzAdaylari(params.sira, params.tur, { uniturGrubu }),
|
||||
);
|
||||
const tamHam = ilFiltre(ilsizHam, iller);
|
||||
const tamFiltreli = kategoriFiltre(tamHam);
|
||||
if (tamFiltreli.length >= HEDEF) {
|
||||
return {
|
||||
@@ -212,12 +216,6 @@ export function havuzOlustur(
|
||||
// 3. İl gevşet: il filtresini kaldırıp kategoriye geri dön
|
||||
const gevsetildi: Gevsetilen[] = [...profilGevsetildi];
|
||||
if (iller.length > 0) gevsetildi.push("il");
|
||||
const ilsizHam = dilimEkle(
|
||||
searchByRank(params.sira, params.tur, {
|
||||
uniturGrubu,
|
||||
limitPerBucket: LIMIT,
|
||||
}),
|
||||
);
|
||||
const ilsizFiltreli = kategoriFiltre(ilsizHam);
|
||||
if (ilsizFiltreli.length >= HEDEF) {
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user