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ı.
|
* 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.
|
* 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
|
// 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.
|
// 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 type { DanismanProfili } from "./danisman-profil";
|
||||||
import { yakinIller } from "./mesafe";
|
import { yakinIller } from "./mesafe";
|
||||||
import {
|
import {
|
||||||
@@ -70,7 +70,6 @@ export function profilSuzgeci(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const HEDEF = 24;
|
const HEDEF = 24;
|
||||||
const LIMIT = 60;
|
|
||||||
|
|
||||||
export type DilimHedef = Record<Dilim, number>;
|
export type DilimHedef = Record<Dilim, number>;
|
||||||
|
|
||||||
@@ -111,8 +110,8 @@ export function dilimHedefiHesapla(havuz: AdayProgram[]): DilimHedef {
|
|||||||
}
|
}
|
||||||
return hedef;
|
return hedef;
|
||||||
}
|
}
|
||||||
// Prompt'a giden nihai havuz: dilim başına aday sayısı. Arama LIMIT ile geniş
|
// Prompt'a giden nihai havuz: dilim başına aday sayısı. Arama dilimin tamamını
|
||||||
// yapılır (filtre/gevşetme zenginliği için), filtre sonrası buraya kırpılır —
|
// 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
|
// 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).
|
// modellerde prefill + isabet maliyeti yüksek (ölçüm: 6 Ağu 2026).
|
||||||
const PROMPT_LIMIT_DILIM = 20;
|
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
|
* 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
|
* 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.
|
* 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(
|
export function havuzOlustur(
|
||||||
params: RaporParams,
|
params: RaporParams,
|
||||||
@@ -160,6 +162,10 @@ export function havuzOlustur(
|
|||||||
kategoriler.length > 0
|
kategoriler.length > 0
|
||||||
? havuz.filter((p) => kategoriEslesir(p.isim, kategoriler, p.fakulte))
|
? havuz.filter((p) => kategoriEslesir(p.isim, kategoriler, p.fakulte))
|
||||||
: havuz;
|
: 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.
|
// 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;
|
// 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 suzgec = profilSuzgeci(profil, iller.length > 0);
|
||||||
const profilGevsetildi: Gevsetilen[] = [];
|
const profilGevsetildi: Gevsetilen[] = [];
|
||||||
if (suzgec.ozellik || suzgec.iller) {
|
if (suzgec.ozellik || suzgec.iller) {
|
||||||
const profilliHam = dilimEkle(
|
const profilliHam = ilFiltre(
|
||||||
searchByRank(params.sira, params.tur, {
|
dilimEkle(
|
||||||
iller: iller.length > 0 ? iller : suzgec.iller,
|
havuzAdaylari(params.sira, params.tur, {
|
||||||
uniturGrubu,
|
uniturGrubu,
|
||||||
ozellik: suzgec.ozellik,
|
ozellik: suzgec.ozellik,
|
||||||
limitPerBucket: LIMIT,
|
}),
|
||||||
}),
|
),
|
||||||
|
iller.length > 0 ? iller : suzgec.iller,
|
||||||
);
|
);
|
||||||
const profilliFiltreli = kategoriFiltre(profilliHam);
|
const profilliFiltreli = kategoriFiltre(profilliHam);
|
||||||
if (profilliFiltreli.length >= HEDEF) {
|
if (profilliFiltreli.length >= HEDEF) {
|
||||||
@@ -185,14 +192,11 @@ export function havuzOlustur(
|
|||||||
profilGevsetildi.push("profil");
|
profilGevsetildi.push("profil");
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Tam filtre: iller + üniversite tipi (SQL), kategori (JS)
|
// 1. Tam filtre: üniversite tipi (SQL), iller + kategori (JS)
|
||||||
const tamHam = dilimEkle(
|
const ilsizHam = dilimEkle(
|
||||||
searchByRank(params.sira, params.tur, {
|
havuzAdaylari(params.sira, params.tur, { uniturGrubu }),
|
||||||
iller: iller.length > 0 ? iller : undefined,
|
|
||||||
uniturGrubu,
|
|
||||||
limitPerBucket: LIMIT,
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
|
const tamHam = ilFiltre(ilsizHam, iller);
|
||||||
const tamFiltreli = kategoriFiltre(tamHam);
|
const tamFiltreli = kategoriFiltre(tamHam);
|
||||||
if (tamFiltreli.length >= HEDEF) {
|
if (tamFiltreli.length >= HEDEF) {
|
||||||
return {
|
return {
|
||||||
@@ -212,12 +216,6 @@ export function havuzOlustur(
|
|||||||
// 3. İl gevşet: il filtresini kaldırıp kategoriye geri dön
|
// 3. İl gevşet: il filtresini kaldırıp kategoriye geri dön
|
||||||
const gevsetildi: Gevsetilen[] = [...profilGevsetildi];
|
const gevsetildi: Gevsetilen[] = [...profilGevsetildi];
|
||||||
if (iller.length > 0) gevsetildi.push("il");
|
if (iller.length > 0) gevsetildi.push("il");
|
||||||
const ilsizHam = dilimEkle(
|
|
||||||
searchByRank(params.sira, params.tur, {
|
|
||||||
uniturGrubu,
|
|
||||||
limitPerBucket: LIMIT,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
const ilsizFiltreli = kategoriFiltre(ilsizHam);
|
const ilsizFiltreli = kategoriFiltre(ilsizHam);
|
||||||
if (ilsizFiltreli.length >= HEDEF) {
|
if (ilsizFiltreli.length >= HEDEF) {
|
||||||
return {
|
return {
|
||||||
|
|||||||
Reference in New Issue
Block a user