@akredit/acik-mcp 0.6.0 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.md +41 -37
  2. package/dist/apps/acik-mcp/src/araclar.js +7 -2
  3. package/dist/apps/acik-mcp/src/bologna.d.ts +12 -0
  4. package/dist/apps/acik-mcp/src/bologna.js +580 -249
  5. package/dist/apps/acik-mcp/src/genel-ayristir.js +2 -2
  6. package/dist/apps/acik-mcp/src/http.js +2 -1
  7. package/dist/apps/acik-mcp/src/kaynak-dogrula.js +1 -1
  8. package/dist/apps/acik-mcp/src/kisisel-veri.d.ts +4 -3
  9. package/dist/apps/acik-mcp/src/kisisel-veri.js +5 -0
  10. package/dist/apps/acik-mcp/src/not-tablosu.d.ts +4 -1
  11. package/dist/apps/acik-mcp/src/not-tablosu.js +10 -4
  12. package/dist/apps/acik-mcp/src/proliz-metin.d.ts +60 -0
  13. package/dist/apps/acik-mcp/src/proliz-metin.js +271 -0
  14. package/dist/apps/acik-mcp/src/promptlar.d.ts +6 -3
  15. package/dist/apps/acik-mcp/src/promptlar.js +97 -48
  16. package/dist/apps/acik-mcp/src/sunucu.d.ts +3 -2
  17. package/dist/apps/acik-mcp/src/sunucu.js +16 -11
  18. package/dist/apps/acik-mcp/src/temel.d.ts +8 -0
  19. package/dist/apps/acik-mcp/src/temel.js +32 -0
  20. package/dist/apps/acik-mcp/src/yardim.js +46 -12
  21. package/dist/packages/baglayicilar/src/bologna-proliz/ayristir.js +4 -2
  22. package/dist/packages/cekirdek/src/bologna/taslak.js +8 -2
  23. package/dist/packages/cekirdek/src/karar/katman.d.ts +2 -2
  24. package/dist/packages/cekirdek/src/karar/katman.js +2 -2
  25. package/dist/packages/cekirdek/src/turkce.d.ts +0 -2
  26. package/dist/packages/cekirdek/src/turkce.js +0 -4
  27. package/dist/packages/olcme/src/girdi.d.ts +3 -0
  28. package/dist/packages/olcme/src/girdi.js +55 -0
  29. package/dist/packages/olcme/src/index.d.ts +1 -0
  30. package/dist/packages/olcme/src/index.js +1 -0
  31. package/package.json +9 -3
@@ -2,8 +2,8 @@
2
2
  // etiketlerine (Amaç/Objective, İçerik/Content, Öğrenme Çıktıları/Learning Outcomes...) göre bölümlere ayırır.
3
3
  // Ağ yoktur, model yoktur: yalnız etiket eşleme ve sayı okuma. Bulunamayan alan boş kalır, tahmin edilmez.
4
4
  //
5
- // Kişi satırları (Dersi Veren, Instructor, e-posta, telefon) ayrıştırmadan önce bellekte atılır. Kaynaklar
6
- // bölümü künye olarak ayrı alınır (yazar adları yayımlanmış künyedir).
5
+ // Görev satırları (Dersi Veren, Instructor, iletişim) ayrıştırmadan önce yetkililer listesine ayrılır ve olduğu gibi
6
+ // korunur; hiçbir şey atılmaz. Kaynaklar bölümü künye olarak ayrı alınır (yazar adları yayımlanmış künyedir).
7
7
  //
8
8
  // Düz metin (dış inceleme 2026-11-05): tarayıcıda Ctrl+A, Ctrl+C ile kopyalanan sayfada tablolar sekmeyle ayrılır.
9
9
  // ÖÇ-PÇ katkı matrisi ve ders yapısı bu sekmeli metinden de okunur; Proliz sayfasının düz metni tanınır.
@@ -3,7 +3,8 @@
3
3
  // verisi taşınmadığı için CORS her kökene açıktır (kimlik bilgisi kabul edilmez).
4
4
  //
5
5
  // Günlük: istek içeriği YAZILMAZ. Yalnız araç (ya da yöntem) adı, süre, gövde boyutu ve durum kodu.
6
- // Sınırlar: gövde en çok 512 KB; adres başına kova (varsayılan 60 istek, saniyede 1 dolar).
6
+ // Sınırlar: gövde en çok 512 KB; adres başına kova (varsayılan 60 istek, saniyede 1 jeton dolar); kaynak_dogrula
7
+ // için adres başına ayrı kova (varsayılan dakikada 5).
7
8
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
8
9
  import { acikSunucu } from "./sunucu.js";
