Danışman araçları, TÜİK mezun istihdamı ve Jev çıktı denetimi
- Danışman: program_netleri, program_karsilastir ve bolum_istihdam araçları; program_ara'ya devlet/vakıf, öğretim türü ve sıralama parametreleri; program_detay'a trend cümlesi; bozuk araç argümanı modele hata döner. - TÜİK Yükseköğretim İstihdam Göstergeleri 2025: bölüm bazında istihdam oranı, ilk işi bulma süresi ve kazanç grubu (src/lib/bolum-istihdam.ts, üretici scripts/tuik-istihdam-uret.py, kaynak Excel'ler data/kaynak/). - Bölüm sayfasında "Mezun istihdamı" ayrıntı satırı ve alt sayfası; AkademikAyrinti başlık/ikon alır. - Jev (TypeSafe AI) ile danışman cevabında garanti/olasılık dili denetimi; ihlalde cevabın sonuna düzeltme notu. Anahtar yoksa denetim atlanır. - /gizlilik: TypeSafe AI (ABD) sağlayıcı satırı. KARARLAR.md: 5 Ekim kaydı. - Üç katalog dosyasındaki bg-white → bg-card değişiklikleri de bu commit'te. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
168
scripts/tuik-istihdam-uret.py
Normal file
168
scripts/tuik-istihdam-uret.py
Normal file
@@ -0,0 +1,168 @@
|
||||
"""TÜİK Yükseköğretim İstihdam Göstergeleri Excel'lerinden src/lib/bolum-istihdam.ts sabitini üretir.
|
||||
|
||||
Kullanım (yılda bir, TÜİK bülteni Temmuz'da yayımlayınca):
|
||||
python3 scripts/tuik-istihdam-uret.py 2025
|
||||
|
||||
Girdi: data/kaynak/tuik-istihdam-{lisans,onlisans}-bolum-{istihdam-orani,is-bulma-suresi,kazanc-gruplari}-<YIL>.xls
|
||||
(veriportali.tuik.gov.tr → "Yükseköğretim İstihdam Göstergeleri, <YIL>" bülteni →
|
||||
Tablolar; tarayıcıyla tek tek indirilir — portal komut satırını engelliyor ve
|
||||
art arda istekte boş sayfa dönüyor). Bağımlılık: xlrd.
|
||||
|
||||
Kurallar:
|
||||
- Veri ÜLKE GENELİ ve bölüm bazındadır; üniversite kırılımı TÜİK'te yok.
|
||||
- Anahtar: /bolum/[slug] sayfa slug'ı (bolumBazAdi + turkishSlugify). Yalnız
|
||||
yokatlas.db'de sayfası olan bölümler yazılır; eşleşmeyenler ekrana basılır.
|
||||
Takma ad tablosu yok: eşleşmeyenler henüz mezun vermemiş yeni bölümler ya da
|
||||
bizde olmayan özel yetenek / kapanmış bölümlerdir.
|
||||
- "." = TÜİK'in yayımlamadığı hücre → null. Mezun sayısı medyanın altındaki
|
||||
bölümler tablolarda hiç yok.
|
||||
- Kazanç TL değil, TÜİK'in beş kademeli grubudur (Çok düşük … Çok yüksek).
|
||||
"""
|
||||
|
||||
import re
|
||||
import sqlite3
|
||||
import sys
|
||||
import unicodedata
|
||||
from pathlib import Path
|
||||
|
||||
import xlrd
|
||||
|
||||
KOK = Path(__file__).resolve().parent.parent
|
||||
YIL = sys.argv[1] if len(sys.argv) > 1 else "2025"
|
||||
BULTEN_URL = "https://veriportali.tuik.gov.tr/tr/press/58027"
|
||||
DUZEYLER = (("lisans", 0), ("onlisans", 1))
|
||||
KAZANC_GRUPLARI = ("Çok düşük", "Düşük", "Orta", "Yüksek", "Çok yüksek")
|
||||
|
||||
|
||||
def slugify(s: str) -> str:
|
||||
# src/lib/slug.ts turkishSlugify ile aynı kural.
|
||||
s = s.replace("I", "ı").replace("İ", "i").lower()
|
||||
for a, b in [("ı", "i"), ("ş", "s"), ("ğ", "g"), ("ü", "u"), ("ö", "o"),
|
||||
("ç", "c"), ("â", "a"), ("î", "i"), ("û", "u")]:
|
||||
s = s.replace(a, b)
|
||||
s = unicodedata.normalize("NFKD", s)
|
||||
s = re.sub(r"[̀-ͯ]", "", s)
|
||||
s = re.sub(r"[^a-z0-9]+", "-", s)
|
||||
return s.strip("-")
|
||||
|
||||
|
||||
def bolum_slug(isim: str) -> str:
|
||||
# src/lib/slug.ts bolumSayfaSlug ile aynı kural.
|
||||
return slugify(re.sub(r"\s+", " ", re.sub(r"\s*\([^)]*\)", "", isim)).strip())
|
||||
|
||||
|
||||
def tr_satir(v) -> str:
|
||||
"""Hücreler 'Türkçe\\r\\nEnglish' biçiminde; Türkçe satırı al."""
|
||||
return str(v).replace("\r", "").split("\n")[0].strip()
|
||||
|
||||
|
||||
def tablo(duzey: str, ad: str) -> tuple[dict[str, tuple[str, object]], object]:
|
||||
"""slug → (TÜİK adı, son yıl değeri) ve 'Toplam' satırının değeri."""
|
||||
yol = KOK / "data" / "kaynak" / f"tuik-istihdam-{duzey}-bolum-{ad}-{YIL}.xls"
|
||||
s = xlrd.open_workbook(yol).sheet_by_index(0)
|
||||
baslik = next(r for r in range(s.nrows) if str(s.cell_value(r, 0)).startswith("Bölüm"))
|
||||
kolon = next(c for c in range(s.ncols) if str(s.cell_value(baslik, c)).startswith(YIL))
|
||||
satirlar: dict[str, tuple[str, object]] = {}
|
||||
toplam = None
|
||||
for r in range(baslik + 1, s.nrows):
|
||||
bolum = tr_satir(s.cell_value(r, 0))
|
||||
if not bolum or bolum.startswith(("TÜİK", "TurkStat", ".")):
|
||||
continue
|
||||
v = s.cell_value(r, kolon)
|
||||
if bolum == "Toplam":
|
||||
toplam = v
|
||||
continue
|
||||
satirlar[slugify(bolum)] = (bolum, v)
|
||||
return satirlar, toplam
|
||||
|
||||
|
||||
def sayi(v) -> float | None:
|
||||
return round(float(v), 1) if isinstance(v, (int, float)) else None
|
||||
|
||||
|
||||
def kazanc(v) -> str | None:
|
||||
g = tr_satir(v)
|
||||
if g in KAZANC_GRUPLARI:
|
||||
return g
|
||||
if g not in (".", ""):
|
||||
sys.exit(f"Bilinmeyen kazanç grubu: {g!r}")
|
||||
return None
|
||||
|
||||
|
||||
def ts(v) -> str:
|
||||
if v is None:
|
||||
return "null"
|
||||
return f'"{v}"' if isinstance(v, str) else str(v)
|
||||
|
||||
|
||||
db = sqlite3.connect(f"file:{KOK / 'data' / 'yokatlas.db'}?mode=ro", uri=True)
|
||||
bloklar: list[str] = []
|
||||
ortalamalar: list[str] = []
|
||||
for duzey, onlisans in DUZEYLER:
|
||||
sayfalar = {bolum_slug(i) for (i,) in db.execute("SELECT DISTINCT isim FROM programs WHERE onlisans = ?", (onlisans,))}
|
||||
oran, oran_toplam = tablo(duzey, "istihdam-orani")
|
||||
sure, sure_toplam = tablo(duzey, "is-bulma-suresi")
|
||||
kaz, _ = tablo(duzey, "kazanc-gruplari")
|
||||
hepsi = sorted(set(oran) | set(sure) | set(kaz))
|
||||
eslesen = [k for k in hepsi if k in sayfalar]
|
||||
eslesmeyen = [(oran.get(k) or sure.get(k) or kaz[k])[0] for k in hepsi if k not in sayfalar]
|
||||
print(f"{duzey}: TÜİK {len(hepsi)} bölüm, sayfası olan {len(eslesen)}")
|
||||
if eslesmeyen:
|
||||
print(" sayfası olmayan (yazılmadı):", ", ".join(eslesmeyen))
|
||||
satirlar = []
|
||||
for k in eslesen:
|
||||
o = sayi(oran[k][1]) if k in oran else None
|
||||
s = sayi(sure[k][1]) if k in sure else None
|
||||
g = kazanc(kaz[k][1]) if k in kaz else None
|
||||
if o is None and s is None and g is None:
|
||||
continue
|
||||
satirlar.append(f' "{k}": {{ istihdam: {ts(o)}, isBulmaAy: {ts(s)}, kazanc: {ts(g)} }},')
|
||||
bloklar.append(f" {duzey}: {{\n" + "\n".join(satirlar) + "\n },")
|
||||
ortalamalar.append(f" {duzey}: {{ istihdam: {ts(sayi(oran_toplam))}, isBulmaAy: {ts(sayi(sure_toplam))} }},")
|
||||
|
||||
cikti = f"""// TÜİK Yükseköğretim İstihdam Göstergeleri {YIL}: bölüm başına mezun istihdamı.
|
||||
//
|
||||
// ÜRETİLMİŞ DOSYA — elle düzenleme. Kaynak Excel'ler data/kaynak/ altında;
|
||||
// yeniden üretmek için: python3 scripts/tuik-istihdam-uret.py {YIL}
|
||||
//
|
||||
// Veri ÜLKE GENELİ ve bölüm bazındadır: aynı bölümün her üniversitesi için
|
||||
// aynı sayı döner; üniversite kırılımı TÜİK'te yok. Kayıtlı istihdam = mezunun
|
||||
// SGK kaydıyla çalışıyor olması. Kazanç TL değil, beş kademeli gruptur.
|
||||
// Anahtar: /bolum/[slug] sayfa slug'ı. Burada olmayan bölüm ya henüz yeterli
|
||||
// mezun vermemiştir ya da TÜİK tablosunda yoktur — "kötü" demek değildir.
|
||||
|
||||
export const BOLUM_ISTIHDAM_YIL = {YIL};
|
||||
|
||||
export const BOLUM_ISTIHDAM_KAYNAK = "TÜİK, Yükseköğretim İstihdam Göstergeleri, {YIL}";
|
||||
|
||||
export const BOLUM_ISTIHDAM_KAYNAK_URL = "{BULTEN_URL}";
|
||||
|
||||
export type KazancGrubu = {" | ".join(f'"{g}"' for g in KAZANC_GRUPLARI)};
|
||||
|
||||
export type IstihdamDuzeyi = "lisans" | "onlisans";
|
||||
|
||||
export type BolumIstihdam = {{
|
||||
/** Kayıtlı istihdam oranı (%) */
|
||||
istihdam: number | null;
|
||||
/** Mezuniyetten sonra ilk işi bulma süresi (ay, ortalama) */
|
||||
isBulmaAy: number | null;
|
||||
/** Aylık ortalama kazanç grubu (bölümler arası göreli) */
|
||||
kazanc: KazancGrubu | null;
|
||||
}};
|
||||
|
||||
/** Tüm mezunların ortalaması — kıyas çizgisi. */
|
||||
export const ISTIHDAM_ORTALAMA: Record<IstihdamDuzeyi, {{ istihdam: number | null; isBulmaAy: number | null }}> = {{
|
||||
{chr(10).join(ortalamalar)}
|
||||
}};
|
||||
|
||||
export const BOLUM_ISTIHDAM: Record<IstihdamDuzeyi, Record<string, BolumIstihdam>> = {{
|
||||
{chr(10).join(bloklar)}
|
||||
}};
|
||||
|
||||
/** Bölüm sayfa slug'ının TÜİK satırı; yoksa null. */
|
||||
export function bolumIstihdamBul(slug: string, duzey: IstihdamDuzeyi): BolumIstihdam | null {{
|
||||
return BOLUM_ISTIHDAM[duzey][slug] ?? null;
|
||||
}}
|
||||
"""
|
||||
(KOK / "src" / "lib" / "bolum-istihdam.ts").write_text(cikti, encoding="utf-8")
|
||||
print("yazıldı: src/lib/bolum-istihdam.ts")
|
||||
Reference in New Issue
Block a user