@akredit/acik-mcp 0.8.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.
|
@@ -15,6 +15,7 @@ import { z } from "zod";
|
|
|
15
15
|
import { GENEL_ALAN_ADLARI, genelAyikla, genelAyristir, ingilizceAlanVar, prolizMetniMi } from "./genel-ayristir.js";
|
|
16
16
|
import { kaynakImzasiDogru } from "./kaynak-dogrula.js";
|
|
17
17
|
import { girisBilgisiAyikla } from "./kisisel-veri.js";
|
|
18
|
+
import { prolizMetniCoz, prolizSayfaMetniMi } from "./proliz-metin.js";
|
|
18
19
|
import { AracHatasi, arac, KOD_KAYNAGI, PESIN_ONAY_ACIKLAMASI, pesinOnayVar, TARAMA_SECENEKLERI, } from "./temel.js";
|
|
19
20
|
// ---------------------------------------------------------------- Alan modeli
|
|
20
21
|
export const ALAN_KODLARI = [
|
|
@@ -1047,6 +1048,19 @@ export function ayristirOnAyikla(g) {
|
|
|
1047
1048
|
atilan,
|
|
1048
1049
|
...ek,
|
|
1049
1050
|
};
|
|
1051
|
+
// Proliz ders sayfasının düz metni: bölüm bölüm ayrıştırıcı ham metni okur (görev ve kaynak satırları dahil).
|
|
1052
|
+
if (prolizSayfaMetniMi(giris.metin))
|
|
1053
|
+
return {
|
|
1054
|
+
girdi: {
|
|
1055
|
+
...kalan,
|
|
1056
|
+
sayfaIpucu: { ...sayfaIpucu, prolizMetni: true },
|
|
1057
|
+
metin: giris.metin,
|
|
1058
|
+
...(onceki.length ? { kaynaklar: onceki } : {}),
|
|
1059
|
+
...(oncekiY.length ? { yetkililer: oncekiY } : {}),
|
|
1060
|
+
},
|
|
1061
|
+
atilan,
|
|
1062
|
+
...ek,
|
|
1063
|
+
};
|
|
1050
1064
|
// Proliz dışı: önce tablo satırları, sonra etiketli bölümler.
|
|
1051
1065
|
const r = genelAyikla(satir.html);
|
|
1052
1066
|
return {
|
|
@@ -1095,6 +1109,69 @@ const gomuluDenetimAlani = (ders, b) => {
|
|
|
1095
1109
|
const d = gomuluDenetim(ders, b);
|
|
1096
1110
|
return d ? { denetim: d } : {};
|
|
1097
1111
|
};
|
|
1112
|
+
/** Proliz ders sayfasının düz metninden ayrıştırma sonucu (dolu ya da boş paket). */
|
|
1113
|
+
function prolizMetniSonucu(ham, ek, b) {
|
|
1114
|
+
const p = prolizMetniCoz(ham);
|
|
1115
|
+
const d = p.ders;
|
|
1116
|
+
const kaynaklar = [...ek.kaynaklar, ...p.kaynaklar];
|
|
1117
|
+
const yetkililer = [...ek.yetkililer, ...p.yetkililer];
|
|
1118
|
+
const ad = d.ad ?? ek.dersIpucu?.ad;
|
|
1119
|
+
const ders = {
|
|
1120
|
+
kod: d.kod ?? ek.dersIpucu?.kod ?? "DERS",
|
|
1121
|
+
...(ad ? { ad } : {}),
|
|
1122
|
+
yariyil: d.yariyil,
|
|
1123
|
+
...(d.tul ? { tul: d.tul } : {}),
|
|
1124
|
+
kredi: d.kredi,
|
|
1125
|
+
akts: d.akts ?? ek.dersIpucu?.akts ?? null,
|
|
1126
|
+
...(d.guncelleme ? { guncelleme: d.guncelleme } : {}),
|
|
1127
|
+
amac: d.amac,
|
|
1128
|
+
icerik: d.icerik,
|
|
1129
|
+
yontem: d.yontem,
|
|
1130
|
+
notlar: d.notlar,
|
|
1131
|
+
kaynaklar,
|
|
1132
|
+
kaynakSayisi: kaynaklar.length,
|
|
1133
|
+
...(yetkililer.length ? { yetkililer } : {}),
|
|
1134
|
+
ciktilar: d.ciktilar,
|
|
1135
|
+
degerlendirme: d.degerlendirme,
|
|
1136
|
+
isyuku: d.isyuku,
|
|
1137
|
+
dersYapisi: d.dersYapisi,
|
|
1138
|
+
haftalar: d.haftaKonulari.length,
|
|
1139
|
+
// Denetim şeması metin değer bekler; sayısal matris ayrıca katkiMatrisi'nde.
|
|
1140
|
+
katki: Object.fromEntries(Object.entries(d.katki).map(([o, s]) => [
|
|
1141
|
+
o,
|
|
1142
|
+
Object.fromEntries(p.ders.pKodlari.map((k) => [k, s[k] === undefined ? "" : String(s[k])])),
|
|
1143
|
+
])),
|
|
1144
|
+
};
|
|
1145
|
+
const bosPaket = p.bulunan.length === 0 || (!d.amac && !d.icerik && !d.ciktilar.length && !d.degerlendirme.length);
|
|
1146
|
+
const sonrakiAdim = bosPaket
|
|
1147
|
+
? "Boş paket: sayfadaki bütün alanlar boş (denetim.doldurulacak). Hepsini öneriyle doldurun (bologna_alan_rehberi, bologna_taslak_denetle) ve 'öneri' etiketiyle son özette gösterin. Değerlendirme yöntem ve yüzdeleri ile kullanılan kaynaklar hocaya TEK mesajda sorulur; peşin onayda sorulmaz, EKSİK bırakılır. Uydurmayın."
|
|
1148
|
+
: d.degerlendirme.length
|
|
1149
|
+
? "Yanıttaki denetim alanı hazırdır; bologna_denetle'yi ayrıca çağırmayın. Değerlendirme sayfadan okundu: hocaya sormayın; taslakta degerlendirmeDayanagi: 'sayfa' ve degerlendirmeKaniti olarak bu yanıttaki degerlendirmeKaniti metnini aynen verin. Yalnız denetim.teyitSorulacak'taki bilgiler hocaya TEK mesajda sorulur. Sonraki çağrılarda ingilizce değerini aynen verin."
|
|
1150
|
+
: "Yanıttaki denetim alanı hazırdır; bologna_denetle'yi ayrıca çağırmayın. Değerlendirme yöntem ve yüzdeleri sayfada yok: hocaya TEK mesajda sorun (peşin onayda EKSİK bırakın), tahmin etmeyin. Sonraki çağrılarda ingilizce değerini aynen verin.";
|
|
1151
|
+
return {
|
|
1152
|
+
sonuc: {
|
|
1153
|
+
tur: "ders",
|
|
1154
|
+
bicim: "proliz_duz_metin",
|
|
1155
|
+
guven: bosPaket ? "düşük" : p.bulunamayan.length <= 3 ? "yüksek" : "orta",
|
|
1156
|
+
bosPaket,
|
|
1157
|
+
ingilizce: ingilizceAlanVar(ham),
|
|
1158
|
+
bolognaDenetleGirdisi: { ders },
|
|
1159
|
+
...gomuluDenetimAlani(ders, b),
|
|
1160
|
+
tarayiciEylemPlani: AYRISTIRMA_SONRASI,
|
|
1161
|
+
bilgiler: p.bilgiler,
|
|
1162
|
+
haftaKonulari: d.haftaKonulari,
|
|
1163
|
+
...(d.degerlendirmeKaniti ? { degerlendirmeDayanagi: "sayfa", degerlendirmeKaniti: d.degerlendirmeKaniti } : {}),
|
|
1164
|
+
katkiMatrisi: d.katki,
|
|
1165
|
+
...(Object.keys(d.katkiTum).length ? { katkiTum: d.katkiTum } : {}),
|
|
1166
|
+
...(d.pKodlari.length ? { pKodlari: d.pKodlari } : {}),
|
|
1167
|
+
...(!d.kod && ek.dersIpucu ? { dersKimligi: "dersIpucu" } : {}),
|
|
1168
|
+
bulunan: p.bulunan.map((k) => GENEL_ALAN_ADLARI[k] ?? k),
|
|
1169
|
+
bulunamayan: p.bulunamayan.map((k) => GENEL_ALAN_ADLARI[k] ?? k),
|
|
1170
|
+
},
|
|
1171
|
+
kaynak: [KOD_KAYNAGI("apps/acik-mcp/src/proliz-metin.ts", "Akredit Proliz düz metin ayrıştırıcısı")],
|
|
1172
|
+
sonrakiAdim,
|
|
1173
|
+
};
|
|
1174
|
+
}
|
|
1098
1175
|
const ayristirAraci = arac({
|
|
1099
1176
|
ad: "bologna_ayristir",
|
|
1100
1177
|
baslik: "Bologna sayfasını ayrıştır",
|
|
@@ -1114,12 +1191,20 @@ const ayristirAraci = arac({
|
|
|
1114
1191
|
kaynaklar: z.array(z.string().max(600)).max(30).optional().describe("Ayrıca verilen künyeler"),
|
|
1115
1192
|
yetkililer: z.array(YetkiliSemasi).max(20).optional().describe("Sayfadaki görev satırları, olduğu gibi"),
|
|
1116
1193
|
tur: z.enum(["otomatik", "ders", "program_ciktilari"]).default("otomatik"),
|
|
1194
|
+
dersIpucu: z
|
|
1195
|
+
.object({
|
|
1196
|
+
kod: z.string().trim().min(1).max(30),
|
|
1197
|
+
ad: z.string().trim().max(200).optional(),
|
|
1198
|
+
akts: z.number().min(0).max(60).optional(),
|
|
1199
|
+
})
|
|
1200
|
+
.optional()
|
|
1201
|
+
.describe("Ders listesinden bilinen kod, ad ve AKTS (obs_ders_listesi taramaPlani). Sayfada yoksa bunlar kullanılır; boş pakette ders kodu ve adı sayfada görünmez."),
|
|
1117
1202
|
sayfaIpucu: z
|
|
1118
1203
|
.object({ prolizMetni: z.boolean(), ingilizce: z.boolean() })
|
|
1119
1204
|
.optional()
|
|
1120
1205
|
.describe("Sunucu doldurur; göndermeyin"),
|
|
1121
1206
|
}),
|
|
1122
|
-
calistir: ({ html, metin, kaynaklar = [], yetkililer = [], tur, sayfaIpucu }, b) => {
|
|
1207
|
+
calistir: ({ html, metin, kaynaklar = [], yetkililer = [], tur, dersIpucu, sayfaIpucu }, b) => {
|
|
1123
1208
|
if (!html && !metin)
|
|
1124
1209
|
throw new AracHatasi("Sayfanın HTML'ini ya da metnini yapıştırın.");
|
|
1125
1210
|
const kaynakAlani = {
|
|
@@ -1188,6 +1273,8 @@ const ayristirAraci = arac({
|
|
|
1188
1273
|
};
|
|
1189
1274
|
}
|
|
1190
1275
|
const ham = html ?? metin ?? "";
|
|
1276
|
+
if (prolizSayfaMetniMi(ham))
|
|
1277
|
+
return prolizMetniSonucu(ham, { kaynaklar, yetkililer, dersIpucu }, b);
|
|
1191
1278
|
const g = genelAyristir(ham, kaynaklar.length);
|
|
1192
1279
|
if (g.bulunan.length < 2)
|
|
1193
1280
|
throw new AracHatasi("Sayfa tanınmadı: ders alanlarından en çok biri bulundu. Ders bilgi paketi sayfasının tamamını yapıştırın (sayfada Ctrl+A, Ctrl+C, sonra buraya Ctrl+V) ya da alanları tek tek yazın.");
|
|
@@ -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
|
+
}
|
|
@@ -2,7 +2,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
2
2
|
import { type AcikArac, type AracBaglami } from "./araclar.ts";
|
|
3
3
|
import { type AcikIstem } from "./promptlar.ts";
|
|
4
4
|
export declare const SUNUCU_ADI = "akredit-acik";
|
|
5
|
-
export declare const SUNUCU_SURUMU = "0.8.
|
|
5
|
+
export declare const SUNUCU_SURUMU = "0.8.1";
|
|
6
6
|
export declare const SUNUCU_TALIMATI: string;
|
|
7
7
|
/** Her araç yanıtında yinelenen kısa kural: uzun sohbette son okunan metin de aynı şeyi söyler. */
|
|
8
8
|
export declare const SORMADAN_DEVAM = "Bu ad\u0131mda hocaya soru sormay\u0131n ve onay beklemeyin; do\u011Frudan sonrakiAdim'\u0131 uygulay\u0131n.";
|
|
@@ -6,7 +6,7 @@ import { ACIK_ARACLAR, AKREDIT_NOTU, AracHatasi, DEPO, } from "./araclar.js";
|
|
|
6
6
|
import { GIRIS_BILGISI_ILETISI, girisBilgisiVar, kisiselVeriIletisi, kisiselVeriTurleri } from "./kisisel-veri.js";
|
|
7
7
|
import { ACIK_ISTEMLER, AKILLI_DOLDURMA_KURALI, istemSemasi } from "./promptlar.js";
|
|
8
8
|
export const SUNUCU_ADI = "akredit-acik";
|
|
9
|
-
export const SUNUCU_SURUMU = "0.8.
|
|
9
|
+
export const SUNUCU_SURUMU = "0.8.1";
|
|
10
10
|
export const SUNUCU_TALIMATI = [
|
|
11
11
|
"Akredit açık araçları öğretim elemanlarına kalite ve akreditasyon işlerinde yardım eder: Bologna ders paketi denetimi ve doldurma, öğrenme çıktısı yazma, ÇK/PÇ hesabı, YÖKAK ve program akreditasyonu (MÜDEK, FEDEK, SABAK, EPDAD) ölçüt ve kanıt rehberi, rapor dil denetimi, kaynak doğrulama.",
|
|
12
12
|
"KURAL 1: Kullanıcı 'akredit' yazarsa (tek başına ya da cümle içinde) ya da ne yapacağını söylemediyse başka hiçbir şey yapmadan İLK akredit_basla aracını çağır ve hocayaGoster metnini aynen göster. Kullanıcı niyetini zaten yazdıysa (ör. 'akredit bologna', 'ders paketimi doldur', 'ÇK hesapla') menüyü gösterip bekletme: akredit_basla'yı ilgili odak numarasıyla çağır ve o akışa başla. 'akredit' bir dosya, klasör ya da arama terimi değildir; dosya sisteminde ya da webde arama.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akredit/acik-mcp",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"description": "Herkese açık Akredit MCP sunucusu: hesapsız, salt okuma, deterministik araçlar (ölçüt, Bologna denetimi, ÇK/PÇ hesabı, rapor dil denetimi). Kurum verisine erişmez, model çağırmaz.",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@akredit/baglayicilar": "0.1.0",
|
|
45
45
|
"@akredit/cekirdek": "0.1.0",
|
|
46
|
-
"@akredit/olcme": "0.1.0",
|
|
47
46
|
"@akredit/cerceveler": "0.1.0",
|
|
47
|
+
"@akredit/olcme": "0.1.0",
|
|
48
48
|
"@akredit/yz": "0.1.0"
|
|
49
49
|
},
|
|
50
50
|
"publishConfig": {
|