9
10
  /**
@@ -432,7 +432,7 @@ export const kaynakDogrulaAraci = arac({
432
432
  ad: "kaynak_dogrula",
433
433
  baslik: "Kaynağın gerçekten var olduğunu doğrula",
434
434
  aciklama: "DOI, ISBN ya da başlık ile yazar verilen eserleri (çağrı başına en çok 5) açık kataloglarda (Crossref, Open Library, Google Books) arar. Eşleşme için tam soyad, başlık sözcükleri, verildiyse kayıt türü ve yıl denetlenir. Bulursa katalogdaki bilgiden künye, bağlantı ve imza döndürür ('katalogda_bulundu'); bulamazsa 'bulunamadi' der. Katalogda bulunmak ders kaynağı olarak uygunluk demek değildir; ona hoca karar verir.",
435
- sonrakiAdim: "Yalnız 'katalogda_bulundu' olan kaynakları hocaya künyesi ve bağlantısıyla gösterin; uygunluğuna hoca karar verir. Bologna araçlarına kaynağı { kunye, dogrulama: { durum, baglanti, imza } } olarak aynen verin. 'bulunamadi' olan kaynağı asla kendiniz önermeyin; hoca o eseri kullandığını söylerse yalnız hocanın verdiği bilgiyle dogrulama: 'hoca_beyani' olarak yazın.",
435
+ sonrakiAdim: "Yalnız 'katalogda_bulundu' olan kaynakları hocaya künyesi ve bağlantısıyla gösterin; uygunluğuna hoca karar verir. Bologna araçlarına kaynağı { kunye, dogrulama: { durum, baglanti, imza } } olarak aynen verin. 'bulunamadi' dönerse hocaya 'ne yapayım' diye sormayın: kendi web aramanızla aynı konuda en çok 2 başka aday (ISBN ya da DOI ile) bulup bu aracı bir kez daha çağırın; yine bulunamazsa alanı 'EKSİK: ders kaynağı' bırakıp akışı sürdürün, son özette sorun. 'bulunamadi' olan kaynağı asla kendiniz önermeyin; hoca o eseri kullandığını söylerse yalnız hocanın verdiği bilgiyle dogrulama: 'hoca_beyani' olarak yazın.",
436
436
  kunyeAlanlari: ["kaynaklar"],
437
437
  disAg: true,
438
438
  // Künye ve katalog numaraları kişisel veri değildir; ISBN gibi uzun sayılar numara deseniyle karışmasın.
@@ -1,10 +1,11 @@
1
+ export type KisiselVeriKapsami = "yok" | "ogrenci" | "tam";
2
+ /** HTML ise yalnız görünen metni bırakır (betik, stil, yorum, etiket ve öznitelikler atılır). Değilse aynen döner. */
3
+ export declare function gorunenMetin(x: string): string;
1
4
  /**
2
5
  * Girdide kişisel veri olabilecek türlerin adları (değerler değil). Boşsa temiz.
3
6
  * `kunyeAnahtarlari`: altındaki metinler künye kuralıyla (yazar adı serbest) değerlendirilen alan adları.
7
+ * `kapsam` verilmezse "tam"dır; sunucu, kapsam bildirmeyen araçlar için "ogrenci" verir (bkz. sunucu.ts).
4
8
  */
5
- export type KisiselVeriKapsami = "yok" | "ogrenci" | "tam";
6
- /** HTML ise yalnız görünen metni bırakır (betik, stil, yorum, etiket ve öznitelikler atılır). Değilse aynen döner. */
7
- export declare function gorunenMetin(x: string): string;
8
9
  export declare function kisiselVeriTurleri(girdi: unknown, kunyeAnahtarlari?: readonly string[], kapsam?: KisiselVeriKapsami): string[];
9
10
  export declare function kisiselVeriIletisi(turler: readonly string[]): string;
10
11
  /** Girdide giriş bilgisi var mı? Yalnız evet ya da hayır döner, değer döndürmez. */
@@ -55,6 +55,11 @@ export function gorunenMetin(x) {
55
55
  .replace(/<[^>]{0,2000}>/gu, " ")
56
56
  .replace(/&nbsp;/gu, " ");
57
57
  }
