@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.
- package/README.md +41 -37
- package/dist/apps/acik-mcp/src/araclar.js +7 -2
- package/dist/apps/acik-mcp/src/bologna.d.ts +12 -0
- package/dist/apps/acik-mcp/src/bologna.js +580 -249
- package/dist/apps/acik-mcp/src/genel-ayristir.js +2 -2
- package/dist/apps/acik-mcp/src/http.js +2 -1
- package/dist/apps/acik-mcp/src/kaynak-dogrula.js +1 -1
- package/dist/apps/acik-mcp/src/kisisel-veri.d.ts +4 -3
- package/dist/apps/acik-mcp/src/kisisel-veri.js +5 -0
- package/dist/apps/acik-mcp/src/not-tablosu.d.ts +4 -1
- package/dist/apps/acik-mcp/src/not-tablosu.js +10 -4
- package/dist/apps/acik-mcp/src/proliz-metin.d.ts +60 -0
- package/dist/apps/acik-mcp/src/proliz-metin.js +271 -0
- package/dist/apps/acik-mcp/src/promptlar.d.ts +6 -3
- package/dist/apps/acik-mcp/src/promptlar.js +97 -48
- package/dist/apps/acik-mcp/src/sunucu.d.ts +3 -2
- package/dist/apps/acik-mcp/src/sunucu.js +16 -11
- package/dist/apps/acik-mcp/src/temel.d.ts +8 -0
- package/dist/apps/acik-mcp/src/temel.js +32 -0
- package/dist/apps/acik-mcp/src/yardim.js +46 -12
- package/dist/packages/baglayicilar/src/bologna-proliz/ayristir.js +4 -2
- package/dist/packages/cekirdek/src/bologna/taslak.js +8 -2
- package/dist/packages/cekirdek/src/karar/katman.d.ts +2 -2
- package/dist/packages/cekirdek/src/karar/katman.js +2 -2
- package/dist/packages/cekirdek/src/turkce.d.ts +0 -2
- package/dist/packages/cekirdek/src/turkce.js +0 -4
- package/dist/packages/olcme/src/girdi.d.ts +3 -0
- package/dist/packages/olcme/src/girdi.js +55 -0
- package/dist/packages/olcme/src/index.d.ts +1 -0
- package/dist/packages/olcme/src/index.js +1 -0
- package/package.json +9 -3
|
@@ -5,8 +5,9 @@
|
|
|
5
5
|
// Akredit kurallarıyla denetler ve bütün sayıları (iş yükü, toplamlar) kendisi hesaplar (ilke 2).
|
|
6
6
|
// Kurallar @akredit/cekirdek taslak ve sayfa denetiminden gelir; resmî ölçüt değildir, öyle etiketlenir.
|
|
7
7
|
// Sunucu adres çekmez (SSRF yok): Bologna sayfası yapıştırılan HTML olarak gelir, ağsız ayrıştırılır.
|
|
8
|
-
// Yetkili
|
|
9
|
-
// yayımlanmış künyedir: yazar adı künye kuralıyla serbesttir
|
|
8
|
+
// Yetkili satırları (Dersi Verenler, Koordinatör) sayfada yayımlanmış görev bilgisidir: olduğu gibi okunur, taşınır
|
|
9
|
+
// ve korunur; araçlar onları değiştirmez. Kaynaklar yayımlanmış künyedir: yazar adı künye kuralıyla serbesttir.
|
|
10
|
+
// Öğrenci ve kimlik verisi (T.C. kimlik no, öğrenci numarası, IBAN) ile giriş bilgisi reddedilir.
|
|
10
11
|
import { dersSayfasiCoz, programCiktilari, tablolar } from "../../../packages/baglayicilar/src/bologna-proliz/ayristir.js";
|
|
11
12
|
import { bolognaDenetle, taslakDenetle, } from "../../../packages/cekirdek/src/index.js";
|
|
12
13
|
import { DOGRULANMADI as DOGRULANMADI_ISARETI } from "../../../packages/cerceveler/src/index.js";
|
|
@@ -14,7 +15,8 @@ import { z } from "zod";
|
|
|
14
15
|
import { GENEL_ALAN_ADLARI, genelAyikla, genelAyristir, ingilizceAlanVar, prolizMetniMi } from "./genel-ayristir.js";
|
|
15
16
|
import { kaynakImzasiDogru } from "./kaynak-dogrula.js";
|
|
16
17
|
import { girisBilgisiAyikla } from "./kisisel-veri.js";
|
|
17
|
-
import {
|
|
18
|
+
import { prolizMetniCoz, prolizSayfaMetniMi } from "./proliz-metin.js";
|
|
19
|
+
import { AracHatasi, arac, KOD_KAYNAGI, PESIN_ONAY_ACIKLAMASI, pesinOnayVar, TARAMA_SECENEKLERI, } from "./temel.js";
|
|
18
20
|
// ---------------------------------------------------------------- Alan modeli
|
|
19
21
|
export const ALAN_KODLARI = [
|
|
20
22
|
"amac",
|
|
@@ -40,7 +42,7 @@ export const ALANLAR = {
|
|
|
40
42
|
ad: "Dersin amacı",
|
|
41
43
|
sekme: DERS_BILGILERI,
|
|
42
44
|
kurallar: [
|
|
43
|
-
"
|
|
45
|
+
"Akredit önerisi, araç denetlemez (hoca bakar): ders adıyla tutarlı olsun; başka dersten kopya olmasın.",
|
|
44
46
|
"K2: 60-120 kelime. İngilizcesi tam çeviri olsun.",
|
|
45
47
|
],
|
|
46
48
|
sinir: { enAzKelime: 60, enCokKelime: 120 },
|
|
@@ -65,7 +67,10 @@ export const ALANLAR = {
|
|
|
65
67
|
yontem: {
|
|
66
68
|
ad: "Yöntem ve teknikler",
|
|
67
69
|
sekme: DERS_BILGILERI,
|
|
68
|
-
kurallar: [
|
|
70
|
+
kurallar: [
|
|
71
|
+
"K2: 40-90 kelime.",
|
|
72
|
+
"Akredit önerisi, araç denetlemez (hoca bakar): başka dersin yöntem metniyle birebir aynı olmasın.",
|
|
73
|
+
],
|
|
69
74
|
sinir: { enAzKelime: 40, enCokKelime: 90 },
|
|
70
75
|
kaliplar: ["Ders [anlatım, tartışma, uygulama, proje] ile yürütülür. [Haftalık etkinlik] konuyu pekiştirir."],
|
|
71
76
|
yasaklar: [ORTAK_YASAK],
|
|
@@ -94,6 +99,7 @@ export const ALANLAR = {
|
|
|
94
99
|
"K4: Her çıktı ölçülebilir bir eylem fiiliyle biter (açıklar, uygular, tasarlar). 'Bilgi sahibi olur', 'öğrenir', 'kavrar' olmaz.",
|
|
95
100
|
"K4: '-ebilme/-abilme' (tasarlayabilme) ve '-ebilir' kalıpları kabul edilir. 'Sahip olur', 'farkında olur', 'haberdar olur', 'vakıf olur' ölçülemez.",
|
|
96
101
|
"K5: 4-10 çıktı geçerli, 5-8 önerilir (Akredit önerisi). Ö1'den kesintisiz. Her çıktının türü Bilgi, Beceri ya da Yetkinlik; çoğu Beceri, en az biri Yetkinlik.",
|
|
102
|
+
"K4 (uyarı): 'farkındalık' ifadesi hata sayılmaz ama ölçmesi zordur; gözlenebilir bir eylemle yazın.",
|
|
97
103
|
"İngilizcesi tam çeviri olsun.",
|
|
98
104
|
],
|
|
99
105
|
sinir: { enAzCikti: 4, enCokCikti: 10, onerilenEnAz: 5, onerilenEnCok: 8 },
|
|
@@ -102,7 +108,7 @@ export const ALANLAR = {
|
|
|
102
108
|
"[Yöntem] kullanarak [problem türünü] çözer. (Beceri)",
|
|
103
109
|
"[Ürün] tasarlar ve ekip içinde sunar. (Yetkinlik)",
|
|
104
110
|
],
|
|
105
|
-
yasaklar: ["Ölçülemeyen fiil (bilir, anlar, öğrenir,
|
|
111
|
+
yasaklar: ["Ölçülemeyen fiil (bilir, anlar, öğrenir, kavrar, bilgi sahibi olur)."],
|
|
106
112
|
mcpTasir: true,
|
|
107
113
|
},
|
|
108
114
|
degerlendirme: {
|
|
@@ -166,7 +172,7 @@ export const ALANLAR = {
|
|
|
166
172
|
kurallar: [
|
|
167
173
|
"K12: Her Ö satırı dolu; değerler 1-5 ya da boş.",
|
|
168
174
|
"Yalnız programın resmî P kodları kullanılır; P kodlarını Bologna sayfasından alın, ezberden yazmayın.",
|
|
169
|
-
"
|
|
175
|
+
"K12: En az 10 dolu hücrenin %60'tan fazlası 5 olmasın (şişirilmiş matris); ayrıştırın.",
|
|
170
176
|
"'Tüm' satırına dokunmayın.",
|
|
171
177
|
],
|
|
172
178
|
sinir: { enAz: 1, enCok: 5 },
|
|
@@ -207,7 +213,7 @@ export const ALANLAR = {
|
|
|
207
213
|
ad: "Dersin yetkilileri",
|
|
208
214
|
sekme: "Dersin Yetkilileri",
|
|
209
215
|
kurallar: [
|
|
210
|
-
"
|
|
216
|
+
"Araç denetlemez (kişisel veri taşınmaz; hoca bakar): koordinatör ve dersi veren hocanın kendisidir; başka hocanın adı yazılmaz.",
|
|
211
217
|
"Sayfada yayımlanmış görev bilgisi (Dersi Verenler, Yardımcılar, Koordinatör) olduğu gibi okunur ve korunur.",
|
|
212
218
|
],
|
|
213
219
|
kaliplar: [],
|
|
@@ -228,7 +234,7 @@ const RESMI_DAYANAK = {
|
|
|
228
234
|
durum: DOGRULANMADI_ISARETI,
|
|
229
235
|
not: "Bu kurallar Akredit'in iç kurallarıdır, resmî ölçüt değildir. Kurumunuzun Bologna ya da AKTS yönergesi farklı sınır koyabilir; o yönerge önceliklidir.",
|
|
230
236
|
};
|
|
231
|
-
const SECENEK_KURALI = "Sunucu metin üretmez.
|
|
237
|
+
const SECENEK_KURALI = "Sunucu metin üretmez. Taslağı asistan yazar: isterseniz 2-3 kısa, birbirinden gerçekten farklı aday (A, B, C) yazıp bologna_taslak_denetle ile denetleyin ve geçenlerden en iyisini kendiniz seçin; hocaya alan alan seçenek sormayın, seçileni son özette gösterin. Hocanın vermediği bilgi (kitap, araç, proje adı) uydurulmaz.";
|
|
232
238
|
const KAYNAK_KURALLAR = KOD_KAYNAGI("packages/cekirdek/src/bologna/taslak.ts", "Akredit Bologna taslak kuralları");
|
|
233
239
|
const KAYNAK_SAYFA = KOD_KAYNAGI("packages/cekirdek/src/bologna/denetim.ts", "Akredit Bologna sayfa denetim kuralları");
|
|
234
240
|
const KAYNAK_AYRISTIRICI = KOD_KAYNAGI("packages/baglayicilar/src/bologna-proliz/ayristir.ts", "Akredit Bologna (Proliz) sayfa ayrıştırıcısı");
|
|
@@ -377,6 +383,11 @@ const TaslakAlanlari = z.object({
|
|
|
377
383
|
.enum(["sayfa", "hoca", "oneri"])
|
|
378
384
|
.optional()
|
|
379
385
|
.describe("Değerlendirme yöntem ve yüzdelerinin kaynağı: 'sayfa' (OBS sayfasından okundu), 'hoca' (hoca söyledi), 'oneri' (asistan önerisi, teyitsiz). Teyitsiz yüzde OBS'ye yazılmaz."),
|
|
386
|
+
degerlendirmeKaniti: z
|
|
387
|
+
.string()
|
|
388
|
+
.max(2000)
|
|
389
|
+
.optional()
|
|
390
|
+
.describe("'sayfa' ya da 'hoca' dayanağının kanıtı: sayfadaki değerlendirme tablosunun metni ya da hocanın sohbete yazdığı yanıt, olduğu gibi (ör. 'vize 40 final 60'). Her katkı yüzdesi bu metinde geçmelidir; geçmezse yüzde teyitsiz sayılır."),
|
|
380
391
|
kaynaklar: z
|
|
381
392
|
.array(KaynakOgesi)
|
|
382
393
|
.max(30)
|
|
@@ -470,13 +481,11 @@ export function alanBulgulari(ders, alanlar, s) {
|
|
|
470
481
|
aciklama: `${i + 1}. kaynakta yıl görünmüyor. Yılı hocaya sorun ya da kaynak_bicimle ile biçimlendirin.`,
|
|
471
482
|
});
|
|
472
483
|
}
|
|
473
|
-
if (verilen.has("degerlendirme") &&
|
|
474
|
-
alanlar.degerlendirmeDayanagi !== "sayfa" &&
|
|
475
|
-
alanlar.degerlendirmeDayanagi !== "hoca")
|
|
484
|
+
if (verilen.has("degerlendirme") && !degerlendirmeTeyitli(alanlar))
|
|
476
485
|
ekle("degerlendirme", {
|
|
477
486
|
duzey: "uyari",
|
|
478
487
|
kural: "Değerlendirme",
|
|
479
|
-
aciklama: "Değerlendirme yöntem ve yüzdeleri teyitsiz
|
|
488
|
+
aciklama: "Değerlendirme yöntem ve yüzdeleri teyitsiz: dayanak 'sayfa' ya da 'hoca' değil ya da yüzdeler degerlendirmeKaniti metninde geçmiyor. Hocaya tek mesajda sorun, yanıtını degerlendirmeKaniti'ne olduğu gibi yazın; teyitsiz yüzde OBS'ye yazılmaz.",
|
|
480
489
|
kuralTuru: "Akredit önerisi",
|
|
481
490
|
dayanak: "Değişmez ilke 2-3: değerlendirme oranları resmî taahhüttür, uydurulmaz; beyan hocadadır.",
|
|
482
491
|
});
|
|
@@ -734,6 +743,99 @@ function alanDurumlari(d, sayfaBulgulari) {
|
|
|
734
743
|
kural: ALANLAR[alan].kurallar.join(" "),
|
|
735
744
|
}));
|
|
736
745
|
}
|
|
746
|
+
const BolognaDenetleGirdisi = z.object({
|
|
747
|
+
ders: BolognaDersGirdisi,
|
|
748
|
+
programCiktilari: z
|
|
749
|
+
.array(z.object({ kod: z.string().max(20), metin: z.string().max(1000) }))
|
|
750
|
+
.max(40)
|
|
751
|
+
.optional()
|
|
752
|
+
.describe("Program çıktıları; verilirse program düzeyindeki kurallar da çalışır"),
|
|
753
|
+
matrisSatiri: z
|
|
754
|
+
.record(z.string().max(20), z.string().max(5))
|
|
755
|
+
.optional()
|
|
756
|
+
.describe("Program matrisinde bu dersin satırı: program çıktısı kodu ve değer"),
|
|
757
|
+
aktsSaat: z
|
|
758
|
+
.tuple([z.number().min(1), z.number().max(60)])
|
|
759
|
+
.optional()
|
|
760
|
+
.describe("AKTS başına saat aralığı; varsayılan [25, 30]"),
|
|
761
|
+
kurumBoleni: z
|
|
762
|
+
.number()
|
|
763
|
+
.int()
|
|
764
|
+
.min(20)
|
|
765
|
+
.max(40)
|
|
766
|
+
.optional()
|
|
767
|
+
.describe("Kurumun AKTS başına saat değeri; verilirse aralık yerine bu değer kullanılır (taslak denetimiyle aynı)"),
|
|
768
|
+
});
|
|
769
|
+
/** bologna_denetle hesabı. Ayrı işlevdir: bologna_ayristir aynı denetimi gömülü döndürür (tip dökümü gerekmez). */
|
|
770
|
+
const bolognaDenetimi = ({ ders, programCiktilari, matrisSatiri, aktsSaat: aralik, kurumBoleni }, b) => {
|
|
771
|
+
const aktsSaat = kurumBoleni ? [kurumBoleni, kurumBoleni] : aralik;
|
|
772
|
+
const { amac: _a, icerik: _i, yontem: _y, notlar: _n, kaynakSayisi: _k, kaynaklar: _kk, ...sayfaAlanlari } = ders;
|
|
773
|
+
const sayfa = {
|
|
774
|
+
...sayfaAlanlari,
|
|
775
|
+
ad: ders.ad ?? ders.kod,
|
|
776
|
+
bilgiler: {},
|
|
777
|
+
};
|
|
778
|
+
const pkodlar = Object.keys(matrisSatiri ?? {});
|
|
779
|
+
const veri = {
|
|
780
|
+
adres: "kullanici-girdisi",
|
|
781
|
+
okundu: b.simdi().toISOString(),
|
|
782
|
+
programCiktilari: programCiktilari ?? [],
|
|
783
|
+
matris: {
|
|
784
|
+
pkodlar,
|
|
785
|
+
dersler: matrisSatiri
|
|
786
|
+
? [
|
|
787
|
+
{
|
|
788
|
+
kod: ders.kod,
|
|
789
|
+
ad: ders.ad ?? ders.kod,
|
|
790
|
+
zorunlu: ders.zorunlu,
|
|
791
|
+
grup: null,
|
|
792
|
+
yariyil: ders.yariyil,
|
|
793
|
+
grupBasligi: false,
|
|
794
|
+
degerler: matrisSatiri,
|
|
795
|
+
},
|
|
796
|
+
]
|
|
797
|
+
: [],
|
|
798
|
+
},
|
|
799
|
+
tyycIsaret: null,
|
|
800
|
+
dersler: [{ ...sayfa, zorunlu: ders.zorunlu, bos: false, kaynak: "kullanici-girdisi" }],
|
|
801
|
+
};
|
|
802
|
+
let bulgular = bolognaDenetle(veri, { simdi: b.simdi().getTime(), ...(aktsSaat ? { aktsSaat } : {}) }).map((x) => ({
|
|
803
|
+
...x,
|
|
804
|
+
kuralTuru: x.kural === "akts-isyuku" && kurumBoleni ? "kurum" : "Akredit önerisi",
|
|
805
|
+
dayanak: x.kural === "akts-isyuku"
|
|
806
|
+
? kurumBoleni
|
|
807
|
+
? "Kurumun verdiği AKTS başına saat değeri."
|
|
808
|
+
: "AKTS başına 25-30 saat yaygın uygulamadır; resmî metinle DOĞRULANMADI."
|
|
809
|
+
: "Akredit iç kuralı (packages/cekirdek/src/bologna/denetim.ts); resmî dayanağı DOĞRULANMADI.",
|
|
810
|
+
}));
|
|
811
|
+
// Program düzeyi kuralları yalnız program çıktıları verildiyse anlamlıdır.
|
|
812
|
+
if (!programCiktilari)
|
|
813
|
+
bulgular = bulgular.filter((x) => x.konu !== "-");
|
|
814
|
+
const alanlar = alanDurumlari(ders, bulgular);
|
|
815
|
+
const doldurulacak = alanlar.filter((a) => a.durum === "bos" || a.durum === "zayif");
|
|
816
|
+
return {
|
|
817
|
+
sonuc: {
|
|
818
|
+
ders: ders.kod,
|
|
819
|
+
hataSayisi: bulgular.filter((x) => x.duzey === "hata").length,
|
|
820
|
+
uyariSayisi: bulgular.filter((x) => x.duzey === "uyari").length,
|
|
821
|
+
ozet: doldurulacak.length
|
|
822
|
+
? `${doldurulacak.length} alan doldurulmalı ya da iyileştirilmeli: ${doldurulacak.map((a) => `${a.no}) ${a.ad}`).join(", ")}.`
|
|
823
|
+
: "Gönderilen alanlarda boş ya da zayıf alan bulunmadı.",
|
|
824
|
+
alanlar,
|
|
825
|
+
doldurulacak: doldurulacak.map((a) => a.no),
|
|
826
|
+
kalanAlanSayisi: doldurulacak.length,
|
|
827
|
+
dokunulmayacak: ["Dersin Yetkilileri sayfadaki hâliyle korunur; değiştirilmez."],
|
|
828
|
+
bulgular,
|
|
829
|
+
not: "Kurallar Akredit'in iç kurallarıdır, resmî ölçüt değildir. Düzeltme kararını ders sorumlusu ve komisyon verir.",
|
|
830
|
+
},
|
|
831
|
+
kaynak: [KAYNAK_SAYFA, KAYNAK_KURALLAR],
|
|
832
|
+
...(doldurulacak.length
|
|
833
|
+
? {}
|
|
834
|
+
: {
|
|
835
|
+
sonrakiAdim: "Eksik görünmüyor. Bunu tek cümleyle söyleyin ve sormadan kaliteyi artıracak 2-4 somut öneri verin: zayıf ya da kısa alanlar, öğrenme çıktılarında Bloom düzeyi ve ölçülebilirlik, iş yükü ile AKTS uyumu, kaynakların güncelliği, İngilizce alanlar. Altına: 1) önerileri uygulayalım (önerilen) 2) sıradaki derse geç 3) bitir.",
|
|
836
|
+
}),
|
|
837
|
+
};
|
|
838
|
+
};
|
|
737
839
|
export const bolognaDenetleAraci = arac({
|
|
738
840
|
ad: "bologna_denetle",
|
|
739
841
|
baslik: "Bologna ders paketini denetle",
|
|
@@ -741,96 +843,8 @@ export const bolognaDenetleAraci = arac({
|
|
|
741
843
|
sonrakiAdim: "Nasıl doldurulacağını hocaya sormayın; doğrudan akıllı doldurun. Sayfada değerlendirme yüzdeleri ya da kaynaklar yoksa yalnız bunları TEK mesajda sorun (yüzdeyi tahmin etmeyin). Gerisini sormadan bologna_alan_rehberi(alan='hepsi'), is_yuku_oner ve bologna_taslak_denetle ile doldurup denetleyin; sonunda bologna_kopya_metni özetini tek seferde gösterin.",
|
|
742
844
|
kisiselVeriKapsami: BOLOGNA_KAPSAMI,
|
|
743
845
|
kunyeAlanlari: BOLOGNA_KUNYE_ALANLARI,
|
|
744
|
-
girdi:
|
|
745
|
-
|
|
746
|
-
programCiktilari: z
|
|
747
|
-
.array(z.object({ kod: z.string().max(20), metin: z.string().max(1000) }))
|
|
748
|
-
.max(40)
|
|
749
|
-
.optional()
|
|
750
|
-
.describe("Program çıktıları; verilirse program düzeyindeki kurallar da çalışır"),
|
|
751
|
-
matrisSatiri: z
|
|
752
|
-
.record(z.string().max(20), z.string().max(5))
|
|
753
|
-
.optional()
|
|
754
|
-
.describe("Program matrisinde bu dersin satırı: program çıktısı kodu ve değer"),
|
|
755
|
-
aktsSaat: z
|
|
756
|
-
.tuple([z.number().min(1), z.number().max(60)])
|
|
757
|
-
.optional()
|
|
758
|
-
.describe("AKTS başına saat aralığı; varsayılan [25, 30]"),
|
|
759
|
-
kurumBoleni: z
|
|
760
|
-
.number()
|
|
761
|
-
.int()
|
|
762
|
-
.min(20)
|
|
763
|
-
.max(40)
|
|
764
|
-
.optional()
|
|
765
|
-
.describe("Kurumun AKTS başına saat değeri; verilirse aralık yerine bu değer kullanılır (taslak denetimiyle aynı)"),
|
|
766
|
-
}),
|
|
767
|
-
calistir: ({ ders, programCiktilari, matrisSatiri, aktsSaat: aralik, kurumBoleni }, b) => {
|
|
768
|
-
const aktsSaat = kurumBoleni ? [kurumBoleni, kurumBoleni] : aralik;
|
|
769
|
-
const { amac: _a, icerik: _i, yontem: _y, notlar: _n, kaynakSayisi: _k, kaynaklar: _kk, ...sayfaAlanlari } = ders;
|
|
770
|
-
const sayfa = {
|
|
771
|
-
...sayfaAlanlari,
|
|
772
|
-
ad: ders.ad ?? ders.kod,
|
|
773
|
-
bilgiler: {},
|
|
774
|
-
};
|
|
775
|
-
const pkodlar = Object.keys(matrisSatiri ?? {});
|
|
776
|
-
const veri = {
|
|
777
|
-
adres: "kullanici-girdisi",
|
|
778
|
-
okundu: b.simdi().toISOString(),
|
|
779
|
-
programCiktilari: programCiktilari ?? [],
|
|
780
|
-
matris: {
|
|
781
|
-
pkodlar,
|
|
782
|
-
dersler: matrisSatiri
|
|
783
|
-
? [
|
|
784
|
-
{
|
|
785
|
-
kod: ders.kod,
|
|
786
|
-
ad: ders.ad ?? ders.kod,
|
|
787
|
-
zorunlu: ders.zorunlu,
|
|
788
|
-
grup: null,
|
|
789
|
-
yariyil: ders.yariyil,
|
|
790
|
-
grupBasligi: false,
|
|
791
|
-
degerler: matrisSatiri,
|
|
792
|
-
},
|
|
793
|
-
]
|
|
794
|
-
: [],
|
|
795
|
-
},
|
|
796
|
-
tyycIsaret: null,
|
|
797
|
-
dersler: [{ ...sayfa, zorunlu: ders.zorunlu, bos: false, kaynak: "kullanici-girdisi" }],
|
|
798
|
-
};
|
|
799
|
-
let bulgular = bolognaDenetle(veri, { simdi: b.simdi().getTime(), ...(aktsSaat ? { aktsSaat } : {}) }).map((x) => ({
|
|
800
|
-
...x,
|
|
801
|
-
kuralTuru: x.kural === "akts-isyuku" && kurumBoleni ? "kurum" : "Akredit önerisi",
|
|
802
|
-
dayanak: x.kural === "akts-isyuku"
|
|
803
|
-
? kurumBoleni
|
|
804
|
-
? "Kurumun verdiği AKTS başına saat değeri."
|
|
805
|
-
: "AKTS başına 25-30 saat yaygın uygulamadır; resmî metinle DOĞRULANMADI."
|
|
806
|
-
: "Akredit iç kuralı (packages/cekirdek/src/bologna/denetim.ts); resmî dayanağı DOĞRULANMADI.",
|
|
807
|
-
}));
|
|
808
|
-
// Program düzeyi kuralları yalnız program çıktıları verildiyse anlamlıdır.
|
|
809
|
-
if (!programCiktilari)
|
|
810
|
-
bulgular = bulgular.filter((x) => x.konu !== "-");
|
|
811
|
-
const alanlar = alanDurumlari(ders, bulgular);
|
|
812
|
-
const doldurulacak = alanlar.filter((a) => a.durum === "bos" || a.durum === "zayif");
|
|
813
|
-
return {
|
|
814
|
-
sonuc: {
|
|
815
|
-
ders: ders.kod,
|
|
816
|
-
hataSayisi: bulgular.filter((x) => x.duzey === "hata").length,
|
|
817
|
-
uyariSayisi: bulgular.filter((x) => x.duzey === "uyari").length,
|
|
818
|
-
ozet: doldurulacak.length
|
|
819
|
-
? `${doldurulacak.length} alan doldurulmalı ya da iyileştirilmeli: ${doldurulacak.map((a) => `${a.no}) ${a.ad}`).join(", ")}.`
|
|
820
|
-
: "Gönderilen alanlarda boş ya da zayıf alan bulunmadı.",
|
|
821
|
-
alanlar,
|
|
822
|
-
doldurulacak: doldurulacak.map((a) => a.no),
|
|
823
|
-
kalanAlanSayisi: doldurulacak.length,
|
|
824
|
-
dokunulmayacak: ["Dersin Yetkilileri kişi adı içerir; hoca OBS'de kendisi yazar."],
|
|
825
|
-
bulgular,
|
|
826
|
-
not: "Kurallar Akredit'in iç kurallarıdır, resmî ölçüt değildir. Düzeltme kararını ders sorumlusu ve komisyon verir.",
|
|
827
|
-
},
|
|
828
|
-
kaynak: [KAYNAK_SAYFA, KAYNAK_KURALLAR],
|
|
829
|
-
...(doldurulacak.length
|
|
830
|
-
? {}
|
|
831
|
-
: { sonrakiAdim: "Hocaya sorun: 1) başka bir ders 2) OBS'ye kopya metni (bologna_kopya_metni) 3) bitir." }),
|
|
832
|
-
};
|
|
833
|
-
},
|
|
846
|
+
girdi: BolognaDenetleGirdisi,
|
|
847
|
+
calistir: bolognaDenetimi,
|
|
834
848
|
});
|
|
835
849
|
// ---------------------------------------------------------------- bologna_alan_rehberi
|
|
836
850
|
const alanRehberi = arac({
|
|
@@ -1034,6 +1048,19 @@ export function ayristirOnAyikla(g) {
|
|
|
1034
1048
|
atilan,
|
|
1035
1049
|
...ek,
|
|
1036
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
|
+
};
|
|
1037
1064
|
// Proliz dışı: önce tablo satırları, sonra etiketli bölümler.
|
|
1038
1065
|
const r = genelAyikla(satir.html);
|
|
1039
1066
|
return {
|
|
@@ -1048,11 +1075,108 @@ export function ayristirOnAyikla(g) {
|
|
|
1048
1075
|
...ek,
|
|
1049
1076
|
};
|
|
1050
1077
|
}
|
|
1078
|
+
/**
|
|
1079
|
+
* Ayrıştırmanın hemen ardından denetim: tarama sırasında ders başına iki çağrı yerine bir çağrı yeter.
|
|
1080
|
+
* Girdi denetimin şemasından geçmezse (sayfa eksik okunduysa) denetim atlanır, asistan bologna_denetle'yi çağırır.
|
|
1081
|
+
*/
|
|
1082
|
+
function gomuluDenetim(ders, b) {
|
|
1083
|
+
const g = BolognaDenetleGirdisi.safeParse({ ders });
|
|
1084
|
+
if (!g.success)
|
|
1085
|
+
return undefined;
|
|
1086
|
+
const s = bolognaDenetimi(g.data, b).sonuc;
|
|
1087
|
+
const d = g.data.ders;
|
|
1088
|
+
return {
|
|
1089
|
+
ozet: s.ozet,
|
|
1090
|
+
kalanAlanSayisi: s.kalanAlanSayisi,
|
|
1091
|
+
doldurulacak: s.alanlar
|
|
1092
|
+
.filter((a) => s.doldurulacak.includes(a.no))
|
|
1093
|
+
.map((a) => ({ no: a.no, ad: a.ad, durum: a.durum })),
|
|
1094
|
+
// Yalnız hocanın bilebileceği resmî bilgi sorulur; sayfada varsa sorulmaz.
|
|
1095
|
+
teyitSorulacak: [
|
|
1096
|
+
...(d.degerlendirme.length ? [] : ["Değerlendirme yöntem ve yüzdeleri"]),
|
|
1097
|
+
...(d.kaynaklar?.length || d.kaynakSayisi ? [] : ["Kullanılan kaynaklar"]),
|
|
1098
|
+
],
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
/** Ders sayfası okunduktan sonra tarayıcıda yapılacaklar; model bu turlarda sunucuya uğramaz. */
|
|
1102
|
+
const AYRISTIRMA_SONRASI = [
|
|
1103
|
+
"ADIM 1: Hocaya soru sormayın. Tarama planında ders kaldıysa ders listesine dönüp sıradakini açın.",
|
|
1104
|
+
"ADIM 2: Plan bittiyse bütün derslerin denetim özetinden TEK rapor yazın (obs_ders_listesi'nin raporBicimi); sayfada olmayan değerlendirme yüzdeleri ve kaynakları aynı mesajda sorun.",
|
|
1105
|
+
"ADIM 3: Hoca seçince taslakları bologna_alan_rehberi ve bologna_taslak_denetle ile sormadan hazırlayın; sonra bologna_kopya_metni (birden çok derste bologna_toplu_ozet) ile tek özet ve tek kayıt onayı.",
|
|
1106
|
+
"Peşin onay varsa ADIM 1-3 yerine obs_ders_listesi'nin peşin onay adımlarını izleyin: rapor durağı ve teyit sorusu yoktur.",
|
|
1107
|
+
];
|
|
1108
|
+
const gomuluDenetimAlani = (ders, b) => {
|
|
1109
|
+
const d = gomuluDenetim(ders, b);
|
|
1110
|
+
return d ? { denetim: d } : {};
|
|
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
|
+
}
|
|
1051
1175
|
const ayristirAraci = arac({
|
|
1052
1176
|
ad: "bologna_ayristir",
|
|
1053
1177
|
baslik: "Bologna sayfasını ayrıştır",
|
|
1054
1178
|
aciklama: "Yapıştırılan Bologna ders sayfasını ya da program çıktıları sayfasını çözer. Önce Proliz biçimini dener, olmazsa her OBS için genel ayrıştırıcı çalışır (HTML ya da düz metin, Türkçe ya da İngilizce etiketler). Bulunan ve bulunamayan alanları, güven düzeyini söyler. Adres çekmez.",
|
|
1055
|
-
sonrakiAdim: "
|
|
1179
|
+
sonrakiAdim: "Yanıttaki denetim alanı varsa bologna_denetle'yi ayrıca çağırmayın: eksikler (denetim.doldurulacak) ve teyit sorulacaklar (denetim.teyitSorulacak) hazırdır. Denetim alanı yoksa ders verisini bologna_denetle'ye verin (girdi hazır: bolognaDenetleGirdisi). Sonraki çağrılarda ingilizce değerini aynen verin. Sayfada değerlendirme yüzdeleri ya da kaynaklar yoksa yalnız bunları hocaya TEK mesajda sorun; yüzdeyi tahmin etmeyin. Uygulama/lab T+U+L'den okunur (U+L > 0 ise var), sorulmaz. Amaç ve içerik sayfada yoksa önden sorulmaz; 'öneri' olarak yazılıp son özette teyide sunulur.",
|
|
1056
1180
|
kisiselVeriKapsami: BOLOGNA_KAPSAMI,
|
|
1057
1181
|
kunyeAlanlari: BOLOGNA_KUNYE_ALANLARI,
|
|
1058
1182
|
onAyikla: ayristirOnAyikla,
|
|
@@ -1067,12 +1191,20 @@ const ayristirAraci = arac({
|
|
|
1067
1191
|
kaynaklar: z.array(z.string().max(600)).max(30).optional().describe("Ayrıca verilen künyeler"),
|
|
1068
1192
|
yetkililer: z.array(YetkiliSemasi).max(20).optional().describe("Sayfadaki görev satırları, olduğu gibi"),
|
|
1069
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."),
|
|
1070
1202
|
sayfaIpucu: z
|
|
1071
1203
|
.object({ prolizMetni: z.boolean(), ingilizce: z.boolean() })
|
|
1072
1204
|
.optional()
|
|
1073
1205
|
.describe("Sunucu doldurur; göndermeyin"),
|
|
1074
1206
|
}),
|
|
1075
|
-
calistir: ({ html, metin, kaynaklar = [], yetkililer = [], tur, sayfaIpucu }) => {
|
|
1207
|
+
calistir: ({ html, metin, kaynaklar = [], yetkililer = [], tur, dersIpucu, sayfaIpucu }, b) => {
|
|
1076
1208
|
if (!html && !metin)
|
|
1077
1209
|
throw new AracHatasi("Sayfanın HTML'ini ya da metnini yapıştırın.");
|
|
1078
1210
|
const kaynakAlani = {
|
|
@@ -1128,10 +1260,12 @@ const ayristirAraci = arac({
|
|
|
1128
1260
|
guven: "yüksek",
|
|
1129
1261
|
ingilizce: sayfaIpucu?.ingilizce ?? ingilizceAlanVar(html),
|
|
1130
1262
|
bolognaDenetleGirdisi: { ders },
|
|
1263
|
+
...gomuluDenetimAlani(ders, b),
|
|
1264
|
+
tarayiciEylemPlani: AYRISTIRMA_SONRASI,
|
|
1131
1265
|
bulunamayan: bulunamayan.map((k) => GENEL_ALAN_ADLARI[k] ?? k),
|
|
1132
1266
|
okunamayan: bulunamayan.length
|
|
1133
1267
|
? [
|
|
1134
|
-
"Bu alanlar sayfada görünmedi.
|
|
1268
|
+
"Bu alanlar sayfada görünmedi. Tarayıcı aracınız varsa OBS düzenleme ekranından okuyun. Yoksa önden sormayın: kaynaklar teyit sorusuyla birlikte sorulur, öbürleri 'öneri' olarak yazılıp son özette teyide sunulur.",
|
|
1135
1269
|
]
|
|
1136
1270
|
: [],
|
|
1137
1271
|
},
|
|
@@ -1139,6 +1273,8 @@ const ayristirAraci = arac({
|
|
|
1139
1273
|
};
|
|
1140
1274
|
}
|
|
1141
1275
|
const ham = html ?? metin ?? "";
|
|
1276
|
+
if (prolizSayfaMetniMi(ham))
|
|
1277
|
+
return prolizMetniSonucu(ham, { kaynaklar, yetkililer, dersIpucu }, b);
|
|
1142
1278
|
const g = genelAyristir(ham, kaynaklar.length);
|
|
1143
1279
|
if (g.bulunan.length < 2)
|
|
1144
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.");
|
|
@@ -1155,13 +1291,15 @@ const ayristirAraci = arac({
|
|
|
1155
1291
|
guven: g.guven,
|
|
1156
1292
|
ingilizce: sayfaIpucu?.ingilizce ?? ingilizceAlanVar(ham),
|
|
1157
1293
|
bolognaDenetleGirdisi: { ders },
|
|
1294
|
+
...gomuluDenetimAlani(ders, b),
|
|
1295
|
+
tarayiciEylemPlani: AYRISTIRMA_SONRASI,
|
|
1158
1296
|
bilgiler: g.bilgiler,
|
|
1159
1297
|
haftaKonulari,
|
|
1160
1298
|
bulunan: g.bulunan.map((k) => GENEL_ALAN_ADLARI[k] ?? k),
|
|
1161
1299
|
bulunamayan: g.bulunamayan.map((k) => GENEL_ALAN_ADLARI[k] ?? k),
|
|
1162
1300
|
not: g.guven === "yüksek"
|
|
1163
|
-
? "Genel ayrıştırıcı alanların çoğunu buldu.
|
|
1164
|
-
: "Genel ayrıştırıcı bazı alanları bulamadı. Bulunanları
|
|
1301
|
+
? "Genel ayrıştırıcı alanların çoğunu buldu. Okunan değerleri son özette 'sayfa' dayanağıyla gösterin; hoca orada doğrular."
|
|
1302
|
+
: "Genel ayrıştırıcı bazı alanları bulamadı. Bulunanları son özette gösterin; bulunamayanları tarayıcıyla OBS düzenleme ekranından okuyun ya da 'öneri' olarak yazıp son özette teyide sunun. Yalnız değerlendirme ve kaynaklar hocaya sorulur.",
|
|
1165
1303
|
},
|
|
1166
1304
|
kaynak: [KOD_KAYNAGI("apps/acik-mcp/src/genel-ayristir.ts", "Akredit genel Bologna ayrıştırıcısı")],
|
|
1167
1305
|
};
|
|
@@ -1183,8 +1321,8 @@ const ZORUNLU_ALANLAR = [
|
|
|
1183
1321
|
const durumAraci = arac({
|
|
1184
1322
|
ad: "bologna_durum",
|
|
1185
1323
|
baslik: "Bologna paketi ne kadar tamam",
|
|
1186
|
-
aciklama: "
|
|
1187
|
-
sonrakiAdim: "Kalan
|
|
1324
|
+
aciklama: "Hazırlanan taslak alanların bütününe bakar: hangi zorunlu alan tamam, hangisi eksik ya da kurala uymuyor, kaç alan kaldı. Eksik kalmadıysa biçim denetiminden geçti der. Bütün alanlar yazılınca, OBS'ye yazmadan önce çağrılır.",
|
|
1325
|
+
sonrakiAdim: "Kalan alanları hocaya sormadan tamamlayın ya da düzeltin (bologna_alan_rehberi, bologna_taslak_denetle) ve yeniden denetleyin; ara rapor vermeyin. Yalnız hocanın bilebileceği resmî bilgi (değerlendirme, kullanılan kaynak) eksikse onu TEK mesajda sorun. Kalan yoksa OBS'ye yazma adımına geçin.",
|
|
1188
1326
|
kisiselVeriKapsami: BOLOGNA_KAPSAMI,
|
|
1189
1327
|
kunyeAlanlari: BOLOGNA_KUNYE_ALANLARI,
|
|
1190
1328
|
girdi: z.object({
|
|
@@ -1212,9 +1350,7 @@ const durumAraci = arac({
|
|
|
1212
1350
|
const kalan = satirlar.filter((s) => s.durum !== "tamam");
|
|
1213
1351
|
const tamamlandi = kalan.length === 0 && genel.length === 0;
|
|
1214
1352
|
const teyitBekleyen = [
|
|
1215
|
-
...(alanlar.degerlendirme && alanlar
|
|
1216
|
-
? ["Değerlendirme yöntem ve yüzdeleri"]
|
|
1217
|
-
: []),
|
|
1353
|
+
...(alanlar.degerlendirme && !degerlendirmeTeyitli(alanlar) ? ["Değerlendirme yöntem ve yüzdeleri"] : []),
|
|
1218
1354
|
];
|
|
1219
1355
|
return {
|
|
1220
1356
|
sonuc: {
|
|
@@ -1478,7 +1614,7 @@ const obsKarsilastirAraci = arac({
|
|
|
1478
1614
|
ad: "obs_karsilastir",
|
|
1479
1615
|
baslik: "OBS'ye yazılanı geri okuyup doğrula",
|
|
1480
1616
|
aciklama: "OBS'ye kaydettikten sonra sayfa geri okunur; bu araç beklenen alanları (bologna_kopya_metni'nin obsKarsilastirGirdisi) geri okunan sayfa metniyle karşılaştırır ve farkları döndürür. Ders kodu ya da dönem değişmişse dur der. OBS'ye yazmaz.",
|
|
1481
|
-
sonrakiAdim: "dur true ise yazmayı hemen bırakın ve hocaya nedenini söyleyin. Farklı alan varsa
|
|
1617
|
+
sonrakiAdim: "dur true ise yazmayı hemen bırakın, toplu kayıttaysa sonraki derslere de geçmeyin ve hocaya nedenini söyleyin. Farklı alan varsa önce sormadan bir kez toparlayın: o sekmenin Kaydet'ine basılmamışsa basın ya da onaylanan AYNI değeri yeniden yazın, sonra geri okuyup yeniden karşılaştırın. Metni kısaltmak ya da değiştirmek gerekiyorsa bu yeni içeriktir: hocaya alan alan gösterip onay alın. Hepsi eşleştiyse 'Kaydedildi ve geri okunarak doğrulandı' deyin ve toplu kayıttaysa sormadan sonraki derse geçin. Onaya Gönder'e hoca basar.",
|
|
1482
1618
|
kisiselVeriKapsami: BOLOGNA_KAPSAMI,
|
|
1483
1619
|
kunyeAlanlari: ["beklenen"],
|
|
1484
1620
|
onAyikla: (g) => {
|
|
@@ -1512,6 +1648,7 @@ const obsKarsilastirAraci = arac({
|
|
|
1512
1648
|
return {
|
|
1513
1649
|
sonuc,
|
|
1514
1650
|
kaynak: [KOD_KAYNAGI("apps/acik-mcp/src/bologna.ts", "Akredit OBS geri okuma karşılaştırması")],
|
|
1651
|
+
hocayaSimdiSor: sonuc.dur,
|
|
1515
1652
|
...(sonuc.dur
|
|
1516
1653
|
? {
|
|
1517
1654
|
sonrakiAdim: "Yazmayı durdurun: ders kodu ya da dönem beklenenden farklı. Hocaya nedenini söyleyin, başka alana yazmayın.",
|
|
@@ -1529,165 +1666,359 @@ const SEKME_SIRASI = [
|
|
|
1529
1666
|
"Sür. Kalkınma Amaçları",
|
|
1530
1667
|
"Diğer Kaynaklar",
|
|
1531
1668
|
];
|
|
1669
|
+
/**
|
|
1670
|
+
* Değerlendirme yüzdesi kod kapısı (ilke 2-3): dayanak 'sayfa' ya da 'hoca' olsa da her katkı yüzdesi verilen
|
|
1671
|
+
* kanıt metninde (sayfa tablosu ya da hocanın yanıtı) ayrı bir sayı olarak geçmelidir. Model beyanına güvenilmez.
|
|
1672
|
+
*/
|
|
1673
|
+
export function degerlendirmeTeyitli(a) {
|
|
1674
|
+
if (a.degerlendirmeDayanagi !== "sayfa" && a.degerlendirmeDayanagi !== "hoca")
|
|
1675
|
+
return false;
|
|
1676
|
+
if (!a.degerlendirme?.length)
|
|
1677
|
+
return true;
|
|
1678
|
+
const sayilar = new Set((a.degerlendirmeKaniti ?? "").match(/\d+(?:[.,]\d+)?/gu)?.map((x) => Number(x.replace(",", "."))));
|
|
1679
|
+
return a.degerlendirme.every((x) => sayilar.has(x.katki));
|
|
1680
|
+
}
|
|
1681
|
+
/** Peşin onay varken yazma adımı: durak yok, sonda tek rapor. */
|
|
1682
|
+
const PESIN_ONAY_YAZMA = "Peşin onay var (pesinOnayKaydi): hocaya sormadan yalnız bu paketteki alanları OBS'ye yazın, Kaydet'e basın, sayfayı geri okuyup obs_karsilastir ile doğrulayın. yazilamayan ve kalanEksikler'i uydurmayın, boş bırakın. Başka ders kaldıysa sormadan ona geçin. Hepsi bitince TEK rapor: ders ders yazılan alanlar, 'EKSİK' bırakılanlar (değerlendirme yüzdesi, kaynak), geri okuma sonucu, ve 'Onaya Gönder'e siz basarsınız; itirazınız olan alanı OBS'de düzeltin ya da bana numarasını yazın.' dur/fark çıkarsa o derste durun, rapora yazın.";
|
|
1683
|
+
const KopyaMetniGirdisi = z.object({
|
|
1684
|
+
ders: DersTemeli,
|
|
1685
|
+
alanlar: TaslakAlanlari,
|
|
1686
|
+
programCiktilari: z.array(z.string().max(10)).max(40).optional(),
|
|
1687
|
+
ingilizce: z.boolean().default(false).describe("bologna_ayristir'in ingilizce değeri"),
|
|
1688
|
+
oncekiDegerler: z
|
|
1689
|
+
.record(z.string().max(120), z.string().max(20_000))
|
|
1690
|
+
.optional()
|
|
1691
|
+
.describe("OBS'deki şimdiki değerler (alan adı, ör. 'Dersin Amacı (Türkçe)' → değer); önceki/yeni özeti için"),
|
|
1692
|
+
pesinOnay: z.string().max(500).optional().describe(PESIN_ONAY_ACIKLAMASI),
|
|
1693
|
+
});
|
|
1694
|
+
/** bologna_kopya_metni hesabı. Ayrı işlevdir: bologna_toplu_ozet her ders için aynı kapıları çalıştırır. */
|
|
1695
|
+
const kopyaPaketi = ({ ders, alanlar: gelen, programCiktilari, ingilizce, oncekiDegerler, pesinOnay, }) => {
|
|
1696
|
+
const pesin = pesinOnayVar(pesinOnay);
|
|
1697
|
+
// Yazma paketi kapısı: doğrulanmamış kaynak ve teyitsiz değerlendirme yüzdesi pakete giremez (ilke 1-3).
|
|
1698
|
+
const yazilamayan = [];
|
|
1699
|
+
const kaynaklar = gelen.kaynaklar?.filter((k, i) => {
|
|
1700
|
+
if (kaynakDurumu(k) !== "dogrulanmamis")
|
|
1701
|
+
return true;
|
|
1702
|
+
yazilamayan.push({
|
|
1703
|
+
alan: "Kaynaklar",
|
|
1704
|
+
neden: `${i + 1}. kaynak doğrulanmamış (katalogda bulunmadı ya da hoca beyanı yok); OBS'ye yazılmaz.`,
|
|
1705
|
+
});
|
|
1706
|
+
return false;
|
|
1707
|
+
});
|
|
1708
|
+
const teyitli = degerlendirmeTeyitli(gelen);
|
|
1709
|
+
if (gelen.degerlendirme && !teyitli)
|
|
1710
|
+
yazilamayan.push({
|
|
1711
|
+
alan: "Değerlendirme",
|
|
1712
|
+
neden: "Değerlendirme yöntem ve yüzdeleri teyitsiz (sayfadan okunmadı, hoca söylemedi ya da yüzdeler degerlendirmeKaniti metninde yok); OBS'ye yazılmaz. Hocaya sorun.",
|
|
1713
|
+
});
|
|
1714
|
+
const { degerlendirme: _d, kaynaklar: _k, ...kalanAlanlar } = gelen;
|
|
1715
|
+
const alanlar = {
|
|
1716
|
+
...kalanAlanlar,
|
|
1717
|
+
...(gelen.degerlendirme && teyitli ? { degerlendirme: gelen.degerlendirme } : {}),
|
|
1718
|
+
...(kaynaklar ? { kaynaklar } : {}),
|
|
1719
|
+
};
|
|
1720
|
+
const bolumler = [];
|
|
1721
|
+
const ekle = (sekme, alan, deger) => bolumler.push({ sira: bolumler.length + 1, sekme, alan, deger });
|
|
1722
|
+
const hesap = bolognaHesabi(ders, alanlar);
|
|
1723
|
+
const iki = (ad, m) => {
|
|
1724
|
+
if (!m)
|
|
1725
|
+
return;
|
|
1726
|
+
ekle(DERS_BILGILERI, `${ad} (Türkçe)`, m.tr.trim());
|
|
1727
|
+
if (m.en?.trim())
|
|
1728
|
+
ekle(DERS_BILGILERI, `${ad} (İngilizce)`, m.en.trim());
|
|
1729
|
+
};
|
|
1730
|
+
iki("Dersin Amacı", alanlar.amac);
|
|
1731
|
+
iki("Dersin İçeriği", alanlar.icerik);
|
|
1732
|
+
iki("Yöntem ve Teknikler", alanlar.yontem);
|
|
1733
|
+
iki("Ders Notları", alanlar.notlar);
|
|
1734
|
+
if (alanlar.onKosul !== undefined)
|
|
1735
|
+
ekle(DERS_BILGILERI, "Ön Koşul", alanlar.onKosul.trim());
|
|
1736
|
+
if (alanlar.degerlendirme)
|
|
1737
|
+
ekle(DERS_BILGILERI, "Değerlendirme (ad, sayı, katkı %)", [
|
|
1738
|
+
...alanlar.degerlendirme.map((x) => `${x.ad}\t${x.sayi}\t${x.katki}`),
|
|
1739
|
+
`Toplam\t\t${hesap.degerlendirmeToplami}`,
|
|
1740
|
+
].join("\n"));
|
|
1741
|
+
if (hesap.isyuku)
|
|
1742
|
+
ekle(DERS_BILGILERI, "AKTS İş Yükü (ad, sayı, süre, toplam saat)", [
|
|
1743
|
+
...hesap.isyuku.satirlar.map((x) => `${x.ad}\t${x.sayi}\t${x.sure}\t${x.toplam}`),
|
|
1744
|
+
`Toplam İş Yükü\t\t\t${hesap.isyuku.toplam}`,
|
|
1745
|
+
...(hesap.isyuku.hedef !== null ? [`AKTS\t\t\t${ders.akts}`] : []),
|
|
1746
|
+
].join("\n"));
|
|
1747
|
+
if (alanlar.dersYapisi)
|
|
1748
|
+
ekle(DERS_BILGILERI, "Ders Yapısı (alan, %)", alanlar.dersYapisi.map((x) => `${x.alan}\t${x.yuzde}`).join("\n"));
|
|
1749
|
+
if (alanlar.ciktilar)
|
|
1750
|
+
ekle("Öğrenme Çıktıları", "Öğrenme çıktıları (kod, tür, Türkçe, İngilizce)", alanlar.ciktilar.map((c) => `${c.kod}\t${c.tur}\t${c.tr.trim()}\t${c.en?.trim() ?? ""}`).join("\n"));
|
|
1751
|
+
if (alanlar.haftalar)
|
|
1752
|
+
ekle("Ders Akışı", "Haftalar (hafta, konu, İngilizce konu, ön hazırlık, dokümanlar)", alanlar.haftalar
|
|
1753
|
+
.map((h) => `${h.hafta}\t${h.konu.tr.trim()}\t${h.konu.en?.trim() ?? ""}\t${h.onHazirlik.trim()}\t${h.dokuman.trim()}`)
|
|
1754
|
+
.join("\n"));
|
|
1755
|
+
if (alanlar.katki) {
|
|
1756
|
+
const p = programCiktilari?.length
|
|
1757
|
+
? programCiktilari
|
|
1758
|
+
: [...new Set(Object.values(alanlar.katki).flatMap((s) => Object.keys(s)))];
|
|
1759
|
+
ekle("Prg.Çıktısına Katkısı", "Katkı tablosu ('Tüm' satırına dokunmayın)", [
|
|
1760
|
+
`\t${p.join("\t")}`,
|
|
1761
|
+
...Object.entries(alanlar.katki).map(([o, s]) => `${o}\t${p.map((k) => s[k] ?? "").join("\t")}`),
|
|
1762
|
+
].join("\n"));
|
|
1763
|
+
}
|
|
1764
|
+
if (alanlar.ska)
|
|
1765
|
+
ekle("Sür. Kalkınma Amaçları", "Seçilecek amaç numaraları", alanlar.ska.join(", "));
|
|
1766
|
+
if (alanlar.kaynaklar?.length)
|
|
1767
|
+
ekle("Diğer Kaynaklar", "Kaynaklar (her satır bir künye)", alanlar.kaynaklar.map(kunyeMetni).join("\n"));
|
|
1768
|
+
if (!bolumler.length && !yazilamayan.length)
|
|
1769
|
+
throw new AracHatasi("Kopyalanacak alan yok. En az bir alan gönderin.");
|
|
1770
|
+
const b = alanBulgulari(ders, alanlar, { ingilizce, programCiktilari });
|
|
1771
|
+
const kalanEksikler = [...b.entries()].flatMap(([alan, xs]) => xs.filter((x) => x.duzey === "hata").map((x) => ({ alan, neden: x.aciklama })));
|
|
1772
|
+
const baslik = [ders.kod, ders.ad].filter(Boolean).join(" ") || "Ders";
|
|
1773
|
+
const sirali = [...bolumler].sort((x, y) => SEKME_SIRASI.indexOf(x.sekme) - SEKME_SIRASI.indexOf(y.sekme) || x.sira - y.sira);
|
|
1774
|
+
const metin = [
|
|
1775
|
+
`${baslik}: OBS'ye kopyalanacak alanlar`,
|
|
1776
|
+
"",
|
|
1777
|
+
...sirali.flatMap((x, i) => [`${i + 1}. ${x.sekme} > ${x.alan}`, x.deger, ""]),
|
|
1778
|
+
...(ders.yetkililer?.length
|
|
1779
|
+
? [
|
|
1780
|
+
"Dersin Yetkilileri (sayfadaki hâliyle; değiştirmeyin, OBS'de zaten böyle kayıtlı):",
|
|
1781
|
+
...ders.yetkililer.map((y) => `${y.rol}: ${y.deger}`),
|
|
1782
|
+
]
|
|
1783
|
+
: ["Dersin Yetkilileri: bu metin onlara dokunmaz; OBS'deki hâliyle kalır."]),
|
|
1784
|
+
...(kalanEksikler.length
|
|
1785
|
+
? [
|
|
1786
|
+
"",
|
|
1787
|
+
"Kalan eksikler:",
|
|
1788
|
+
...kalanEksikler.map((e) => `- ${ALANLAR[e.alan]?.ad ?? "Genel"}: ${e.neden}`),
|
|
1789
|
+
]
|
|
1790
|
+
: []),
|
|
1791
|
+
...(yazilamayan.length
|
|
1792
|
+
? ["", "Yazma paketine alınmayanlar:", ...yazilamayan.map((y) => `- ${y.alan}: ${y.neden}`)]
|
|
1793
|
+
: []),
|
|
1794
|
+
].join("\n");
|
|
1795
|
+
const numarali = sirali.map((x, i) => ({ ...x, sira: i + 1 }));
|
|
1796
|
+
const normal = (v) => v.replace(/\s+/gu, " ").trim();
|
|
1797
|
+
const degisiklikOzeti = numarali.map((x) => {
|
|
1798
|
+
const onceki = oncekiDegerler?.[x.alan];
|
|
1799
|
+
return {
|
|
1800
|
+
no: x.sira,
|
|
1801
|
+
sekme: x.sekme,
|
|
1802
|
+
alan: x.alan,
|
|
1803
|
+
onceki: onceki === undefined ? null : onceki,
|
|
1804
|
+
yeni: x.deger,
|
|
1805
|
+
degisim: onceki === undefined ? "bilinmiyor" : normal(onceki) === normal(x.deger) ? "aynı" : "değişecek",
|
|
1806
|
+
};
|
|
1807
|
+
});
|
|
1808
|
+
return {
|
|
1809
|
+
sonuc: {
|
|
1810
|
+
metin,
|
|
1811
|
+
bolumler: numarali,
|
|
1812
|
+
degisiklikOzeti,
|
|
1813
|
+
yazilamayan,
|
|
1814
|
+
hesap,
|
|
1815
|
+
kalanEksikler,
|
|
1816
|
+
obsKarsilastirGirdisi: {
|
|
1817
|
+
...(ders.kod ? { dersKodu: ders.kod } : {}),
|
|
1818
|
+
...(ders.donem ? { donem: ders.donem } : {}),
|
|
1819
|
+
beklenen: numarali.map((x) => ({ alan: x.alan, deger: x.deger })),
|
|
1820
|
+
},
|
|
1821
|
+
...(pesin ? { pesinOnayKaydi: pesinOnay } : {}),
|
|
1822
|
+
not: `Biçim denetiminden geçti; içerik teyidi: hoca. ${pesin
|
|
1823
|
+
? "Peşin onay var: degisiklikOzeti'ni sondaki tek raporda gösterin."
|
|
1824
|
+
: "Yazmadan önce degisiklikOzeti'ni (önceki/yeni) hocaya alan alan gösterin; hoca bir kez 'kaydet' der."} Kayıttan sonra sayfayı geri okuyup obs_karsilastir ile doğrulayın. Alan adları OBS sürümüne göre küçük farklar gösterebilir.`,
|
|
1825
|
+
},
|
|
1826
|
+
kaynak: [KAYNAK_KURALLAR],
|
|
1827
|
+
...(pesin
|
|
1828
|
+
? {
|
|
1829
|
+
hocayaSimdiSor: false,
|
|
1830
|
+
sonrakiAdim: PESIN_ONAY_YAZMA,
|
|
1831
|
+
}
|
|
1832
|
+
: pesinOnay
|
|
1833
|
+
? {
|
|
1834
|
+
sonrakiAdim: "pesinOnay metninde kayıt sözü yok ya da söz olumsuz; peşin onay sayılmadı. degisiklikOzeti'ni gösterip kayıt onayını isteyin: 1) OBS'ye yaz ve kaydet 2) formu doldur, Kaydet'e ben basayım 3) kopya metni.",
|
|
1835
|
+
}
|
|
1836
|
+
: {}),
|
|
1837
|
+
...(kalanEksikler.length && !pesin
|
|
1838
|
+
? {
|
|
1839
|
+
sonrakiAdim: "Kalan eksikleri sormadan doldurmaya dönün (bologna_alan_rehberi, bologna_taslak_denetle); yalnız hocanın bilebileceği resmî bilgi (değerlendirme, kullanılan kaynak) eksikse onu TEK mesajda, numaralı sorun. Sonra degisiklikOzeti ile kayıt onayını aynı mesajda isteyin.",
|
|
1840
|
+
}
|
|
1841
|
+
: {}),
|
|
1842
|
+
};
|
|
1843
|
+
};
|
|
1532
1844
|
const kopyaMetniAraci = arac({
|
|
1533
1845
|
ad: "bologna_kopya_metni",
|
|
1534
1846
|
baslik: "OBS'ye kopyalanacak metin",
|
|
1535
|
-
|
|
1536
|
-
|
|
1847
|
+
hocayaSimdiSor: true,
|
|
1848
|
+
aciklama: "Hocanın seçtiği alanları OBS sekmesi ve alan adıyla sıralı, alan alan kopyalanacak düz metin olarak verir. Tablolar sekmeyle ayrılır. Toplamları kendisi hesaplar ve kalan eksikleri yazar. Doğrulanmamış kaynağı ve teyitsiz değerlendirme yüzdesini pakete almaz. Sunucu OBS'ye yazmaz.",
|
|
1849
|
+
sonrakiAdim: "Metnin tamamını basmayın. Yalnız degisiklikOzeti'ni tablo olarak gösterin (Alan | Önceki | Yeni, ilk cümle | Dayanak: sayfa/hoca/öneri) ve AYNI mesajda sorun: 1) OBS'ye yaz ve kaydet (önerilen; bu seçim özete 'kaydet' onayıdır) 2) formu doldur, Kaydet'e ben basayım 3) alan alan kopya metnini ver. Değiştirmek istediği alan varsa numarasını yazar. Tarayıcı aracınız yoksa yalnız 3'ü uygulayın ve metni kod bloğunda verin. Birden çok ders taranıyorsa tüm derslerin özetini tek tabloda gösterip tek onay alın.",
|
|
1537
1850
|
kisiselVeriKapsami: BOLOGNA_KAPSAMI,
|
|
1538
1851
|
kunyeAlanlari: BOLOGNA_KUNYE_ALANLARI,
|
|
1852
|
+
girdi: KopyaMetniGirdisi,
|
|
1853
|
+
calistir: kopyaPaketi,
|
|
1854
|
+
});
|
|
1855
|
+
/** OBS'de ders listesi açılınca: hocaya "hangi ders?" sordurmaz, tarama planını ve rapor biçimini verir. */
|
|
1856
|
+
const dersListesiAraci = arac({
|
|
1857
|
+
ad: "obs_ders_listesi",
|
|
1858
|
+
baslik: "OBS ders listesini tara",
|
|
1859
|
+
aciklama: "OBS ya da Bologna sisteminde hocanın ders listesi açılınca, hocaya 'hangi ders?' diye SORMADAN İLK bu aracı çağır: listedeki ders kodlarını ve adlarını ver. Tarama sırasını, hocaya gösterilecek kısa bilgi cümlesini ve sonuç raporunun biçimini döndürür. OBS'ye yazmaz, adres açmaz.",
|
|
1860
|
+
sonrakiAdim: "Hocaya hocayaGoster cümlesini yazın ve hiçbir şey sormadan tarayiciEylemPlani'nı uygulayın: taramaPlani sırasıyla her dersin Bologna sayfasını açıp bologna_ayristir'a verin (yanıttaki denetim eksikleri sayar). Bitince raporBicimi'ne göre TEK mesaj yazın.",
|
|
1861
|
+
kisiselVeriKapsami: "tam",
|
|
1539
1862
|
girdi: z.object({
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
.
|
|
1547
|
-
.
|
|
1863
|
+
dersler: z
|
|
1864
|
+
.array(z.object({ kod: z.string().trim().min(1).max(30), ad: z.string().trim().max(160).optional() }))
|
|
1865
|
+
.min(1)
|
|
1866
|
+
.max(60)
|
|
1867
|
+
.describe("Listede görünen dersler: kod ve ad (her çağrıda listenin tamamı)"),
|
|
1868
|
+
baslangic: z
|
|
1869
|
+
.number()
|
|
1870
|
+
.int()
|
|
1871
|
+
.min(0)
|
|
1872
|
+
.max(59)
|
|
1873
|
+
.default(0)
|
|
1874
|
+
.describe("Taranacak dilimin ilk dersinin sırası (0'dan). İlk çağrıda 0; sonraki dilimde yanıttaki sonrakiBaslangic."),
|
|
1875
|
+
pesinOnay: z.string().max(500).optional().describe(PESIN_ONAY_ACIKLAMASI),
|
|
1548
1876
|
}),
|
|
1549
|
-
calistir: ({
|
|
1550
|
-
|
|
1551
|
-
const
|
|
1552
|
-
const
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1877
|
+
calistir: ({ dersler, baslangic, pesinOnay }) => {
|
|
1878
|
+
const pesin = pesinOnayVar(pesinOnay);
|
|
1879
|
+
const DILIM = 15;
|
|
1880
|
+
const plan = dersler.slice(baslangic, baslangic + DILIM);
|
|
1881
|
+
const sonraki = baslangic + plan.length;
|
|
1882
|
+
const kalan = dersler.length - sonraki;
|
|
1883
|
+
const hepsi = baslangic === 0 && kalan === 0;
|
|
1884
|
+
return {
|
|
1885
|
+
sonuc: {
|
|
1886
|
+
hocayaGoster: pesin
|
|
1887
|
+
? baslangic === 0
|
|
1888
|
+
? `${dersler.length} dersiniz var. Hepsini tarayıp eksikleri dolduracak ve OBS'ye kaydedeceğim; bilmediğim resmî bilgiyi boş bırakacağım. Bitince tek rapor vereceğim. Vazgeçmek için 'dur' yazın.`
|
|
1889
|
+
: ""
|
|
1890
|
+
: baslangic === 0
|
|
1891
|
+
? `${dersler.length} dersiniz var. ${hepsi ? "Hepsini" : `${DILIM}'erli dilimlerle hepsini`} tarıyorum; eksikleri ve önerilerimi birazdan tek listede göstereceğim.`
|
|
1892
|
+
: `Sıradaki ${plan.length} dersi tarıyorum (${baslangic + 1}-${sonraki} / ${dersler.length}).`,
|
|
1893
|
+
taramaPlani: plan.map((d, i) => ({ sira: baslangic + i + 1, kod: d.kod, ad: d.ad ?? "" })),
|
|
1894
|
+
// Model tarayıcı turlarında sunucuya uğramaz; adımlar emir kipinde verilir ki ara soru doğmasın.
|
|
1895
|
+
tarayiciEylemPlani: [
|
|
1896
|
+
`ADIM 1: Hocaya soru sormayın. Listede '${plan[0]?.kod ?? ""}' satırının düzenleme/ders bilgi paketi bağlantısına tıklayın.`,
|
|
1897
|
+
"ADIM 2: Açılan sayfanın tamamını (metin ya da HTML) okuyup bologna_ayristir'a verin; yanıttaki 'denetim' alanı eksikleri sayar, bologna_denetle'yi ayrıca çağırmayın.",
|
|
1898
|
+
"ADIM 3: Sayfa iframe içindeyse iframe'in src adresine gidin. Alanlar ayrı sekmelerdeyse (Ders Bilgileri, Öğrenme Çıktıları, Ders Akışı, Katkı) sırayla açıp metinleri birleştirin.",
|
|
1899
|
+
pesin
|
|
1900
|
+
? "ADIM 4: Bu dersin eksiklerini doldurup kaydedin (sonrakiAdim'daki sıra), sonra listeye dönüp sıradaki derse geçin; bütün plan bitene kadar hocaya hiçbir şey yazmayın."
|
|
1901
|
+
: "ADIM 4: Listeye dönüp sıradaki derse geçin; bütün plan bitene kadar hocaya hiçbir şey yazmayın.",
|
|
1902
|
+
"ADIM 5: Giriş ekranına düşerseniz durun: 'Oturum düştü; OBS sekmesinde yeniden girip girdim yazın' deyin. Parola ve doğrulama kodu istemeyin.",
|
|
1903
|
+
pesin
|
|
1904
|
+
? "ADIM 6: Plan bitince sonrakiAdim'daki TEK raporu yazın; ara rapor ve seçenek yok."
|
|
1905
|
+
: "ADIM 6: Plan bitince raporBicimi'ne göre TEK mesaj yazın.",
|
|
1906
|
+
],
|
|
1907
|
+
...(kalan > 0 ? { taranmayan: kalan, sonrakiBaslangic: sonraki } : {}),
|
|
1908
|
+
// Peşin onayda rapor durağı yoktur; rapor biçimi yalnız olağan akışta döner.
|
|
1909
|
+
...(pesin
|
|
1910
|
+
? {}
|
|
1911
|
+
: {
|
|
1912
|
+
raporBicimi: [
|
|
1913
|
+
"Başlık: 'Şu eksiklikleri tespit ettim, önerilerim şunlar.'",
|
|
1914
|
+
"Her ders bir satır, en eksikten en tamama: ders kodu, eksik ya da kurala uymayan alan sayısı, en önemli 1-2 eksik.",
|
|
1915
|
+
"Eksiği olmayan dersler için: 'eksik görünmüyor' ve kaliteyi artıracak 1-2 somut öneri (zayıf alan, Bloom düzeyi, iş yükü ile AKTS uyumu, kaynak güncelliği, İngilizce alanlar).",
|
|
1916
|
+
"Sayfada olmayan değerlendirme yüzdeleri ve kaynakları aynı mesajda sor (tahmin etme).",
|
|
1917
|
+
`Altına: ${TARAMA_SECENEKLERI.join(" ")}`,
|
|
1918
|
+
],
|
|
1919
|
+
}),
|
|
1920
|
+
},
|
|
1921
|
+
kaynak: [KAYNAK_KURALLAR],
|
|
1922
|
+
hocayaSimdiSor: false,
|
|
1923
|
+
...(pesin
|
|
1924
|
+
? {
|
|
1925
|
+
sonrakiAdim: `Peşin onay var: rapor durağı yok. Her ders için sırayla: bologna_ayristir → eksik alanları bologna_alan_rehberi ve bologna_taslak_denetle ile hazırla → bologna_kopya_metni'ni pesinOnay ile çağır → yaz, Kaydet, geri oku, obs_karsilastir. Sayfada olmayan değerlendirme yüzdesi ve kaynağı sorma, EKSİK bırak.${kalan > 0 ? ` Dilim bitince obs_ders_listesi'ni baslangic=${sonraki} ve aynı pesinOnay ile çağır.` : ""} Hepsi bitince TEK rapor: ders ders yazılanlar, EKSİK kalanlar, geri okuma sonucu; Onaya Gönder hocada.`,
|
|
1926
|
+
}
|
|
1927
|
+
: kalan > 0
|
|
1928
|
+
? {
|
|
1929
|
+
sonrakiAdim: `Bu dilimi sormadan tarayın ve raporunu verin. Hoca 1'i seçip bu dilim kapanınca 'devam edeyim mi' diye sormadan obs_ders_listesi'ni baslangic=${sonraki} ile yeniden çağırıp sonraki dilime geçin.`,
|
|
1930
|
+
}
|
|
1931
|
+
: {}),
|
|
1582
1932
|
};
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
if (alanlar.ska)
|
|
1618
|
-
ekle("Sür. Kalkınma Amaçları", "Seçilecek amaç numaraları", alanlar.ska.join(", "));
|
|
1619
|
-
if (alanlar.kaynaklar?.length)
|
|
1620
|
-
ekle("Diğer Kaynaklar", "Kaynaklar (her satır bir künye)", alanlar.kaynaklar.map(kunyeMetni).join("\n"));
|
|
1621
|
-
if (!bolumler.length && !yazilamayan.length)
|
|
1622
|
-
throw new AracHatasi("Kopyalanacak alan yok. En az bir alan gönderin.");
|
|
1623
|
-
const b = alanBulgulari(ders, alanlar, { ingilizce, programCiktilari });
|
|
1624
|
-
const kalanEksikler = [...b.entries()].flatMap(([alan, xs]) => xs.filter((x) => x.duzey === "hata").map((x) => ({ alan, neden: x.aciklama })));
|
|
1625
|
-
const baslik = [ders.kod, ders.ad].filter(Boolean).join(" ") || "Ders";
|
|
1626
|
-
const sirali = [...bolumler].sort((x, y) => SEKME_SIRASI.indexOf(x.sekme) - SEKME_SIRASI.indexOf(y.sekme) || x.sira - y.sira);
|
|
1627
|
-
const metin = [
|
|
1628
|
-
`${baslik}: OBS'ye kopyalanacak alanlar`,
|
|
1629
|
-
"",
|
|
1630
|
-
...sirali.flatMap((x, i) => [`${i + 1}. ${x.sekme} > ${x.alan}`, x.deger, ""]),
|
|
1631
|
-
...(ders.yetkililer?.length
|
|
1632
|
-
? [
|
|
1633
|
-
"Dersin Yetkilileri (sayfadaki hâliyle; değiştirmeyin, OBS'de zaten böyle kayıtlı):",
|
|
1634
|
-
...ders.yetkililer.map((y) => `${y.rol}: ${y.deger}`),
|
|
1635
|
-
]
|
|
1636
|
-
: ["Dersin Yetkilileri: bu metin onlara dokunmaz; OBS'deki hâliyle kalır."]),
|
|
1637
|
-
...(kalanEksikler.length
|
|
1638
|
-
? [
|
|
1639
|
-
"",
|
|
1640
|
-
"Kalan eksikler:",
|
|
1641
|
-
...kalanEksikler.map((e) => `- ${ALANLAR[e.alan]?.ad ?? "Genel"}: ${e.neden}`),
|
|
1642
|
-
]
|
|
1643
|
-
: []),
|
|
1644
|
-
...(yazilamayan.length
|
|
1645
|
-
? ["", "Yazma paketine alınmayanlar:", ...yazilamayan.map((y) => `- ${y.alan}: ${y.neden}`)]
|
|
1646
|
-
: []),
|
|
1647
|
-
].join("\n");
|
|
1648
|
-
const numarali = sirali.map((x, i) => ({ ...x, sira: i + 1 }));
|
|
1649
|
-
const normal = (v) => v.replace(/\s+/gu, " ").trim();
|
|
1650
|
-
const degisiklikOzeti = numarali.map((x) => {
|
|
1651
|
-
const onceki = oncekiDegerler?.[x.alan];
|
|
1933
|
+
},
|
|
1934
|
+
});
|
|
1935
|
+
// ---------------------------------------------------------------- bologna_toplu_ozet
|
|
1936
|
+
/**
|
|
1937
|
+
* Toplu mod: birden çok dersin yazma paketi tek tabloda, tek onayla. Her ders için bologna_kopya_metni'nin kapıları
|
|
1938
|
+
* aynen çalışır (teyitsiz yüzde ve doğrulanmamış kaynak pakete girmez). Onay hakkı korunur: her satırda dayanak
|
|
1939
|
+
* etiketi görünür, hoca ders numarasıyla seçebilir, kayıt döngüsünde her dersten sonra obs_karsilastir çalışır.
|
|
1940
|
+
*/
|
|
1941
|
+
const topluOzetAraci = arac({
|
|
1942
|
+
ad: "bologna_toplu_ozet",
|
|
1943
|
+
baslik: "Birden çok dersin yazma özeti",
|
|
1944
|
+
aciklama: "Birden çok dersin (1-15) hazır taslaklarını tek çağrıda yazma paketine çevirir ve hocaya gösterilecek tek özet tablosunu üretir: ders, değişecek alanlar, değerlendirme dayanağı, iş yükü, kaynak sayısı, pakete alınmayanlar. Her ders için bologna_kopya_metni kuralları aynen uygulanır. OBS'ye yazmaz.",
|
|
1945
|
+
sonrakiAdim: "tablo'yu olduğu gibi ve hocayaGoster seçenekleriyle TEK mesajda gösterin; tek onay alın. Hoca 1 derse kayıt döngüsü: her dersi sırayla yazın, Kaydet'e basın, geri okuyup o dersin paketi (paketler[kod]) ile obs_karsilastir çağırın; dur true ya da ikinci farkta bütün döngüyü durdurup hocaya söyleyin. Onaya Gönder'e hiçbir derste basmayın.",
|
|
1946
|
+
hocayaSimdiSor: true,
|
|
1947
|
+
kisiselVeriKapsami: BOLOGNA_KAPSAMI,
|
|
1948
|
+
kunyeAlanlari: BOLOGNA_KUNYE_ALANLARI,
|
|
1949
|
+
girdi: z.object({
|
|
1950
|
+
dersler: z.array(KopyaMetniGirdisi).min(1).max(15).describe("Her öğe bologna_kopya_metni girdisiyle aynı"),
|
|
1951
|
+
pesinOnay: z.string().max(500).optional().describe(PESIN_ONAY_ACIKLAMASI),
|
|
1952
|
+
}),
|
|
1953
|
+
calistir: (g) => {
|
|
1954
|
+
const pesin = pesinOnayVar(g.pesinOnay);
|
|
1955
|
+
const satirlar = g.dersler.map((d, i) => {
|
|
1956
|
+
const s = kopyaPaketi(d).sonuc;
|
|
1957
|
+
const dayanak = d.alanlar.degerlendirmeDayanagi;
|
|
1958
|
+
const degerlendirme = d.alanlar.degerlendirme
|
|
1959
|
+
? degerlendirmeTeyitli(d.alanlar)
|
|
1960
|
+
? `${d.alanlar.degerlendirme.map((x) => `${x.ad} %${x.katki}`).join(", ")} (${dayanak})`
|
|
1961
|
+
: "teyitsiz, yazılmaz"
|
|
1962
|
+
: "sayfadaki korunur";
|
|
1963
|
+
const kaynaklar = d.alanlar.kaynaklar ?? [];
|
|
1964
|
+
const kaynakOzeti = kaynaklar.length
|
|
1965
|
+
? `${kaynaklar.length} (${kaynaklar.map((k) => kaynakDurumu(k)).join(", ")})`
|
|
1966
|
+
: "EKSİK";
|
|
1652
1967
|
return {
|
|
1653
|
-
no:
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1968
|
+
no: i + 1,
|
|
1969
|
+
kod: d.ders.kod,
|
|
1970
|
+
ad: d.ders.ad ?? "",
|
|
1971
|
+
degisecekAlanlar: s.degisiklikOzeti.filter((x) => x.degisim !== "aynı").map((x) => x.alan),
|
|
1972
|
+
degerlendirme,
|
|
1973
|
+
isYuku: s.hesap.isyuku
|
|
1974
|
+
? `${s.hesap.isyuku.toplam} saat${d.ders.akts ? ` / ${d.ders.akts} AKTS` : ""}`
|
|
1975
|
+
: "EKSİK",
|
|
1976
|
+
kaynaklar: kaynakOzeti,
|
|
1977
|
+
yazilamayan: s.yazilamayan.map((y) => y.alan),
|
|
1978
|
+
kalanEksik: s.kalanEksikler.length,
|
|
1979
|
+
paket: s.obsKarsilastirGirdisi,
|
|
1659
1980
|
};
|
|
1660
1981
|
});
|
|
1982
|
+
const paketler = Object.fromEntries(satirlar.map((s) => [s.kod, s.paket]));
|
|
1983
|
+
const tablo = satirlar.map(({ paket: _p, ...kalan }) => kalan);
|
|
1984
|
+
const sorunlu = satirlar.filter((s) => s.yazilamayan.length || s.kalanEksik);
|
|
1661
1985
|
return {
|
|
1662
1986
|
sonuc: {
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1987
|
+
tablo,
|
|
1988
|
+
paketler,
|
|
1989
|
+
hocayaGoster: [
|
|
1990
|
+
`${satirlar.length} dersin değişiklik özeti yukarıda. Dayanak: sayfa = OBS'den okundu, hoca = siz söylediniz, öneri = ders adından üretildi.`,
|
|
1991
|
+
...(sorunlu.length
|
|
1992
|
+
? [
|
|
1993
|
+
`Pakete alınmayan ya da eksik kalan alanı olan dersler: ${sorunlu.map((s) => `${s.no}) ${s.kod}`).join(", ")}.`,
|
|
1994
|
+
]
|
|
1995
|
+
: []),
|
|
1996
|
+
// Peşin onayda seçenek sorulmaz; özet sondaki raporda yer alır.
|
|
1997
|
+
...(pesin
|
|
1998
|
+
? []
|
|
1999
|
+
: [
|
|
2000
|
+
`1) ${satirlar.length} dersin tamamını OBS'ye yaz ve kaydet (önerilen; bu seçim 'kaydet' onayıdır)`,
|
|
2001
|
+
"2) Yalnız şu dersleri yaz: numaralarını yazın (ör. 1, 3)",
|
|
2002
|
+
"3) Bir dersin tam metnini göster: numarasını yazın (ör. detay 2)",
|
|
2003
|
+
]),
|
|
2004
|
+
"Onaya Gönder'e siz basarsınız.",
|
|
2005
|
+
].join("\n"),
|
|
2006
|
+
...(pesin ? { pesinOnayKaydi: g.pesinOnay } : {}),
|
|
2007
|
+
not: "Biçim denetiminden geçti; içerik teyidi: hoca. Her ders kaydedildikten sonra geri okunup obs_karsilastir ile doğrulanır.",
|
|
1675
2008
|
},
|
|
1676
2009
|
kaynak: [KAYNAK_KURALLAR],
|
|
1677
|
-
...(
|
|
1678
|
-
? {
|
|
1679
|
-
sonrakiAdim: "Kalan eksikleri hocaya numaralı gösterin ve sorun: 1) eksikleri dolduralım 2) böyle kalsın, metni kopyalayacağım.",
|
|
1680
|
-
}
|
|
1681
|
-
: {}),
|
|
2010
|
+
...(pesin ? { hocayaSimdiSor: false, sonrakiAdim: PESIN_ONAY_YAZMA } : {}),
|
|
1682
2011
|
};
|
|
1683
2012
|
},
|
|
1684
2013
|
});
|
|
1685
2014
|
export const BOLOGNA_ARACLARI = [
|
|
2015
|
+
dersListesiAraci,
|
|
1686
2016
|
ayristirAraci,
|
|
1687
2017
|
alanRehberi,
|
|
1688
2018
|
taslakDenetleAraci,
|
|
1689
2019
|
isYukuOnerAraci,
|
|
1690
2020
|
durumAraci,
|
|
1691
2021
|
kopyaMetniAraci,
|
|
2022
|
+
topluOzetAraci,
|
|
1692
2023
|
obsKarsilastirAraci,
|
|
1693
2024
|
];
|