Merge branch 'gece/p3b-tadimlik-dili' into gece/entegrasyon-test-2
This commit is contained in:
@@ -67,9 +67,10 @@ KURALLAR (kesin):
|
||||
- METİN DİYETİ: her alan TEK kısa cümle, dolgu söz yok.
|
||||
- gerekce: bu programın bu aday için neden mantıklı olduğu (TEK cümle,
|
||||
en fazla ~15 kelime, Türkçe).
|
||||
- riskNotu: yerleşme ihtimali değerlendirmesi (TEK kısa cümle). ASLA "garanti",
|
||||
"kesin", "yüzde yüz" deme; "yüksek/orta/düşük ihtimal" dili kullan.
|
||||
- trendOzeti: son 4 yılın taban sıralaması hareketi, TEK cümle (ör. "2022'den
|
||||
- riskNotu: geçmiş taban verisine dayalı risk değerlendirmesi (TEK kısa cümle).
|
||||
ASLA garanti/kesin/yüzde/olasılık/ihtimal deme; "güvenli / az riskli /
|
||||
riskli" etiketleriyle konuş.
|
||||
- trendOzeti: son 5 yılın taban sıralaması hareketi, TEK cümle (ör. "2022'den
|
||||
beri taban 45B'den 38B'ye indi, bölüm zorlaşıyor").
|
||||
- Adayın sıralaması yaklaşıktır; kesin sıra telaffuz etme, banda göre konuş.`;
|
||||
|
||||
@@ -82,9 +83,9 @@ function mockMetin(p: Program, temsiliSira: number, kategori: string | null) {
|
||||
return {
|
||||
gerekce: `(Dev mock) ${p.isim}, ${kategori ? `${kategori} ilgisiyle ve ` : ""}~${temsiliSira.toLocaleString("tr-TR")} bandındaki sıralamanla uyumlu bir aday.`,
|
||||
riskNotu:
|
||||
"(Dev mock) Taban sıralaması senin civarında; orta-yüksek ihtimalle yerleşilebilir.",
|
||||
"(Dev mock) Son taban sıralaması sırana yakın; küçük bir kayma sonucu değiştirebilir.",
|
||||
trendOzeti:
|
||||
"(Dev mock) Son 4 yılda taban sıralaması görece stabil seyretti.",
|
||||
"(Dev mock) Son 5 yılda taban sıralaması görece stabil seyretti.",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,8 @@ KURALLAR:
|
||||
sıralaması adayın belirgin altında, Az riskli (sarı) = adayın sıralaması
|
||||
civarında, Riskli (kırmızı) = geçen yıl adayın sıralamasının üzerinde
|
||||
kapandı. Risk sorularında bu etiketleri ve taban sıralarını kullan.
|
||||
- ASLA yerleşme garantisi verme; "yüksek/orta/düşük ihtimal" dili kullan.
|
||||
- ASLA yerleşme garantisi verme; olasılık, yüzde ya da "ihtimal" dili kullanma —
|
||||
güvenli / az riskli / riskli etiketleri ve taban sıralarıyla konuş.
|
||||
- Bilmediğin güncel veriyi uydurma; emin değilsen YÖK Atlas'a bakmasını söyle.
|
||||
- Taban sıralamalarının her yıl değiştiğini gerektiğinde hatırlat.
|
||||
- Cevapları kısa ve öz tut (genelde 3-6 cümle); istenirse detaylandır.
|
||||
|
||||
@@ -52,7 +52,7 @@ export const metadata: Metadata = {
|
||||
template: "%s — KolayTercih",
|
||||
},
|
||||
description:
|
||||
"YKS sıralamana göre gerçek YÖK Atlas verisiyle dengeli 24 tercihlik liste. İnsan danışmanın onda bir fiyatına, yapay zekâ destekli tercih danışmanlığı.",
|
||||
"YKS sıralamana göre gerçek YÖK Atlas verisiyle dengeli 24 tercihlik liste. İnsan danışmandan çok daha düşük fiyata, yapay zekâ destekli tercih danışmanlığı.",
|
||||
applicationName: "KolayTercih",
|
||||
openGraph: {
|
||||
type: "website",
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
SectionEyebrow,
|
||||
} from "@/components/pixel-decor";
|
||||
import { MAX_REVIZYON, URUNLER } from "@/lib/credits";
|
||||
import { VERI_YIL_SAYISI } from "@/lib/veri-kapsami";
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
@@ -87,7 +88,7 @@ const problems = [
|
||||
icon: Wallet,
|
||||
title: "Danışman lüks haline geldi",
|
||||
description:
|
||||
"İyi bir tercih danışmanı 2.000–10.000 TL istiyor. Ücretsiz robotlar ise sadece liste filtreliyor, akıl vermiyor.",
|
||||
"İyi bir tercih danışmanının ücreti birkaç bin lirayı bulabiliyor. Ücretsiz robotlar ise sadece liste filtreliyor, akıl vermiyor.",
|
||||
},
|
||||
];
|
||||
|
||||
@@ -103,8 +104,7 @@ const steps = [
|
||||
icon: BarChart3,
|
||||
step: "2",
|
||||
title: "Yapay zekâ veriyi tarasın",
|
||||
description:
|
||||
"Son 4 yılın YÖK Atlas verisi — taban sıralaması trendleri, kontenjan değişimleri, doluluk oranları — senin için analiz edilsin.",
|
||||
description: `Son ${VERI_YIL_SAYISI} yılın YÖK Atlas verisi — taban sıralaması trendleri, kontenjan değişimleri, doluluk oranları — senin için analiz edilsin.`,
|
||||
},
|
||||
{
|
||||
icon: ShieldCheck,
|
||||
@@ -118,8 +118,7 @@ const steps = [
|
||||
const faqs = [
|
||||
{
|
||||
question: "KolayTercih'in önerileri neye dayanıyor?",
|
||||
answer:
|
||||
"Tüm öneriler YÖK Atlas'ın resmî verisine dayanır: son 4 yılın taban başarı sıralamaları, kontenjanlar, yerleşme istatistikleri ve doluluk oranları. Yapay zekâ bu veriyi yorumlar; veri olmadan tahmin yürütmez.",
|
||||
answer: `Tüm öneriler YÖK Atlas'ın resmî verisine dayanır: son ${VERI_YIL_SAYISI} yılın taban başarı sıralamaları, kontenjanlar, yerleşme istatistikleri ve doluluk oranları. Yapay zekâ bu veriyi yorumlar; veri olmadan tahmin yürütmez.`,
|
||||
},
|
||||
{
|
||||
question: "Ücretsiz tercih robotlarından farkı ne?",
|
||||
@@ -145,7 +144,7 @@ const comparison = [
|
||||
tercihai: true,
|
||||
},
|
||||
{
|
||||
feature: "Son 4 yılın trend analizi",
|
||||
feature: `Son ${VERI_YIL_SAYISI} yılın trend analizi`,
|
||||
robot: false,
|
||||
danisman: true,
|
||||
tercihai: true,
|
||||
@@ -165,7 +164,7 @@ const comparison = [
|
||||
{
|
||||
feature: "Fiyat",
|
||||
robot: "Ücretsiz",
|
||||
danisman: "2.000–10.000 TL",
|
||||
danisman: "Danışmana göre değişir",
|
||||
tercihai: `${PAKET_FIYATI} TL · tek seferlik`,
|
||||
},
|
||||
];
|
||||
@@ -177,7 +176,7 @@ const paketOzellikleri = [
|
||||
},
|
||||
{
|
||||
icon: ShieldCheck,
|
||||
text: "Her tercih için yerleşme olasılığı ve risk analizi (son 4 yıl)",
|
||||
text: `Her tercih için yerleşme riski ve trend analizi (son ${VERI_YIL_SAYISI} yıl)`,
|
||||
},
|
||||
{ icon: FileText, text: "Veliyle paylaşılabilir PDF rapor" },
|
||||
{ icon: RefreshCcw, text: `${MAX_REVIZYON} liste revizyon hakkı` },
|
||||
@@ -362,7 +361,7 @@ export default function Home() {
|
||||
{[
|
||||
{ value: "23.000+", label: "Taranan lisans programı" },
|
||||
{
|
||||
value: "4 yıl",
|
||||
value: `${VERI_YIL_SAYISI} yıl`,
|
||||
label: "Taban sıralaması trend verisi",
|
||||
},
|
||||
{ value: "%100", label: "Resmî YÖK Atlas kaynağı" },
|
||||
@@ -481,7 +480,7 @@ export default function Home() {
|
||||
Ne kadar? Ne dahil?
|
||||
</h2>
|
||||
<p className="max-w-xl text-lg leading-relaxed text-slate-600">
|
||||
İnsan danışmanın 2.000–10.000 TL aldığı işi tek seferlik{" "}
|
||||
İnsan danışmanın birkaç bin liraya yaptığı işi tek seferlik{" "}
|
||||
{PAKET_FIYATI} TL'ye alırsın. Abonelik yok, gizli ücret
|
||||
yok.
|
||||
</p>
|
||||
|
||||
@@ -46,11 +46,12 @@ export default function PaketPage({
|
||||
<SectionEyebrow>Tek seferlik ödeme · Abonelik yok</SectionEyebrow>
|
||||
<h1 className="mt-4 font-heading text-3xl font-bold sm:text-4xl">
|
||||
İnsan danışmanın işini,{" "}
|
||||
<span className="text-primary">onda bir fiyatına</span>
|
||||
<span className="text-primary">çok daha düşük fiyata</span>
|
||||
</h1>
|
||||
<p className="mx-auto mt-3 max-w-xl text-slate-600">
|
||||
Tercih danışmanları bu dönemde 2.000–10.000 TL alıyor. KolayTercih
|
||||
aynı işi gerçek YÖK Atlas verisiyle, dakikalar içinde yapar.
|
||||
Tercih danışmanlarının ücreti bu dönemde birkaç bin lirayı
|
||||
bulabiliyor. KolayTercih aynı işi gerçek YÖK Atlas verisiyle,
|
||||
dakikalar içinde yapar.
|
||||
</p>
|
||||
</Parallax>
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ const FUNNEL_METINLERI = [
|
||||
"Sıralaman ne söylüyor? Birlikte bakalım.",
|
||||
"Her satırı senin için hesaplıyoruz.",
|
||||
"Akıllı tercih, içi rahat bir bekleyiş.",
|
||||
"Yerleşme ihtimalini görerek seç.",
|
||||
"Yerleşme riskini görerek seç.",
|
||||
"Sana özel liste birkaç dakikada hazır.",
|
||||
"İlgi alanların ve şehrin, tek listede.",
|
||||
"Boş kalan tercih, kaçan fırsattır.",
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { getCurrentUser } from "@/lib/session";
|
||||
import { URUNLER, DENEME_KREDISI } from "@/lib/credits";
|
||||
import { VERI_YIL_SAYISI } from "@/lib/veri-kapsami";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Parallax } from "@/components/parallax";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
@@ -28,7 +29,7 @@ const OZELLIKLER = [
|
||||
},
|
||||
{
|
||||
icon: ShieldCheck,
|
||||
text: "Her tercih için yerleşme olasılığı ve risk analizi (son 4 yıl trend)",
|
||||
text: `Her tercih için yerleşme riski ve trend analizi (son ${VERI_YIL_SAYISI} yıl)`,
|
||||
},
|
||||
{ icon: FileText, text: "Veliyle paylaşılabilir PDF rapor" },
|
||||
{ icon: RefreshCcw, text: "2 liste revizyon hakkı" },
|
||||
|
||||
@@ -18,7 +18,13 @@ import {
|
||||
type AdayProgram,
|
||||
} from "../rapor-havuzu";
|
||||
import { secimOzeti } from "@/features/sihirbaz/sihirbaz-sabitler";
|
||||
import { RISK_ETIKET, dilimdenRisk, riskHesapla } from "../risk";
|
||||
import {
|
||||
RISK_ETIKET,
|
||||
dilimdenRisk,
|
||||
riskHesapla,
|
||||
riskNotuMetni,
|
||||
} from "../risk";
|
||||
import { trendOzetiMetni } from "../trend";
|
||||
|
||||
// Yapay zekâdan istenen küçük şema (kullanıcı kararı, 6 Ağu 2026): riskNotu
|
||||
// ve trendOzeti gerçek sıra geçmişinden deterministik üretilir; dilim iskeleti
|
||||
@@ -127,50 +133,19 @@ function normalizeEt(
|
||||
// geçmişinden hesaplanır; model bu alanlarda jenerik dolgu yazıyordu, kod
|
||||
// kesin rakam verir ("45B düzeyinden 38B düzeyine indi").
|
||||
|
||||
/** 45000 -> "45B", 1_200_000 -> "1,2M" (şemadaki öğrenci dili). */
|
||||
function siraKisa(n: number): string {
|
||||
if (n >= 1_000_000) {
|
||||
return `${(n / 1_000_000).toLocaleString("tr-TR", { maximumFractionDigits: 1 })}M`;
|
||||
}
|
||||
if (n >= 1000) return `${Math.round(n / 1000)}B`;
|
||||
return String(n);
|
||||
}
|
||||
|
||||
function trendOzetiUret(p: AdayProgram): string {
|
||||
const ham: Array<[number, number | null | undefined]> = [
|
||||
[2021, p.sira2021],
|
||||
[2022, p.sira2022],
|
||||
[2023, p.sira2023],
|
||||
[2024, p.sira2024],
|
||||
[2025, p.sira2025],
|
||||
];
|
||||
const seri = ham.filter((cift): cift is [number, number] => cift[1] != null);
|
||||
if (seri.length < 2) {
|
||||
return "Karşılaştırma için yeterli geçmiş yıl verisi yok; trendi temkinli oku.";
|
||||
}
|
||||
const [ilkYil, ilkSira] = seri[0];
|
||||
const [sonYil, sonSira] = seri[seri.length - 1];
|
||||
const oran = (sonSira - ilkSira) / ilkSira;
|
||||
if (oran <= -0.1) {
|
||||
return `${ilkYil}-${sonYil} arasında taban ${siraKisa(ilkSira)} düzeyinden ${siraKisa(sonSira)} düzeyine indi; bölüm zorlaşıyor.`;
|
||||
}
|
||||
if (oran >= 0.1) {
|
||||
return `${ilkYil}-${sonYil} arasında taban ${siraKisa(ilkSira)} düzeyinden ${siraKisa(sonSira)} düzeyine çıktı; yerleşmek görece kolaylaşıyor.`;
|
||||
}
|
||||
return `${ilkYil}-${sonYil} arasında taban sıralaması ${siraKisa(sonSira)} düzeyinde stabil seyretti.`;
|
||||
// Cümle tadımlıkla ortak saf fonksiyondan gelir (lib/trend.ts)
|
||||
return trendOzetiMetni([
|
||||
p.sira2021,
|
||||
p.sira2022,
|
||||
p.sira2023,
|
||||
p.sira2024,
|
||||
p.sira2025,
|
||||
]);
|
||||
}
|
||||
|
||||
function riskNotuUret(p: AdayProgram, adaySira: number): string {
|
||||
switch (riskHesapla(p.sira2025 ?? p.sira2024, adaySira)) {
|
||||
case "guvenli":
|
||||
return "Taban sıralaması senin sıranın belirgin altında; yüksek ihtimalle yerleşirsin.";
|
||||
case "az-riskli":
|
||||
return "Taban sıralaması sırana yakın; orta ihtimalle yerleşilebilir.";
|
||||
case "riskli":
|
||||
return "Taban sıralaması senin sıranın üstünde kapandı; yerleşme ihtimali düşük.";
|
||||
default:
|
||||
return "Güncel taban sıralaması verisi yok; ihtimal öngörülemez, temkinli yaklaş.";
|
||||
}
|
||||
return riskNotuMetni(riskHesapla(p.sira2025 ?? p.sira2024, adaySira));
|
||||
}
|
||||
|
||||
/** Havuz programını render için gereken bilgiye indirger. */
|
||||
|
||||
@@ -42,3 +42,22 @@ export function dilimdenRisk(dilim: Dilim | string): RiskSeviyesi {
|
||||
if (dilim === "dengeli") return "az-riskli";
|
||||
return "riskli";
|
||||
}
|
||||
|
||||
/**
|
||||
* Risk seviyesinin tek cümlelik açıklaması (rapor satırları + tadımlık).
|
||||
* Ürün yerleşme olasılığı HESAPLAMAZ; yalnızca son taban sıralamasını adayın
|
||||
* sırasıyla kıyaslar. Bu yüzden metin olasılık/ihtimal/"yerleşirsin" demez.
|
||||
* "Geçen yıl" da denmez: efektif sıra 2025 yoksa 2024'e düşüyor.
|
||||
*/
|
||||
export function riskNotuMetni(risk: RiskSeviyesi | null): string {
|
||||
switch (risk) {
|
||||
case "guvenli":
|
||||
return "Son taban sıralaması senin sıranın belirgin altında; geçmiş veriye göre güvenli aralıkta.";
|
||||
case "az-riskli":
|
||||
return "Son taban sıralaması sırana yakın; küçük bir kayma sonucu değiştirebilir.";
|
||||
case "riskli":
|
||||
return "Son taban sıralaması senin sıranın üstünde kapandı; hayal tercihi olarak düşün.";
|
||||
default:
|
||||
return "Güncel taban verisi yok; bu satırı temkinli değerlendir.";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import { turkishSlugify } from "./slug";
|
||||
import type { PuanTuruKey } from "@/types/yokatlas";
|
||||
import type { Dilim } from "./rapor-havuzu";
|
||||
import type { RaporSonuc } from "@/features/rapor/types/rapor";
|
||||
import { dilimdenRisk, riskHesapla, riskNotuMetni } from "./risk";
|
||||
import { trendOzetiMetni } from "./trend";
|
||||
|
||||
/** Sihirbaz kategorisi seçilmemiş kullanıcı için kova×tür başına fallback satır. */
|
||||
export const GENEL_KATEGORI = "genel";
|
||||
@@ -48,6 +50,22 @@ export interface TadimlikSatir {
|
||||
|
||||
export type TadimlikTip = "genel" | "devlet" | "vakif";
|
||||
|
||||
/**
|
||||
* gerekce, satırın servis edilen TEK LLM metnidir (batch'te temsili sıraya
|
||||
* göre yazıldı). Ürün yerleşme olasılığı hesaplamaz; bu yüzden olasılık /
|
||||
* ihtimal / şans / garanti / kesin / yüzde / "yerleşirsin" diyen gerekçe
|
||||
* servis edilmez. Eski batch'in "son 4 yıl" kalıbı da takılır (veri 5 yılı
|
||||
* kapsıyor, bkz. veri-kapsami.ts). Kalıp küçük harfledir: metin önce Türkçe
|
||||
* kurallarıyla küçültülür ("İhtimal" → "ihtimal"; düz /i bayrağı İ/ı'yı
|
||||
* eşlemez).
|
||||
*/
|
||||
const YASAK_GEREKCE_DILI =
|
||||
/olasılı|ihtimal|şans|garanti|kesin|yüzde|yerleşirsin|yerleşebil|son (4|dört) yıl/;
|
||||
|
||||
export function gerekceServisEdilebilir(gerekce: string): boolean {
|
||||
return !YASAK_GEREKCE_DILI.test(gerekce.toLocaleLowerCase("tr-TR"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Kova × tür × kategori × tip anahtarıyla tadımlık satırı seçer. Kategoriye
|
||||
* özel satır yoksa "genel" kategoriye düşer; o da yoksa null — çağıran taraf
|
||||
@@ -55,6 +73,9 @@ export type TadimlikTip = "genel" | "devlet" | "vakif";
|
||||
*
|
||||
* Tip fallback'i YOKTUR: kullanıcı devlet/vakıf seçtiyse yanlış tipte örnek
|
||||
* asla gösterilmez — uygun satır yoksa tadımlık gizlenir.
|
||||
*
|
||||
* Gerekçesi dil filtresine takılan satır YOK sayılır: aynı sırayla "genel"
|
||||
* kategoriye düşülür, o da takılırsa null (tadımlık gizlenir).
|
||||
*/
|
||||
export async function tadimlikSec(
|
||||
sira: number,
|
||||
@@ -75,14 +96,23 @@ export async function tadimlikSec(
|
||||
eq(schema.tadimlikHavuzu.tip, tip),
|
||||
),
|
||||
});
|
||||
if (satir) {
|
||||
if (satir && gerekceServisEdilebilir(satir.gerekce)) {
|
||||
const program = satir.program as RaporSonuc["programlar"][string];
|
||||
return {
|
||||
programId: satir.programId,
|
||||
dilim: satir.dilim,
|
||||
// Servis edilen tek LLM metni; yukarıdaki dil filtresinden geçti.
|
||||
gerekce: satir.gerekce,
|
||||
riskNotu: satir.riskNotu,
|
||||
trendOzeti: satir.trendOzeti,
|
||||
program: satir.program as RaporSonuc["programlar"][string],
|
||||
// DB'deki riskNotu ve trendOzeti batch'te LLM'in yazdığı cümlelerdir;
|
||||
// ikisi de servis EDİLMEZ. Risk notu, rozetle (tadimlik-satiri.tsx)
|
||||
// aynı formülden adayın gerçek sırasına göre; trend, rapordakiyle aynı
|
||||
// saf fonksiyondan programın sıra geçmişinden okuma anında üretilir —
|
||||
// metin rozetle çelişemez, "son 4 yıl" gibi bayat kalıp taşınmaz.
|
||||
riskNotu: riskNotuMetni(
|
||||
riskHesapla(program.efektifSira, sira) ?? dilimdenRisk(satir.dilim),
|
||||
),
|
||||
trendOzeti: trendOzetiMetni(program.siraGecmisi),
|
||||
program,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
51
src/lib/trend.ts
Normal file
51
src/lib/trend.ts
Normal file
@@ -0,0 +1,51 @@
|
||||
// Deterministik trend cümlesi: rapor satırları (lib/ai/rapor.ts) ve girişsiz
|
||||
// tadımlık (lib/tadimlik-havuzu.ts) AYNI saf fonksiyondan okur. Cümle gerçek
|
||||
// YÖK Atlas sıra geçmişinden kesin rakamla kurulur ("45B düzeyinden 38B
|
||||
// düzeyine indi"); yıl aralığı sabit bir "son N yıl" kalıbından değil,
|
||||
// serideki dolu yıllardan gelir.
|
||||
|
||||
/**
|
||||
* Sıra geçmişi dizisinin ilk elemanının yılı. Dizi her yerde 2021'den başlar
|
||||
* (bkz. programBilgisi, raporSiraSerisi); yeni veri yılı sona eklenir, bu
|
||||
* çapa değişmez.
|
||||
*/
|
||||
const ILK_VERI_YILI = 2021;
|
||||
|
||||
/** 45000 -> "45B", 1_200_000 -> "1,2M" (şemadaki öğrenci dili). */
|
||||
function siraKisa(n: number): string {
|
||||
if (n >= 1_000_000) {
|
||||
return `${(n / 1_000_000).toLocaleString("tr-TR", { maximumFractionDigits: 1 })}M`;
|
||||
}
|
||||
if (n >= 1000) return `${Math.round(n / 1000)}B`;
|
||||
return String(n);
|
||||
}
|
||||
|
||||
/**
|
||||
* [2021..] taban sıralaması dizisinden tek cümlelik trend özeti.
|
||||
*
|
||||
* Cümle yalnızca OLANI betimler: ilk ve son dolu yılın tabanını kıyaslar
|
||||
* (±%10 eşiği). "Zorlaşıyor / kolaylaşıyor" gibi geleceğe dönük yargı
|
||||
* kurmaz — iki uç yılın kıyası aradaki seyri ve gelecek yılı söylemez.
|
||||
* Küçülen sıra = taban sıkılaştı, büyüyen sıra = taban gevşedi.
|
||||
*/
|
||||
export function trendOzetiMetni(
|
||||
gecmis: readonly (number | null | undefined)[] | undefined,
|
||||
): string {
|
||||
const seri: Array<[number, number]> = [];
|
||||
(gecmis ?? []).forEach((sira, i) => {
|
||||
if (sira != null) seri.push([ILK_VERI_YILI + i, sira]);
|
||||
});
|
||||
if (seri.length < 2) {
|
||||
return "Karşılaştırma için yeterli geçmiş yıl verisi yok; trendi temkinli oku.";
|
||||
}
|
||||
const [ilkYil, ilkSira] = seri[0];
|
||||
const [sonYil, sonSira] = seri[seri.length - 1];
|
||||
const oran = (sonSira - ilkSira) / ilkSira;
|
||||
if (oran <= -0.1) {
|
||||
return `${ilkYil}-${sonYil} arasında taban ${siraKisa(ilkSira)} düzeyinden ${siraKisa(sonSira)} düzeyine indi; taban sıkılaştı.`;
|
||||
}
|
||||
if (oran >= 0.1) {
|
||||
return `${ilkYil}-${sonYil} arasında taban ${siraKisa(ilkSira)} düzeyinden ${siraKisa(sonSira)} düzeyine çıktı; taban gevşedi.`;
|
||||
}
|
||||
return `${ilkYil}-${sonYil} arasında taban sıralaması ${siraKisa(sonSira)} düzeyinde stabil seyretti.`;
|
||||
}
|
||||
6
src/lib/veri-kapsami.ts
Normal file
6
src/lib/veri-kapsami.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
/**
|
||||
* Taban sıralaması geçmişinin kapsadığı yıl sayısı. Veritabanında
|
||||
* sira2021–sira2025 kolonları var (5 yıl); "son N yıl" diyen tüm metinler bu
|
||||
* sabitten okur. Yeni veri yılı eklenince (ör. sira2026) burayı güncelle.
|
||||
*/
|
||||
export const VERI_YIL_SAYISI = 5;
|
||||
Reference in New Issue
Block a user