58
+ /**
59
+ * Girdide kişisel veri olabilecek türlerin adları (değerler değil). Boşsa temiz.
60
+ * `kunyeAnahtarlari`: altındaki metinler künye kuralıyla (yazar adı serbest) değerlendirilen alan adları.
61
+ * `kapsam` verilmezse "tam"dır; sunucu, kapsam bildirmeyen araçlar için "ogrenci" verir (bkz. sunucu.ts).
62
+ */
58
63
  export function kisiselVeriTurleri(girdi, kunyeAnahtarlari = [], kapsam = "tam") {
59
64
  if (kapsam === "yok")
60
65
  return [];
@@ -10,7 +10,10 @@ export declare function tabloOku(metin: string): {
10
10
  export declare function kimlikSutunlari(satirlar: string[][]): number[];
11
11
  /** Kimlik sütunu olmayan tablonun başına o1, o2 takma ad sütunu ekler. Başlıkta zaten takma ad varsa dokunmaz. */
12
12
  export declare function takmaAdEkle(metin: string): string;
13
- /** Tablodan kimlik sütunlarını atar, başa o1, o2 takma adı koyar. Yalnız bellekte çalışır. */
13
+ /**
14
+ * Tablodan kimlik sütunlarını atar, başa o1, o2 takma adı koyar. Yalnız bellekte çalışır. MCP aracı bunu kullanmaz
15
+ * (kimlik sütunu görürse tabloyu reddeder); verinin tarayıcıdan çıkmadığı /deneyin hesaplayıcısı kullanır.
16
+ */
14
17
  export declare function notTablosuAyikla(metin: string): {
15
18
  tablo: string;
16
19
  atilan: string[];
@@ -4,8 +4,9 @@
4
4
  // Kişisel veri (dış inceleme 2026-11-05): tablo sohbete yapıştırıldığı anda yapay zekâ sağlayıcısına gitmiştir;
5
5
  // sunucunun sonradan sütun atması bunu geri almaz. Bu yüzden ad, soyad ya da öğrenci numarası sütunu ya da 9-11
6
6
  // haneli bir numara görülürse tablo işlenmez; hoca bu sütunları Excel'de silip yalnız not sütunlarını yapıştırır ya
7
- // da verinin tarayıcıdan çıkmadığı hesaplayıcıyı kullanır. Değerler hiçbir yere yazılmaz. Sütun başlıklarından ölçme aracı, soru numarası, tam puan ve ders çıktısı (ÇK)
8
- // eşlemesi sezilir; sezilemeyen bilgi tahmin edilmez, eksikler listesine girer. Sayılar yalnız tablodan okunur.
7
+ // da verinin tarayıcıdan çıkmadığı hesaplayıcıyı kullanır. Değerler hiçbir yere yazılmaz. Sütun başlıklarından ölçme
8
+ // aracı, soru numarası, tam puan ve ders çıktısı (ÇK) eşlemesi sezilir; sezilemeyen bilgi tahmin edilmez, eksikler
9
+ // listesine girer. Sayılar yalnız tablodan okunur.
9
10
  import { z } from "zod";
10
11
  import { arac, KOD_KAYNAGI } from "./temel.js";
11
12
  export function ayiracBul(metin) {
@@ -70,7 +71,10 @@ export function takmaAdEkle(metin) {
70
71
  return metin;
71
72
  return satirlar.map((r, i) => [i === 0 ? "Takma ad" : `o${i}`, ...r].join("\t")).join("\n");
72
73
  }
73
- /** Tablodan kimlik sütunlarını atar, başa o1, o2 takma adı koyar. Yalnız bellekte çalışır. */
74
+ /**
75
+ * Tablodan kimlik sütunlarını atar, başa o1, o2 takma adı koyar. Yalnız bellekte çalışır. MCP aracı bunu kullanmaz
76
+ * (kimlik sütunu görürse tabloyu reddeder); verinin tarayıcıdan çıkmadığı /deneyin hesaplayıcısı kullanır.
77
+ */
74
78
  export function notTablosuAyikla(metin) {
75
79
  const { satirlar } = tabloOku(metin);
76
80
  if (satirlar.length < 2)
@@ -145,7 +149,7 @@ export const SUTUN_ESLEME_REHBERI = [
145
149
  export const notTablosuAyristirAraci = arac({
146
150
  ad: "not_tablosu_ayristir",
147
151
  baslik: "Not tablosunu ayrıştır",
148
- aciklama: "Excel ya da CSV'den yapıştırılan not tablosunu ÇK/PÇ hesabına hazırlar. Tabloda yalnız not sütunları olmalı: ad, soyad ya da numara sütunu ya da 9-11 haneli numara görülürse tablo işlenmez. Öğrencilere o1, o2 takma adı verir. Başlıklardan sınav, soru, tam puan ve ders çıktısı eşlemesini sezer; eksikleri listeler.",
152
+ aciklama: "Excel ya da CSV'den yapıştırılan not tablosunu ÇK/PÇ hesabına hazırlar. ÖNCE ŞUNU SÖYLEYİN: öğrenci notları sohbete yapıştırılınca yapay zekâ sağlayıcısına gider; kimlik sütunu olmasa da en güvenli yol akredit.org/deneyin#ck sayfasındaki tarayıcı içi hesaplayıcıdır (veri bilgisayardan çıkmaz). Hoca yine burada isterse devam edin. Tabloda yalnız not sütunları olmalı: ad, soyad ya da numara sütunu ya da 9-11 haneli numara görülürse tablo işlenmez. Öğrencilere o1, o2 takma adı verir. Başlıklardan sınav, soru, tam puan ve ders çıktısı eşlemesini sezer; eksikleri listeler.",
149
153
  sonrakiAdim: "Eksik yoksa hocaya soru sormadan ckPcHesaplaTaslagi'ni hemen ck_pc_hesapla'ya verin. Eksik varsa (tam puan, soru-çıktı eşlemesi, sınav ağırlığı) hepsini TEK mesajda, numaralı sorun; tahmin etmeyin, sütun sütun ayrı mesaj açmayın.",
150
154
  kisiselVeriIpucu: NOT_TABLOSU_KISISEL_ILETI,
151
155
  kisiselVeriKapsami: "tam",
@@ -260,6 +264,8 @@ export const notTablosuAyristirAraci = arac({
260
264
  kaynak: [KOD_KAYNAGI("apps/acik-mcp/src/not-tablosu.ts", "Akredit not tablosu ayrıştırıcısı")],
261
265
  ...(eksikler.length
262
266
  ? {
267
+ // Eksik bilgi yalnız hocadadır: bu adımda sorulur.
268
+ hocayaSimdiSor: true,
263
269
  sonrakiAdim: "Bütün eksikleri TEK mesajda, numaralı sorun (tahmin etmeyin); yanıtları tamPuanlar, eslemeler ve agirliklar olarak verip aracı yeniden çağırın. Tabloyu yeniden yapıştırtmayın, aynı tabloyu kullanın.",
264
270
  }
265
271
  : {}),
@@ -0,0 +1,60 @@
1
+ export type ProlizBolum = "ayrinti" | "detay" | "kaynak" | "yapi" | "etkinlik" | "degerlendirme" | "isyuku" | "ciktilar" | "konular" | "katki";
2
+ /** Sayfada yazan ama içi boş değer. */
3
+ export declare const bosDeger: (s: string | undefined) => boolean;
4
+ /** Proliz ders sayfasının düz metni mi? En az üç bölüm başlığı satır olarak geçer (boş pakette de). */
5
+ export declare function prolizSayfaMetniMi(girdi: string): boolean;
6
+ export interface ProlizMetinDersi {
7
+ kod?: string;
8
+ ad?: string;
9
+ yariyil: number | null;
10
+ tul?: string;
11
+ kredi: number | null;
12
+ akts: number | null;
13
+ guncelleme?: string;
14
+ amac: string;
15
+ icerik: string;
16
+ yontem: string;
17
+ notlar: string;
18
+ ciktilar: {
19
+ kod: string;
20
+ metin: string;
21
+ }[];
22
+ degerlendirme: {
23
+ ad: string;
24
+ sayi: number | null;
25
+ katki: number | null;
26
+ }[];
27
+ /** Sayfadaki değerlendirme tablosu satırları, olduğu gibi (degerlendirmeKaniti olarak verilir). */
28
+ degerlendirmeKaniti: string;
29
+ isyuku: {
30
+ ad: string;
31
+ sayi: number | null;
32
+ sure: number | null;
33
+ toplam: number | null;
34
+ }[];
35
+ dersYapisi: {
36
+ alan: string;
37
+ yuzde: number | null;
38
+ }[];
39
+ haftaKonulari: string[];
40
+ /** Ö satırları, P sütunları, sayı 1-5 (boş hücre yok). */
41
+ katki: Record<string, Record<string, number>>;
42
+ /** "Tüm" satırı ayrı. */
43
+ katkiTum: Record<string, number>;
44
+ /** Matris başlığındaki P kodları (boş pakette de okunur). */
45
+ pKodlari: string[];
46
+ }
47
+ export interface ProlizMetinSonucu {
48
+ ders: ProlizMetinDersi;
49
+ bilgiler: Record<string, string>;
50
+ yetkililer: {
51
+ rol: string;
52
+ deger: string;
53
+ }[];
54
+ kaynaklar: string[];
55
+ /** Sayfada değeri olan alanlar. */
56
+ bulunan: string[];
57
+ bulunamayan: string[];
58
+ }
59
+ /** Proliz düz metnini bölüm bölüm okur. */
60
+ export declare function prolizMetniCoz(girdi: string): ProlizMetinSonucu;
@@ -0,0 +1,271 @@
1
+ // Proliz OBS herkese açık Bologna ders sayfasının DÜZ METNİ (tarayıcı aracının okuduğu biçim, sekmeli tablolar).
2
+ // Sayfa bölüm başlıklarıyla (Dersin Ayrıntıları, Dersin Detayları, Değerlendirme Ölçütleri, Ders Konuları...) ayrılır;
3
+ // her bölüm kendi tablo biçimine göre okunur. Bölümler arası karışma olmaz: "Ders Konuları"nın haftaları öğrenme
4
+ // çıktısı, değerlendirme kalemleri ders yapısı sayılmaz. Boş değer ("Veri yok", "Kayıt Yok", ".") boş kalır; sayfada
5
+ // olmayan hiçbir şey doldurulmaz. Ağ ve model yoktur.
6
+ /** Bölüm başlıkları; satırın tamamı (iki nokta ve sonrası hariç) eşleşir. */
7
+ /** Başlıklar küçük harfle (tr) yazılır; satır da küçültülerek eşlenir ("İ" büyük-küçük eşlemesi için). */
8
+ const BASLIKLAR = [
9
+ [/^dersin ayrıntıları$/iu, "ayrinti"],
10
+ [/^dersin detayları$/iu, "detay"],
11
+ [/^ders kaynakları$/iu, "kaynak"],
12
+ [/^ders yapısı$/iu, "yapi"],
13
+ [/^planlanan öğrenme aktiviteleri ve metodları$/iu, "etkinlik"],
14
+ [/^değerlendirme ölçütleri$/iu, "degerlendirme"],
15
+ [/^akts hesaplama içeriği$/iu, "isyuku"],
16
+ [/^dersin öğrenme çıktıları(\s*:.*)?$/iu, "ciktilar"],
17
+ [/^ders konuları$/iu, "konular"],
18
+ [/^dersin program çıktılarına katkısı$/iu, "katki"],
19
+ ];
20
+ /** Anahtar-değer bölümlerindeki bilinen etiketler (sekmesi kırpılmış boş etiket de tanınsın diye). */
21
+ const ETIKETLER = [
22
+ "Dersin Dili",
23
+ "Dersin Düzeyi",
24
+ "Bölümü / Programı",
25
+ "Öğrenim Türü",
26
+ "Dersin Türü",
27
+ "Dersin Öğretim Şekli",
28
+ "Dersin Amacı",
29
+ "Dersin İçeriği",
30
+ "Dersin Yöntem ve Teknikleri",
31
+ "Ön Koşulları",
32
+ "Dersin Koordinatörü",
33
+ "Dersi Verenler",
34
+ "Dersin Yardımcıları",
35
+ "Dersin Staj Durumu",
36
+ "Kaynaklar",
37
+ "Ders Notları",
38
+ "Dökümanlar",
39
+ "Ödevler",
40
+ "Sınavlar",
41
+ ];
42
+ const kucuk = (s) => s.toLocaleLowerCase("tr").replace(/\s+/gu, " ").trim();
43
+ const ETIKET_KUMESI = new Set(ETIKETLER.map(kucuk));
44
+ function baslikMi(satir) {
45
+ const s = kucuk(satir);
46
+ for (const [re, b] of BASLIKLAR)
47
+ if (re.test(s))
48
+ return b;
49
+ return null;
50
+ }
51
+ /** Sayfada yazan ama içi boş değer. */
52
+ export const bosDeger = (s) => {
53
+ const t = (s ?? "").replace(/\s+/gu, " ").trim();
54
+ return !t || /^(veri yok|kayıt yok|yok kayıt)\.*$/iu.test(t) || /^[.\-–]+$/u.test(t);
55
+ };
56
+ /** Proliz ders sayfasının düz metni mi? En az üç bölüm başlığı satır olarak geçer (boş pakette de). */
57
+ export function prolizSayfaMetniMi(girdi) {
58
+ if (/<\/?(table|tr|td)\b/iu.test(girdi))
59
+ return false;
60
+ const bulunan = new Set();
61
+ for (const s of girdi.split(/\r?\n/u)) {
62
+ const b = baslikMi(s);
63
+ if (b)
64
+ bulunan.add(b);
65
+ }
66
+ return bulunan.size >= 3;
67
+ }
68
+ const sayiOku = (s) => {
69
+ const m = (s ?? "").replace(",", ".").match(/-?\d+(?:\.\d+)?/u);
70
+ return m ? Number(m[0]) : null;
71
+ };
72
+ const hucreler = (s) => s.split("\t").map((x) => x.replace(/\s+/gu, " ").trim());
73
+ /** Proliz düz metnini bölüm bölüm okur. */
74
+ export function prolizMetniCoz(girdi) {
75
+ const bolumler = new Map();
76
+ let aktif = null;
77
+ for (const ham of girdi.split(/\r?\n/u)) {
78
+ const b = baslikMi(ham);
79
+ if (b) {
80
+ aktif = bolumler.get(b) ?? [];
81
+ bolumler.set(b, aktif);
82
+ continue;
83
+ }
84
+ if (/^\s*katkı düzeyi\s*:/iu.test(ham)) {
85
+ aktif = null;
86
+ continue;
87
+ }
88
+ if (aktif && ham.trim())
89
+ aktif.push(ham);
90
+ }
91
+ const satirlar = (b) => bolumler.get(b) ?? [];
92
+ // Dersin Ayrıntıları: başlık satırı ve altında değer satırı.
93
+ const ayrinti = {};
94
+ const ay = satirlar("ayrinti");
95
+ const bi = ay.findIndex((s) => /\bkodu\b/iu.test(s) && s.includes("\t"));
96
+ if (bi >= 0 && ay[bi + 1]) {
97
+ const bas = hucreler(ay[bi] ?? "");
98
+ const deg = hucreler(ay[bi + 1] ?? "");
99
+ const ALAN = [
100
+ [/^yarıyıl$|^semester$/iu, "yariyil"],
101
+ [/^kodu$|^code$/iu, "kod"],
102
+ [/^adı$|^name$/iu, "ad"],
103
+ [/^t\+u\+l$/iu, "tul"],
104
+ [/^kredi$|^credit$/iu, "kredi"],
105
+ [/^akts$|^ects$/iu, "akts"],
106
+ [/güncelleme/iu, "guncelleme"],
107
+ ];
108
+ bas.forEach((h, j) => {
109
+ const a = ALAN.find(([re]) => re.test(h))?.[1];
110
+ const v = deg[j] ?? "";
111
+ if (a && !bosDeger(v))
112
+ ayrinti[a] = v;
113
+ });
114
+ }
115
+ // Anahtar-değer bölümleri: "Etiket<TAB>değer", değerin devamı sekmesiz sonraki satırlar.
116
+ const anahtarDeger = (b) => {
117
+ const sonuc = [];
118
+ for (const s of satirlar(b)) {
119
+ const sekmeli = s.includes("\t");
120
+ const [ilk = "", ...kalan] = hucreler(s);
121
+ if (sekmeli && ilk)
122
+ sonuc.push([ilk, [kalan.join(" ").trim()]]);
123
+ else if (ETIKET_KUMESI.has(kucuk(s)))
124
+ sonuc.push([s.trim(), [""]]);
125
+ else if (sonuc.length)
126
+ sonuc.at(-1)?.[1].push(s.trim());
127
+ }
128
+ return sonuc.map(([e, d]) => {
129
+ const metin = d
130
+ .map((x) => x.trim())
131
+ .filter((x) => !bosDeger(x))
132
+ .join("\n")
133
+ .trim();
134
+ return [e, metin];
135
+ });
136
+ };
137
+ const kv = new Map();
138
+ for (const [e, d] of [...anahtarDeger("detay"), ...anahtarDeger("kaynak")])
139
+ kv.set(kucuk(e), d);
140
+ const al = (e) => kv.get(kucuk(e)) ?? "";
141
+ const bilgiler = {};
142
+ const BILGI = [
143
+ ["Dersin Dili", "dil"],
144
+ ["Dersin Düzeyi", "duzey"],
145
+ ["Bölümü / Programı", "program"],
146
+ ["Öğrenim Türü", "ogrenimTuru"],
147
+ ["Dersin Türü", "tur"],
148
+ ["Dersin Öğretim Şekli", "ogretimSekli"],
149
+ ["Dersin Staj Durumu", "stajDurumu"],
150
+ ];
151
+ for (const [e, a] of BILGI)
152
+ if (al(e))
153
+ bilgiler[a] = al(e);
154
+ const onKosul = al("Ön Koşulları")
155
+ .replace(/^\(\s*(.*?)\s*\)$/su, "$1")
156
+ .trim();
157
+ if (onKosul)
158
+ bilgiler.onKosul = onKosul;
159
+ const yetkililer = ["Dersin Koordinatörü", "Dersi Verenler", "Dersin Yardımcıları"]
160
+ .filter((e) => kv.has(kucuk(e)) && al(e))
161
+ .map((rol) => ({ rol, deger: al(rol).replace(/\n/gu, ", ") }));
162
+ const kaynaklar = al("Kaynaklar")
163
+ .split(/\n|;\s+(?=\S)/u)
164
+ .map((x) => x.replace(/^\s*(\[\d+\]|\d{1,2}[.)-]|[-•*–])\s*/u, "").trim())
165
+ .filter((x) => x.length >= 5);
166
+ // Tablo bölümleri: başlık satırı (rakamsız) atlanır, "Toplam" satırı sayılmaz.
167
+ const tabloSatirlari = (b) => satirlar(b)
168
+ .map(hucreler)
169
+ .filter((h) => h.length >= 2 && h[0] && !bosDeger(h[0]) && h.slice(1).some((x) => /\d/u.test(x)));
170
+ const TOPLAM = /^toplam/iu;
171
+ const degerlendirmeSatirlari = satirlar("degerlendirme").filter((s) => !bosDeger(s));
172
+ const degerlendirme = tabloSatirlari("degerlendirme")
173
+ .filter((h) => !TOPLAM.test(h[0] ?? ""))
174
+ .map((h) => ({ ad: h[0] ?? "", sayi: sayiOku(h[1]), katki: sayiOku(h.at(-1)) }));
175
+ const isyuku = tabloSatirlari("isyuku")
176
+ .filter((h) => !TOPLAM.test(h[0] ?? "") && h.length >= 4)
177
+ .map((h) => ({ ad: h[0] ?? "", sayi: sayiOku(h[1]), sure: sayiOku(h[2]), toplam: sayiOku(h[3]) }));
178
+ const dersYapisi = satirlar("yapi")
179
+ .map(hucreler)
180
+ .filter((h) => h.length >= 2 && h[0] && /\d/u.test(h[1] ?? ""))
181
+ .map((h) => ({ alan: h[0] ?? "", yuzde: sayiOku(h[1]) }));
182
+ const numarali = (b) => satirlar(b)
183
+ .map(hucreler)
184
+ .filter((h) => /^\d{1,2}$/u.test(h[0] ?? "") && h.slice(1).join(" ").trim())
185
+ .map((h) => h
186
+ .slice(1)
187
+ .filter((x) => !bosDeger(x))
188
+ .join(" ")
189
+ .trim())
190
+ .filter(Boolean);
191
+ const ciktilar = numarali("ciktilar").map((metin, i) => ({ kod: `Ö${i + 1}`, metin }));
192
+ const haftaKonulari = numarali("konular");
193
+ // Katkı matrisi: başlıkta P kodları; satır başı Ö kodu ya da "Tüm".
194
+ const katki = {};
195
+ const katkiTum = {};
196
+ let pKodlari = [];
197
+ let sutunlar = [];
198
+ for (const s of satirlar("katki")) {
199
+ const h = s.split("\t").map((x) => x.trim());
200
+ const ps = h.map((x, j) => ({ j, m: x.match(/^P\s*(\d{1,2})$/iu) })).filter((x) => x.m);
201
+ if (ps.length >= 2) {
202
+ sutunlar = ps.map(({ j, m }) => ({ j, p: `P${Number(m?.[1])}` }));
203
+ pKodlari = sutunlar.map((x) => x.p);
204
+ continue;
205
+ }
206
+ const m = (h[0] ?? "").match(/^(?:Ö|ÖÇ)\s*(\d{1,2})$|^(Tüm)$/iu);
207
+ if (!m || !sutunlar.length)
208
+ continue;
209
+ const deger = {};
210
+ for (const { j, p } of sutunlar) {
211
+ const v = h[j] ?? "";
212
+ if (/^[1-5]$/u.test(v))
213
+ deger[p] = Number(v);
214
+ }
215
+ if (m[2])
216
+ Object.assign(katkiTum, deger);
217
+ else
218
+ katki[`Ö${Number(m[1])}`] = deger;
219
+ }
220
+ const ders = {
221
+ ...(ayrinti.kod ? { kod: ayrinti.kod } : {}),
222
+ ...(ayrinti.ad ? { ad: ayrinti.ad } : {}),
223
+ yariyil: sayiOku(ayrinti.yariyil),
224
+ ...(ayrinti.tul ? { tul: ayrinti.tul.replace(/\s/gu, "") } : {}),
225
+ kredi: sayiOku(ayrinti.kredi),
226
+ akts: sayiOku(ayrinti.akts),
227
+ ...(ayrinti.guncelleme ? { guncelleme: ayrinti.guncelleme } : {}),
228
+ amac: al("Dersin Amacı"),
229
+ icerik: al("Dersin İçeriği"),
230
+ yontem: al("Dersin Yöntem ve Teknikleri"),
231
+ notlar: al("Ders Notları"),
232
+ ciktilar,
233
+ degerlendirme,
234
+ degerlendirmeKaniti: degerlendirme.length ? degerlendirmeSatirlari.join("\n") : "",
235
+ isyuku,
236
+ dersYapisi,
237
+ haftaKonulari,
238
+ katki,
239
+ katkiTum,
240
+ pKodlari,
241
+ };
242
+ const varMi = {
243
+ kod: Boolean(ders.kod),
244
+ ad: Boolean(ders.ad),
245
+ tul: Boolean(ders.tul),
246
+ kredi: ders.kredi !== null,
247
+ akts: ders.akts !== null,
248
+ dil: Boolean(bilgiler.dil),
249
+ tur: Boolean(bilgiler.tur),
250
+ onKosul: Boolean(bilgiler.onKosul),
251
+ amac: Boolean(ders.amac),
252
+ icerik: Boolean(ders.icerik),
253
+ yontem: Boolean(ders.yontem),
254
+ notlar: Boolean(ders.notlar),
255
+ ciktilar: ciktilar.length > 0,
256
+ haftalar: haftaKonulari.length > 0,
257
+ degerlendirme: degerlendirme.length > 0,
258
+ isyuku: isyuku.length > 0,
259
+ kaynaklar: kaynaklar.length > 0,
260
+ katki: Object.keys(katki).length > 0,
261
+ dersYapisi: dersYapisi.length > 0,
262
+ };
263
+ return {
264
+ ders,
265
+ bilgiler,
266
+ yetkililer,
267
+ kaynaklar,
268
+ bulunan: Object.keys(varMi).filter((k) => varMi[k]),
269
+ bulunamayan: Object.keys(varMi).filter((k) => !varMi[k]),
270
+ };
271
+ }
@@ -9,11 +9,11 @@ export interface AcikIstem {
9
9
  }
10
10
  /** Giriş bilgisi kuralı: bütün istemlerde aynen yer alır. */
11
11
  export declare const GIRIS_BILGISI_KURALI: string;
12
- /** Akıllı doldurma kısayolu: bütün menülerde aynı numara ve aynı anlam. */
13
- export declare const AKILLI_DOLDURMA_SECENEGI = "4) Hepsini ak\u0131ll\u0131ca doldur (\u00F6nerilen)";
14
- /** "4) Hepsini akıllıca doldur" seçildiğinde (ya da hoca "sen seç", "hepsi" derse) izlenecek yol. */
12
+ /** Akıllı doldurma kuralı: varsayılan yol; bütün istemlerde ve sunucu talimatında aynen geçer. */
13
+ export declare const AKILLI_DOLDURMA_KURALI = "Varsay\u0131lan ak\u0131ll\u0131 doldurmad\u0131r; hocaya alan alan men\u00FC sorulmaz: her alan i\u00E7in en iyisini sen se\u00E7ersin, ara\u00E7la denetlersin, ge\u00E7meyeni d\u00FCzeltip yeniden denetlersin, sonda tek \u00F6zet g\u00F6sterirsin.";
15
14
  /** Bilinmesi mümkün olmayan ve resmî taahhüt olan bilgiler: sayfadan okunur, yoksa tek mesajda sorulur. */
16
15
  export declare const TEYIT_SORULARI: string;
16
+ /** Akıllı doldurmanın (varsayılan yol) ayrıntısı. */
17
17
  export declare const AKILLI_DOLDURMA: string;
18
18
  /** Bütün istemlerde ortak çalışma biçimi. */
19
19
  export declare const ORTAK_KURALLAR: string;
@@ -55,13 +55,16 @@ export declare function odakBaslangici(no: string): {
55
55
  akis: string;
56
56
  ortakKurallar: string;
57
57
  };
58
+ export declare function niyetCoz(mesaj: string | undefined): string | null;
58
59
  export declare const akreditBaslaAraci: import("./temel.ts").AcikArac<z.ZodObject<{
60
+ kullaniciMesaji: z.ZodOptional<z.ZodString>;
59
61
  odak: z.ZodDefault<z.ZodEnum<{
60
62
  1: "1";
61
63
  2: "2";
62
64
  3: "3";
63
65
  4: "4";
64
66
  5: "5";
67
+ otomatik: "otomatik";
65
68
  menu: "menu";
66
69
  }>>;
67
70
  }, z.core.$strip>>;