zam-core 0.6.2 → 0.6.3
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/dist/cli/index.js +2434 -179
- package/dist/cli/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -7386,7 +7386,7 @@ async function readWebLink(url) {
|
|
|
7386
7386
|
redirect: "manual",
|
|
7387
7387
|
signal: controller.signal,
|
|
7388
7388
|
headers: {
|
|
7389
|
-
"User-Agent": "ZAM-Content-Studio/0.6.
|
|
7389
|
+
"User-Agent": "ZAM-Content-Studio/0.6.3"
|
|
7390
7390
|
}
|
|
7391
7391
|
});
|
|
7392
7392
|
if (res.status >= 300 && res.status < 400) {
|
|
@@ -7464,23 +7464,184 @@ async function setLastCurriculumSelection(db, breadcrumb) {
|
|
|
7464
7464
|
await setSetting(db, LAST_SELECTION_KEY, JSON.stringify(breadcrumb));
|
|
7465
7465
|
}
|
|
7466
7466
|
|
|
7467
|
+
// src/cli/curriculum/providers/bildungsplan-bremen/manifest.ts
|
|
7468
|
+
var BILDUNGSPLAN_BREMEN_MANIFEST = {
|
|
7469
|
+
schoolYear: "2025/2026",
|
|
7470
|
+
capturedOn: "2026-07-02",
|
|
7471
|
+
sourceRevision: "Bildungspl\xE4ne Bremen",
|
|
7472
|
+
schoolTypes: [
|
|
7473
|
+
{ id: "oberschule", label: "Oberschule" },
|
|
7474
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
7475
|
+
],
|
|
7476
|
+
grades: {
|
|
7477
|
+
oberschule: ["7", "8", "9", "10"],
|
|
7478
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
7479
|
+
},
|
|
7480
|
+
subjects: {
|
|
7481
|
+
oberschule: [
|
|
7482
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
7483
|
+
{ id: "informatik", label: "Informatik" },
|
|
7484
|
+
{ id: "physik", label: "Physik" },
|
|
7485
|
+
{ id: "chemie", label: "Chemie" },
|
|
7486
|
+
{ id: "biologie", label: "Biologie" }
|
|
7487
|
+
],
|
|
7488
|
+
gymnasium: [
|
|
7489
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
7490
|
+
{ id: "informatik", label: "Informatik" },
|
|
7491
|
+
{ id: "physik", label: "Physik" },
|
|
7492
|
+
{ id: "chemie", label: "Chemie" },
|
|
7493
|
+
{ id: "biologie", label: "Biologie" }
|
|
7494
|
+
]
|
|
7495
|
+
},
|
|
7496
|
+
tracks: {},
|
|
7497
|
+
topics: {
|
|
7498
|
+
"oberschule|10|mathematik": [
|
|
7499
|
+
{ id: "funktionen", label: "Funktionen" },
|
|
7500
|
+
{ id: "geometrie", label: "Geometrie" }
|
|
7501
|
+
],
|
|
7502
|
+
"gymnasium|10|mathematik": [{ id: "analysis", label: "Analysis" }],
|
|
7503
|
+
"oberschule|9|informatik": [{ id: "algorithmen", label: "Algorithmen" }],
|
|
7504
|
+
"oberschule|9|physik": [{ id: "mechanik", label: "Mechanik" }],
|
|
7505
|
+
"oberschule|9|chemie": [
|
|
7506
|
+
{ id: "reaktionen", label: "Chemische Reaktionen" }
|
|
7507
|
+
],
|
|
7508
|
+
"oberschule|9|biologie": [{ id: "zellen", label: "Zellen" }],
|
|
7509
|
+
"gymnasium|9|physik": [{ id: "elektrizitaet", label: "Elektrizit\xE4t" }],
|
|
7510
|
+
"gymnasium|9|chemie": [{ id: "bindungen", label: "Chemische Bindungen" }],
|
|
7511
|
+
"gymnasium|9|biologie": [{ id: "genetik", label: "Genetik" }]
|
|
7512
|
+
},
|
|
7513
|
+
contentUrls: {
|
|
7514
|
+
"oberschule|10|mathematik": "https://www.lis.bremen.de/schulqualitaet/bildungsplaene-21942",
|
|
7515
|
+
"gymnasium|10|mathematik": "https://www.lis.bremen.de/schulqualitaet/bildungsplaene-21942",
|
|
7516
|
+
"oberschule|9|informatik": "https://www.lis.bremen.de/schulqualitaet/bildungsplaene-21942",
|
|
7517
|
+
"oberschule|9|physik": "https://www.lis.bremen.de/schulqualitaet/bildungsplaene-21942",
|
|
7518
|
+
"oberschule|9|chemie": "https://www.lis.bremen.de/schulqualitaet/bildungsplaene-21942",
|
|
7519
|
+
"oberschule|9|biologie": "https://www.lis.bremen.de/schulqualitaet/bildungsplaene-21942",
|
|
7520
|
+
"gymnasium|9|physik": "https://www.lis.bremen.de/schulqualitaet/bildungsplaene-21942",
|
|
7521
|
+
"gymnasium|9|chemie": "https://www.lis.bremen.de/schulqualitaet/bildungsplaene-21942",
|
|
7522
|
+
"gymnasium|9|biologie": "https://www.lis.bremen.de/schulqualitaet/bildungsplaene-21942"
|
|
7523
|
+
}
|
|
7524
|
+
};
|
|
7525
|
+
|
|
7526
|
+
// src/cli/curriculum/providers/bildungsplan-bremen/index.ts
|
|
7527
|
+
var bildungsplanBremenProvider = {
|
|
7528
|
+
id: "bildungsplan-bremen",
|
|
7529
|
+
country: "DE",
|
|
7530
|
+
countryLabel: "Deutschland",
|
|
7531
|
+
region: "HB",
|
|
7532
|
+
regionLabel: "Bremen",
|
|
7533
|
+
label: "Bildungsplan (Bremen)",
|
|
7534
|
+
listSchoolTypes() {
|
|
7535
|
+
return BILDUNGSPLAN_BREMEN_MANIFEST.schoolTypes;
|
|
7536
|
+
},
|
|
7537
|
+
listGrades(schoolType) {
|
|
7538
|
+
return (BILDUNGSPLAN_BREMEN_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
7539
|
+
id,
|
|
7540
|
+
label: `Klasse ${id}`
|
|
7541
|
+
}));
|
|
7542
|
+
},
|
|
7543
|
+
listSubjects(schoolType, _grade) {
|
|
7544
|
+
return BILDUNGSPLAN_BREMEN_MANIFEST.subjects[schoolType] || [];
|
|
7545
|
+
},
|
|
7546
|
+
listTracks(schoolType, grade, subject) {
|
|
7547
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
7548
|
+
return BILDUNGSPLAN_BREMEN_MANIFEST.tracks[key] || [];
|
|
7549
|
+
},
|
|
7550
|
+
listTopics(selection) {
|
|
7551
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
7552
|
+
const list = BILDUNGSPLAN_BREMEN_MANIFEST.topics[key] || [];
|
|
7553
|
+
return list.map((t2) => ({
|
|
7554
|
+
...t2,
|
|
7555
|
+
sourceRef: key
|
|
7556
|
+
}));
|
|
7557
|
+
},
|
|
7558
|
+
resolveTopic(topic) {
|
|
7559
|
+
const uri = BILDUNGSPLAN_BREMEN_MANIFEST.contentUrls[topic.sourceRef];
|
|
7560
|
+
if (!uri) {
|
|
7561
|
+
throw new Error(
|
|
7562
|
+
`Bildungsplan Bremen: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
7563
|
+
);
|
|
7564
|
+
}
|
|
7565
|
+
return {
|
|
7566
|
+
provider: "bildungsplan-bremen",
|
|
7567
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
7568
|
+
uri
|
|
7569
|
+
};
|
|
7570
|
+
},
|
|
7571
|
+
extractTopics(html, topicIds) {
|
|
7572
|
+
const results = {};
|
|
7573
|
+
const headingMatches = Array.from(
|
|
7574
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
7575
|
+
);
|
|
7576
|
+
const sections = [];
|
|
7577
|
+
for (const m of headingMatches) {
|
|
7578
|
+
const headerText = cleanHtmlText(m[1]).trim();
|
|
7579
|
+
const start = m.index + m[0].length;
|
|
7580
|
+
const nextHeading = html.indexOf("<h", start);
|
|
7581
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 1500);
|
|
7582
|
+
const content = cleanHtmlText(rawContent).slice(0, 1500);
|
|
7583
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
7584
|
+
}
|
|
7585
|
+
for (const topicId of topicIds) {
|
|
7586
|
+
const hashIdx = topicId.indexOf("#");
|
|
7587
|
+
if (hashIdx === -1) continue;
|
|
7588
|
+
const key = topicId.substring(0, hashIdx);
|
|
7589
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
7590
|
+
const list = BILDUNGSPLAN_BREMEN_MANIFEST.topics[key] ?? [];
|
|
7591
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
7592
|
+
if (!match) continue;
|
|
7593
|
+
const label = match.label;
|
|
7594
|
+
const normalizedLabel = normalizeForComparison(label);
|
|
7595
|
+
let section = sections.find(
|
|
7596
|
+
(s) => normalizeForComparison(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
7597
|
+
);
|
|
7598
|
+
if (!section && sections.length > 0) {
|
|
7599
|
+
section = sections.find((s) => s.content.length > 80) || sections[0];
|
|
7600
|
+
}
|
|
7601
|
+
if (section) {
|
|
7602
|
+
results[topicId] = `${label}
|
|
7603
|
+
|
|
7604
|
+
${section.content}`.trim();
|
|
7605
|
+
} else {
|
|
7606
|
+
results[topicId] = label;
|
|
7607
|
+
}
|
|
7608
|
+
}
|
|
7609
|
+
return results;
|
|
7610
|
+
}
|
|
7611
|
+
};
|
|
7612
|
+
function cleanHtmlText(html) {
|
|
7613
|
+
let text = html.replace(
|
|
7614
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
7615
|
+
" "
|
|
7616
|
+
);
|
|
7617
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
7618
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
7619
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
7620
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
7621
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
7622
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
7623
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
7624
|
+
}
|
|
7625
|
+
function normalizeForComparison(str) {
|
|
7626
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
7627
|
+
}
|
|
7628
|
+
|
|
7467
7629
|
// src/cli/curriculum/providers/bildungsplan-bw/manifest.ts
|
|
7468
7630
|
var BILDUNGSPLAN_BW_MANIFEST = {
|
|
7469
7631
|
schoolYear: "2026/2027",
|
|
7470
7632
|
capturedOn: "2026-07-02",
|
|
7471
7633
|
sourceRevision: "Bildungsplan Baden-W\xFCrttemberg Gymnasium 2016",
|
|
7472
|
-
schoolTypes: [
|
|
7473
|
-
{ id: "gymnasium", label: "Gymnasium" },
|
|
7474
|
-
{ id: "realschule", label: "Realschule" }
|
|
7475
|
-
],
|
|
7634
|
+
schoolTypes: [{ id: "gymnasium", label: "Gymnasium" }],
|
|
7476
7635
|
grades: {
|
|
7477
7636
|
gymnasium: ["9", "10", "11", "12"]
|
|
7478
7637
|
},
|
|
7479
7638
|
subjects: {
|
|
7480
7639
|
gymnasium: [
|
|
7481
7640
|
{ id: "mathematik", label: "Mathematik" },
|
|
7641
|
+
{ id: "informatik", label: "Informatik" },
|
|
7482
7642
|
{ id: "physik", label: "Physik" },
|
|
7483
|
-
{ id: "
|
|
7643
|
+
{ id: "chemie", label: "Chemie" },
|
|
7644
|
+
{ id: "biologie", label: "Biologie" }
|
|
7484
7645
|
]
|
|
7485
7646
|
},
|
|
7486
7647
|
tracks: {},
|
|
@@ -7491,10 +7652,44 @@ var BILDUNGSPLAN_BW_MANIFEST = {
|
|
|
7491
7652
|
{ id: "leitidee-raum", label: "Leitidee Raum und Form" },
|
|
7492
7653
|
{ id: "leitidee-funktion", label: "Leitidee Funktionaler Zusammenhang" },
|
|
7493
7654
|
{ id: "leitidee-daten", label: "Leitidee Daten und Zufall" }
|
|
7655
|
+
],
|
|
7656
|
+
"gymnasium|10|informatik": [
|
|
7657
|
+
{ id: "ik-daten-codierung", label: "Daten und Codierung" },
|
|
7658
|
+
{ id: "ik-algorithmen", label: "Algorithmen" },
|
|
7659
|
+
{ id: "ik-rechner-netze", label: "Rechner und Netze" },
|
|
7660
|
+
{
|
|
7661
|
+
id: "ik-informationsgesellschaft",
|
|
7662
|
+
label: "Informationsgesellschaft und Datensicherheit"
|
|
7663
|
+
}
|
|
7664
|
+
],
|
|
7665
|
+
"gymnasium|10|physik": [
|
|
7666
|
+
{ id: "optik-akustik", label: "Optik und Akustik" },
|
|
7667
|
+
{ id: "mechanik", label: "Mechanik" },
|
|
7668
|
+
{
|
|
7669
|
+
id: "elektrizitaet-magnetismus",
|
|
7670
|
+
label: "Elektrizit\xE4t und Magnetismus"
|
|
7671
|
+
},
|
|
7672
|
+
{ id: "energie-waerme", label: "Energie und W\xE4rmelehre" }
|
|
7673
|
+
],
|
|
7674
|
+
"gymnasium|10|chemie": [
|
|
7675
|
+
{ id: "stoffe-reaktionen", label: "Stoffe und chemische Reaktionen" },
|
|
7676
|
+
{ id: "atome-molekuele", label: "Atome und Molek\xFCle" },
|
|
7677
|
+
{ id: "saeuren-basen", label: "S\xE4uren, Basen und Salze" },
|
|
7678
|
+
{ id: "organische-chemie", label: "Organische Chemie" }
|
|
7679
|
+
],
|
|
7680
|
+
"gymnasium|10|biologie": [
|
|
7681
|
+
{ id: "zellen", label: "Zellen und Gewebe" },
|
|
7682
|
+
{ id: "genetik", label: "Genetik und Vererbung" },
|
|
7683
|
+
{ id: "oekologie", label: "\xD6kologie und Umwelt" },
|
|
7684
|
+
{ id: "evolution", label: "Evolution und Vielfalt" }
|
|
7494
7685
|
]
|
|
7495
7686
|
},
|
|
7496
7687
|
contentUrls: {
|
|
7497
|
-
"gymnasium|10|mathematik": "http://www.bildungsplaene-bw.de/,Lde/LS/BP2016BW/ALLG/GYM/M/bp/klasse-10"
|
|
7688
|
+
"gymnasium|10|mathematik": "http://www.bildungsplaene-bw.de/,Lde/LS/BP2016BW/ALLG/GYM/M/bp/klasse-10",
|
|
7689
|
+
"gymnasium|10|informatik": "https://www.bildungsplaene-bw.de/,Lde/BP2016BW_ALLG_SEK1_INFWF",
|
|
7690
|
+
"gymnasium|10|physik": "https://www.bildungsplaene-bw.de/,Lde/BP2016BW_ALLG_GYM_PH",
|
|
7691
|
+
"gymnasium|10|chemie": "https://www.bildungsplaene-bw.de/,Lde/BP2016BW_ALLG_GYM_CH",
|
|
7692
|
+
"gymnasium|10|biologie": "https://www.bildungsplaene-bw.de/,Lde/BP2016BW_ALLG_GYM_BIO"
|
|
7498
7693
|
}
|
|
7499
7694
|
};
|
|
7500
7695
|
|
|
@@ -7555,7 +7750,7 @@ var bildungsplanBwProvider = {
|
|
|
7555
7750
|
const headerMatch = contentHtml.match(
|
|
7556
7751
|
/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/i
|
|
7557
7752
|
);
|
|
7558
|
-
const headerText = headerMatch ?
|
|
7753
|
+
const headerText = headerMatch ? cleanHtmlText2(headerMatch[1]).trim() : "";
|
|
7559
7754
|
sections.push({
|
|
7560
7755
|
id,
|
|
7561
7756
|
headerText,
|
|
@@ -7572,20 +7767,20 @@ var bildungsplanBwProvider = {
|
|
|
7572
7767
|
const match = list.find((t2) => t2.id === shortId);
|
|
7573
7768
|
if (!match) continue;
|
|
7574
7769
|
const label = match.label;
|
|
7575
|
-
const normalizedLabel =
|
|
7770
|
+
const normalizedLabel = normalizeForComparison2(label);
|
|
7576
7771
|
const sectionIndex = sections.findIndex((s) => {
|
|
7577
|
-
const normalizedHeader =
|
|
7772
|
+
const normalizedHeader = normalizeForComparison2(s.headerText);
|
|
7578
7773
|
return normalizedHeader.includes(normalizedLabel) || s.id === shortId;
|
|
7579
7774
|
});
|
|
7580
7775
|
if (sectionIndex === -1) {
|
|
7581
7776
|
continue;
|
|
7582
7777
|
}
|
|
7583
|
-
results[topicId] =
|
|
7778
|
+
results[topicId] = cleanHtmlText2(sections[sectionIndex].contentHtml);
|
|
7584
7779
|
}
|
|
7585
7780
|
return results;
|
|
7586
7781
|
}
|
|
7587
7782
|
};
|
|
7588
|
-
function
|
|
7783
|
+
function cleanHtmlText2(html) {
|
|
7589
7784
|
let text = html.replace(
|
|
7590
7785
|
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
7591
7786
|
" "
|
|
@@ -7598,181 +7793,1731 @@ function cleanHtmlText(html) {
|
|
|
7598
7793
|
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'").replace(/–/g, "\u2013").replace(/—/g, "\u2014").replace(/·/g, "\xB7");
|
|
7599
7794
|
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
7600
7795
|
}
|
|
7601
|
-
function
|
|
7796
|
+
function normalizeForComparison2(str) {
|
|
7602
7797
|
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
7603
7798
|
}
|
|
7604
7799
|
|
|
7605
|
-
// src/cli/curriculum/providers/
|
|
7606
|
-
var
|
|
7607
|
-
schoolYear: "2026
|
|
7800
|
+
// src/cli/curriculum/providers/bildungsplan-hamburg/manifest.ts
|
|
7801
|
+
var BILDUNGSPLAN_HAMBURG_MANIFEST = {
|
|
7802
|
+
schoolYear: "2025/2026",
|
|
7608
7803
|
capturedOn: "2026-07-02",
|
|
7609
|
-
sourceRevision: "
|
|
7804
|
+
sourceRevision: "Bildungspl\xE4ne Hamburg",
|
|
7610
7805
|
schoolTypes: [
|
|
7611
|
-
{ id: "
|
|
7612
|
-
{ id: "
|
|
7613
|
-
{ id: "foerderschule", label: "F\xF6rderschule" },
|
|
7614
|
-
{ id: "realschule", label: "Realschule" },
|
|
7615
|
-
{ id: "gymnasium", label: "Gymnasium" },
|
|
7616
|
-
{ id: "wirtschaftsschule", label: "Wirtschaftsschule" },
|
|
7617
|
-
{ id: "fos", label: "Fachoberschule" },
|
|
7618
|
-
{ id: "bos", label: "Berufsoberschule" }
|
|
7806
|
+
{ id: "stadtteilschule", label: "Stadtteilschule (Realschule)" },
|
|
7807
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
7619
7808
|
],
|
|
7620
7809
|
grades: {
|
|
7621
|
-
|
|
7810
|
+
stadtteilschule: ["7", "8", "9", "10"],
|
|
7811
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
7622
7812
|
},
|
|
7623
7813
|
subjects: {
|
|
7624
|
-
|
|
7625
|
-
{
|
|
7626
|
-
|
|
7627
|
-
|
|
7628
|
-
},
|
|
7629
|
-
{ id: "biologie", label: "Biologie" },
|
|
7814
|
+
stadtteilschule: [
|
|
7815
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
7816
|
+
{ id: "informatik", label: "Informatik" },
|
|
7817
|
+
{ id: "physik", label: "Physik" },
|
|
7630
7818
|
{ id: "chemie", label: "Chemie" },
|
|
7631
|
-
{ id: "
|
|
7632
|
-
|
|
7633
|
-
|
|
7634
|
-
{ id: "ethik", label: "Ethik" },
|
|
7635
|
-
{
|
|
7636
|
-
id: "evangelische-religionslehre",
|
|
7637
|
-
label: "Evangelische Religionslehre"
|
|
7638
|
-
},
|
|
7639
|
-
{ id: "franzoesisch", label: "Franz\xF6sisch" },
|
|
7640
|
-
{ id: "geographie", label: "Geographie" },
|
|
7641
|
-
{ id: "geschichte", label: "Geschichte" },
|
|
7642
|
-
{ id: "it", label: "Informationstechnologie" },
|
|
7643
|
-
{ id: "iu", label: "Islamischer Unterricht" },
|
|
7644
|
-
{ id: "ir", label: "Israelitische Religionslehre" },
|
|
7645
|
-
{
|
|
7646
|
-
id: "katholische-religionslehre",
|
|
7647
|
-
label: "Katholische Religionslehre"
|
|
7648
|
-
},
|
|
7649
|
-
{ id: "kunst", label: "Kunst" },
|
|
7819
|
+
{ id: "biologie", label: "Biologie" }
|
|
7820
|
+
],
|
|
7821
|
+
gymnasium: [
|
|
7650
7822
|
{ id: "mathematik", label: "Mathematik" },
|
|
7651
|
-
{ id: "
|
|
7652
|
-
{ id: "or", label: "Orthodoxe Religionslehre" },
|
|
7823
|
+
{ id: "informatik", label: "Informatik" },
|
|
7653
7824
|
{ id: "physik", label: "Physik" },
|
|
7654
|
-
{ id: "
|
|
7655
|
-
{ id: "
|
|
7656
|
-
{ id: "sozialwesen", label: "Sozialwesen" },
|
|
7657
|
-
{ id: "spanisch", label: "Spanisch" },
|
|
7658
|
-
{ id: "sport", label: "Sport" },
|
|
7659
|
-
{ id: "textiles-gestalten", label: "Textiles Gestalten" },
|
|
7660
|
-
{ id: "werken", label: "Werken" },
|
|
7661
|
-
{ id: "wirtschaft-und-recht", label: "Wirtschaft und Recht" }
|
|
7662
|
-
]
|
|
7663
|
-
},
|
|
7664
|
-
tracks: {
|
|
7665
|
-
"realschule|9|mathematik": [
|
|
7666
|
-
{ id: "wpfg1", label: "Mathematik 9 (I)" },
|
|
7667
|
-
{ id: "wpfg2-3", label: "Mathematik 9 (II/III)" }
|
|
7825
|
+
{ id: "chemie", label: "Chemie" },
|
|
7826
|
+
{ id: "biologie", label: "Biologie" }
|
|
7668
7827
|
]
|
|
7669
7828
|
},
|
|
7829
|
+
tracks: {},
|
|
7670
7830
|
topics: {
|
|
7671
|
-
"
|
|
7672
|
-
{ id: "
|
|
7673
|
-
{ id: "
|
|
7674
|
-
{ id: "lb3", label: "Rechtwinklige Dreiecke", hours: 20 },
|
|
7675
|
-
{ id: "lb4", label: "Kreis", hours: 10 },
|
|
7676
|
-
{ id: "lb5", label: "Raumgeometrie", hours: 20 },
|
|
7677
|
-
{ id: "lb6", label: "Systeme linearer Gleichungen", hours: 12 },
|
|
7678
|
-
{
|
|
7679
|
-
id: "lb7",
|
|
7680
|
-
label: "Quadratische Funktionen und quadratische Gleichungen",
|
|
7681
|
-
hours: 42
|
|
7682
|
-
},
|
|
7683
|
-
{ id: "lb8", label: "Daten und Zufall", hours: 9 }
|
|
7684
|
-
],
|
|
7685
|
-
"realschule|9|mathematik|wpfg2-3": [
|
|
7686
|
-
{ id: "lb1", label: "Reelle Zahlen", hours: 7 },
|
|
7687
|
-
{ id: "lb2", label: "Zentrische Streckung", hours: 13 },
|
|
7688
|
-
{ id: "lb3", label: "Rechtwinklige Dreiecke", hours: 20 },
|
|
7689
|
-
{ id: "lb4", label: "Kreis", hours: 10 },
|
|
7690
|
-
{ id: "lb5", label: "Lineare Funktionen", hours: 15 },
|
|
7691
|
-
{ id: "lb6", label: "Systeme linearer Gleichungen", hours: 10 },
|
|
7692
|
-
{ id: "lb7", label: "Daten und Zufall", hours: 9 }
|
|
7693
|
-
],
|
|
7694
|
-
"realschule|9|deutsch": [
|
|
7695
|
-
{ id: "lb1", label: "Sprechen und Zuh\xF6ren" },
|
|
7696
|
-
{ id: "lb2", label: "Lesen \u2013 mit Texten und weiteren Medien umgehen" },
|
|
7697
|
-
{ id: "lb3", label: "Schreiben" },
|
|
7698
|
-
{
|
|
7699
|
-
id: "lb4",
|
|
7700
|
-
label: "Sprachgebrauch und Sprache untersuchen und reflektieren"
|
|
7701
|
-
}
|
|
7831
|
+
"stadtteilschule|10|mathematik": [
|
|
7832
|
+
{ id: "funktionen", label: "Funktionen" },
|
|
7833
|
+
{ id: "geometrie", label: "Geometrie" }
|
|
7702
7834
|
],
|
|
7703
|
-
"
|
|
7704
|
-
|
|
7705
|
-
|
|
7706
|
-
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
]
|
|
7835
|
+
"gymnasium|10|mathematik": [{ id: "analysis", label: "Analysis" }],
|
|
7836
|
+
"stadtteilschule|9|physik": [{ id: "mechanik", label: "Mechanik" }],
|
|
7837
|
+
"stadtteilschule|9|chemie": [{ id: "reaktionen", label: "Reaktionen" }],
|
|
7838
|
+
"stadtteilschule|9|biologie": [{ id: "zellen", label: "Zellen" }],
|
|
7839
|
+
"gymnasium|9|physik": [{ id: "elektrizitaet", label: "Elektrizit\xE4t" }],
|
|
7840
|
+
"gymnasium|9|chemie": [{ id: "bindungen", label: "Bindungen" }],
|
|
7841
|
+
"gymnasium|9|biologie": [{ id: "genetik", label: "Genetik" }]
|
|
7710
7842
|
},
|
|
7711
7843
|
contentUrls: {
|
|
7712
|
-
"
|
|
7713
|
-
"
|
|
7714
|
-
"
|
|
7715
|
-
"
|
|
7844
|
+
"stadtteilschule|10|mathematik": "https://www.hamburg.de/bildungsplaene",
|
|
7845
|
+
"gymnasium|10|mathematik": "https://www.hamburg.de/bildungsplaene",
|
|
7846
|
+
"stadtteilschule|9|physik": "https://www.hamburg.de/bildungsplaene",
|
|
7847
|
+
"stadtteilschule|9|chemie": "https://www.hamburg.de/bildungsplaene",
|
|
7848
|
+
"stadtteilschule|9|biologie": "https://www.hamburg.de/bildungsplaene",
|
|
7849
|
+
"gymnasium|9|physik": "https://www.hamburg.de/bildungsplaene",
|
|
7850
|
+
"gymnasium|9|chemie": "https://www.hamburg.de/bildungsplaene",
|
|
7851
|
+
"gymnasium|9|biologie": "https://www.hamburg.de/bildungsplaene"
|
|
7716
7852
|
}
|
|
7717
7853
|
};
|
|
7718
7854
|
|
|
7719
|
-
// src/cli/curriculum/providers/
|
|
7720
|
-
|
|
7721
|
-
|
|
7722
|
-
}
|
|
7723
|
-
var lehrplanplusBayernProvider = {
|
|
7724
|
-
id: "lehrplanplus-bayern",
|
|
7855
|
+
// src/cli/curriculum/providers/bildungsplan-hamburg/index.ts
|
|
7856
|
+
var bildungsplanHamburgProvider = {
|
|
7857
|
+
id: "bildungsplan-hamburg",
|
|
7725
7858
|
country: "DE",
|
|
7726
7859
|
countryLabel: "Deutschland",
|
|
7727
|
-
region: "
|
|
7728
|
-
regionLabel: "
|
|
7729
|
-
label: "
|
|
7860
|
+
region: "HH",
|
|
7861
|
+
regionLabel: "Hamburg",
|
|
7862
|
+
label: "Bildungsplan (Hamburg)",
|
|
7730
7863
|
listSchoolTypes() {
|
|
7731
|
-
return
|
|
7864
|
+
return BILDUNGSPLAN_HAMBURG_MANIFEST.schoolTypes;
|
|
7732
7865
|
},
|
|
7733
7866
|
listGrades(schoolType) {
|
|
7734
|
-
return (
|
|
7735
|
-
id
|
|
7736
|
-
label:
|
|
7867
|
+
return (BILDUNGSPLAN_HAMBURG_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
7868
|
+
id,
|
|
7869
|
+
label: `Klasse ${id}`
|
|
7737
7870
|
}));
|
|
7738
7871
|
},
|
|
7739
7872
|
listSubjects(schoolType, _grade) {
|
|
7740
|
-
return
|
|
7873
|
+
return BILDUNGSPLAN_HAMBURG_MANIFEST.subjects[schoolType] || [];
|
|
7741
7874
|
},
|
|
7742
7875
|
listTracks(schoolType, grade, subject) {
|
|
7743
|
-
|
|
7876
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
7877
|
+
return BILDUNGSPLAN_HAMBURG_MANIFEST.tracks[key] || [];
|
|
7744
7878
|
},
|
|
7745
7879
|
listTopics(selection) {
|
|
7746
|
-
const {
|
|
7747
|
-
|
|
7748
|
-
|
|
7749
|
-
|
|
7750
|
-
...topic,
|
|
7880
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
7881
|
+
const list = BILDUNGSPLAN_HAMBURG_MANIFEST.topics[key] || [];
|
|
7882
|
+
return list.map((t2) => ({
|
|
7883
|
+
...t2,
|
|
7751
7884
|
sourceRef: key
|
|
7752
7885
|
}));
|
|
7753
7886
|
},
|
|
7754
7887
|
resolveTopic(topic) {
|
|
7755
|
-
const uri =
|
|
7888
|
+
const uri = BILDUNGSPLAN_HAMBURG_MANIFEST.contentUrls[topic.sourceRef];
|
|
7756
7889
|
if (!uri) {
|
|
7757
7890
|
throw new Error(
|
|
7758
|
-
`
|
|
7891
|
+
`Bildungsplan Hamburg: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
7759
7892
|
);
|
|
7760
7893
|
}
|
|
7761
7894
|
return {
|
|
7762
|
-
provider: "
|
|
7895
|
+
provider: "bildungsplan-hamburg",
|
|
7763
7896
|
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
7764
7897
|
uri
|
|
7765
7898
|
};
|
|
7766
7899
|
},
|
|
7767
7900
|
extractTopics(html, topicIds) {
|
|
7768
|
-
const
|
|
7901
|
+
const results = {};
|
|
7902
|
+
const headingMatches = Array.from(
|
|
7903
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
7904
|
+
);
|
|
7769
7905
|
const sections = [];
|
|
7770
|
-
for (
|
|
7771
|
-
const
|
|
7772
|
-
const
|
|
7773
|
-
|
|
7774
|
-
const
|
|
7775
|
-
const
|
|
7906
|
+
for (const m of headingMatches) {
|
|
7907
|
+
const headerText = cleanHtmlText3(m[1]).trim();
|
|
7908
|
+
const start = m.index + m[0].length;
|
|
7909
|
+
const nextHeading = html.indexOf("<h", start);
|
|
7910
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 1500);
|
|
7911
|
+
const content = cleanHtmlText3(rawContent).slice(0, 1500);
|
|
7912
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
7913
|
+
}
|
|
7914
|
+
for (const topicId of topicIds) {
|
|
7915
|
+
const hashIdx = topicId.indexOf("#");
|
|
7916
|
+
if (hashIdx === -1) continue;
|
|
7917
|
+
const key = topicId.substring(0, hashIdx);
|
|
7918
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
7919
|
+
const list = BILDUNGSPLAN_HAMBURG_MANIFEST.topics[key] ?? [];
|
|
7920
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
7921
|
+
if (!match) continue;
|
|
7922
|
+
const label = match.label;
|
|
7923
|
+
const normalizedLabel = normalizeForComparison3(label);
|
|
7924
|
+
let section = sections.find(
|
|
7925
|
+
(s) => normalizeForComparison3(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
7926
|
+
);
|
|
7927
|
+
if (!section && sections.length > 0) {
|
|
7928
|
+
section = sections.find((s) => s.content.length > 80) || sections[0];
|
|
7929
|
+
}
|
|
7930
|
+
if (section) {
|
|
7931
|
+
results[topicId] = `${label}
|
|
7932
|
+
|
|
7933
|
+
${section.content}`.trim();
|
|
7934
|
+
} else {
|
|
7935
|
+
results[topicId] = label;
|
|
7936
|
+
}
|
|
7937
|
+
}
|
|
7938
|
+
return results;
|
|
7939
|
+
}
|
|
7940
|
+
};
|
|
7941
|
+
function cleanHtmlText3(html) {
|
|
7942
|
+
let text = html.replace(
|
|
7943
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
7944
|
+
" "
|
|
7945
|
+
);
|
|
7946
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
7947
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
7948
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
7949
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
7950
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
7951
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
7952
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
7953
|
+
}
|
|
7954
|
+
function normalizeForComparison3(str) {
|
|
7955
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
7956
|
+
}
|
|
7957
|
+
|
|
7958
|
+
// src/cli/curriculum/providers/fachanforderungen-sh/manifest.ts
|
|
7959
|
+
var FACHANFORDERUNGEN_SH_MANIFEST = {
|
|
7960
|
+
schoolYear: "2025/2026",
|
|
7961
|
+
capturedOn: "2026-07-02",
|
|
7962
|
+
sourceRevision: "Fachanforderungen Schleswig-Holstein",
|
|
7963
|
+
schoolTypes: [
|
|
7964
|
+
{ id: "gemeinschaftsschule", label: "Gemeinschaftsschule" },
|
|
7965
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
7966
|
+
],
|
|
7967
|
+
grades: {
|
|
7968
|
+
gemeinschaftsschule: ["7", "8", "9", "10"],
|
|
7969
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
7970
|
+
},
|
|
7971
|
+
subjects: {
|
|
7972
|
+
gemeinschaftsschule: [
|
|
7973
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
7974
|
+
{ id: "informatik", label: "Informatik" },
|
|
7975
|
+
{ id: "physik", label: "Physik" },
|
|
7976
|
+
{ id: "chemie", label: "Chemie" },
|
|
7977
|
+
{ id: "biologie", label: "Biologie" }
|
|
7978
|
+
],
|
|
7979
|
+
gymnasium: [
|
|
7980
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
7981
|
+
{ id: "informatik", label: "Informatik" },
|
|
7982
|
+
{ id: "physik", label: "Physik" },
|
|
7983
|
+
{ id: "chemie", label: "Chemie" },
|
|
7984
|
+
{ id: "biologie", label: "Biologie" }
|
|
7985
|
+
]
|
|
7986
|
+
},
|
|
7987
|
+
tracks: {},
|
|
7988
|
+
topics: {
|
|
7989
|
+
"gemeinschaftsschule|10|mathematik": [
|
|
7990
|
+
{ id: "funktionen", label: "Funktionen" }
|
|
7991
|
+
],
|
|
7992
|
+
"gymnasium|10|mathematik": [{ id: "analysis", label: "Analysis" }],
|
|
7993
|
+
"gemeinschaftsschule|9|physik": [{ id: "mechanik", label: "Mechanik" }],
|
|
7994
|
+
"gemeinschaftsschule|9|chemie": [{ id: "reaktionen", label: "Reaktionen" }],
|
|
7995
|
+
"gemeinschaftsschule|9|biologie": [{ id: "zellen", label: "Zellen" }],
|
|
7996
|
+
"gymnasium|9|physik": [{ id: "elektrizitaet", label: "Elektrizit\xE4t" }],
|
|
7997
|
+
"gymnasium|9|chemie": [{ id: "bindungen", label: "Bindungen" }],
|
|
7998
|
+
"gymnasium|9|biologie": [{ id: "genetik", label: "Genetik" }]
|
|
7999
|
+
},
|
|
8000
|
+
contentUrls: {
|
|
8001
|
+
"gemeinschaftsschule|10|mathematik": "https://fachportal.lernnetz.de/sh/fachanforderungen.html",
|
|
8002
|
+
"gymnasium|10|mathematik": "https://fachportal.lernnetz.de/sh/fachanforderungen.html",
|
|
8003
|
+
"gemeinschaftsschule|9|physik": "https://fachportal.lernnetz.de/sh/fachanforderungen.html",
|
|
8004
|
+
"gemeinschaftsschule|9|chemie": "https://fachportal.lernnetz.de/sh/fachanforderungen.html",
|
|
8005
|
+
"gemeinschaftsschule|9|biologie": "https://fachportal.lernnetz.de/sh/fachanforderungen.html",
|
|
8006
|
+
"gymnasium|9|physik": "https://fachportal.lernnetz.de/sh/fachanforderungen.html",
|
|
8007
|
+
"gymnasium|9|chemie": "https://fachportal.lernnetz.de/sh/fachanforderungen.html",
|
|
8008
|
+
"gymnasium|9|biologie": "https://fachportal.lernnetz.de/sh/fachanforderungen.html"
|
|
8009
|
+
}
|
|
8010
|
+
};
|
|
8011
|
+
|
|
8012
|
+
// src/cli/curriculum/providers/fachanforderungen-sh/index.ts
|
|
8013
|
+
var fachanforderungenShProvider = {
|
|
8014
|
+
id: "fachanforderungen-sh",
|
|
8015
|
+
country: "DE",
|
|
8016
|
+
countryLabel: "Deutschland",
|
|
8017
|
+
region: "SH",
|
|
8018
|
+
regionLabel: "Schleswig-Holstein",
|
|
8019
|
+
label: "Fachanforderungen (Schleswig-Holstein)",
|
|
8020
|
+
listSchoolTypes() {
|
|
8021
|
+
return FACHANFORDERUNGEN_SH_MANIFEST.schoolTypes;
|
|
8022
|
+
},
|
|
8023
|
+
listGrades(schoolType) {
|
|
8024
|
+
return (FACHANFORDERUNGEN_SH_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
8025
|
+
id,
|
|
8026
|
+
label: `Klasse ${id}`
|
|
8027
|
+
}));
|
|
8028
|
+
},
|
|
8029
|
+
listSubjects(schoolType, _grade) {
|
|
8030
|
+
return FACHANFORDERUNGEN_SH_MANIFEST.subjects[schoolType] || [];
|
|
8031
|
+
},
|
|
8032
|
+
listTracks(schoolType, grade, subject) {
|
|
8033
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
8034
|
+
return FACHANFORDERUNGEN_SH_MANIFEST.tracks[key] || [];
|
|
8035
|
+
},
|
|
8036
|
+
listTopics(selection) {
|
|
8037
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
8038
|
+
const list = FACHANFORDERUNGEN_SH_MANIFEST.topics[key] || [];
|
|
8039
|
+
return list.map((t2) => ({
|
|
8040
|
+
...t2,
|
|
8041
|
+
sourceRef: key
|
|
8042
|
+
}));
|
|
8043
|
+
},
|
|
8044
|
+
resolveTopic(topic) {
|
|
8045
|
+
const uri = FACHANFORDERUNGEN_SH_MANIFEST.contentUrls[topic.sourceRef];
|
|
8046
|
+
if (!uri) {
|
|
8047
|
+
throw new Error(
|
|
8048
|
+
`Fachanforderungen SH: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
8049
|
+
);
|
|
8050
|
+
}
|
|
8051
|
+
return {
|
|
8052
|
+
provider: "fachanforderungen-sh",
|
|
8053
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
8054
|
+
uri
|
|
8055
|
+
};
|
|
8056
|
+
},
|
|
8057
|
+
extractTopics(html, topicIds) {
|
|
8058
|
+
const results = {};
|
|
8059
|
+
const headingMatches = Array.from(
|
|
8060
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
8061
|
+
);
|
|
8062
|
+
const sections = [];
|
|
8063
|
+
for (const m of headingMatches) {
|
|
8064
|
+
const headerText = cleanHtmlText4(m[1]).trim();
|
|
8065
|
+
const start = m.index + m[0].length;
|
|
8066
|
+
const nextHeading = html.indexOf("<h", start);
|
|
8067
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 1500);
|
|
8068
|
+
const content = cleanHtmlText4(rawContent).slice(0, 1500);
|
|
8069
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
8070
|
+
}
|
|
8071
|
+
for (const topicId of topicIds) {
|
|
8072
|
+
const hashIdx = topicId.indexOf("#");
|
|
8073
|
+
if (hashIdx === -1) continue;
|
|
8074
|
+
const key = topicId.substring(0, hashIdx);
|
|
8075
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
8076
|
+
const list = FACHANFORDERUNGEN_SH_MANIFEST.topics[key] ?? [];
|
|
8077
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
8078
|
+
if (!match) continue;
|
|
8079
|
+
const label = match.label;
|
|
8080
|
+
const normalizedLabel = normalizeForComparison4(label);
|
|
8081
|
+
let section = sections.find(
|
|
8082
|
+
(s) => normalizeForComparison4(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
8083
|
+
);
|
|
8084
|
+
if (!section && sections.length > 0) {
|
|
8085
|
+
section = sections.find((s) => s.content.length > 80) || sections[0];
|
|
8086
|
+
}
|
|
8087
|
+
if (section) {
|
|
8088
|
+
results[topicId] = `${label}
|
|
8089
|
+
|
|
8090
|
+
${section.content}`.trim();
|
|
8091
|
+
} else {
|
|
8092
|
+
results[topicId] = label;
|
|
8093
|
+
}
|
|
8094
|
+
}
|
|
8095
|
+
return results;
|
|
8096
|
+
}
|
|
8097
|
+
};
|
|
8098
|
+
function cleanHtmlText4(html) {
|
|
8099
|
+
let text = html.replace(
|
|
8100
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
8101
|
+
" "
|
|
8102
|
+
);
|
|
8103
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
8104
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
8105
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
8106
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
8107
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
8108
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
8109
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
8110
|
+
}
|
|
8111
|
+
function normalizeForComparison4(str) {
|
|
8112
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
8113
|
+
}
|
|
8114
|
+
|
|
8115
|
+
// src/cli/curriculum/providers/kerncurriculum-hessen/manifest.ts
|
|
8116
|
+
var KERNCURRICULUM_HESSEN_MANIFEST = {
|
|
8117
|
+
schoolYear: "2025/2026",
|
|
8118
|
+
capturedOn: "2026-07-02",
|
|
8119
|
+
sourceRevision: "Kerncurricula Hessen (Sekundarstufe I)",
|
|
8120
|
+
schoolTypes: [
|
|
8121
|
+
{ id: "realschule", label: "Realschule" },
|
|
8122
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
8123
|
+
],
|
|
8124
|
+
grades: {
|
|
8125
|
+
realschule: ["7", "8", "9", "10"],
|
|
8126
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
8127
|
+
},
|
|
8128
|
+
subjects: {
|
|
8129
|
+
realschule: [
|
|
8130
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
8131
|
+
{ id: "informatik", label: "Informatik" },
|
|
8132
|
+
{ id: "physik", label: "Physik" },
|
|
8133
|
+
{ id: "chemie", label: "Chemie" },
|
|
8134
|
+
{ id: "biologie", label: "Biologie" }
|
|
8135
|
+
],
|
|
8136
|
+
gymnasium: [
|
|
8137
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
8138
|
+
{ id: "informatik", label: "Informatik" },
|
|
8139
|
+
{ id: "physik", label: "Physik" },
|
|
8140
|
+
{ id: "chemie", label: "Chemie" },
|
|
8141
|
+
{ id: "biologie", label: "Biologie" }
|
|
8142
|
+
]
|
|
8143
|
+
},
|
|
8144
|
+
tracks: {},
|
|
8145
|
+
topics: {
|
|
8146
|
+
// Minimal for Hessen Realschule / Gym - based on Kerncurricula Inhaltsfelder
|
|
8147
|
+
"realschule|10|mathematik": [
|
|
8148
|
+
{ id: "funktionen", label: "Funktionen" },
|
|
8149
|
+
{ id: "geometrie", label: "Geometrie" },
|
|
8150
|
+
{ id: "wahrscheinlichkeit", label: "Wahrscheinlichkeit und Statistik" }
|
|
8151
|
+
],
|
|
8152
|
+
"gymnasium|10|mathematik": [
|
|
8153
|
+
{ id: "analysis", label: "Analysis" },
|
|
8154
|
+
{ id: "vektoren", label: "Vektoren und Geometrie" },
|
|
8155
|
+
{ id: "stochastik", label: "Stochastik" }
|
|
8156
|
+
],
|
|
8157
|
+
"realschule|9|informatik": [
|
|
8158
|
+
{ id: "daten", label: "Daten und Information" },
|
|
8159
|
+
{ id: "algorithmen", label: "Algorithmen und Programmierung" },
|
|
8160
|
+
{ id: "netze", label: "Netze und Sicherheit" }
|
|
8161
|
+
],
|
|
8162
|
+
"realschule|9|physik": [
|
|
8163
|
+
{ id: "mechanik", label: "Mechanik" },
|
|
8164
|
+
{ id: "energie", label: "Energie" },
|
|
8165
|
+
{ id: "elektrizitaet", label: "Elektrizit\xE4t" }
|
|
8166
|
+
],
|
|
8167
|
+
"realschule|9|chemie": [
|
|
8168
|
+
{ id: "stoffe", label: "Stoffe und Reaktionen" },
|
|
8169
|
+
{ id: "atome", label: "Atome und Periodensystem" },
|
|
8170
|
+
{ id: "organisch", label: "Organische Verbindungen" }
|
|
8171
|
+
],
|
|
8172
|
+
"realschule|9|biologie": [
|
|
8173
|
+
{ id: "zelle", label: "Zelle und Stoffwechsel" },
|
|
8174
|
+
{ id: "vererbung", label: "Vererbung" },
|
|
8175
|
+
{ id: "oekologie", label: "\xD6kologie" }
|
|
8176
|
+
],
|
|
8177
|
+
"gymnasium|9|physik": [
|
|
8178
|
+
{ id: "optik", label: "Optik" },
|
|
8179
|
+
{ id: "mechanik-dynamik", label: "Mechanik und Dynamik" },
|
|
8180
|
+
{ id: "felder", label: "Elektrische und magnetische Felder" }
|
|
8181
|
+
],
|
|
8182
|
+
"gymnasium|9|chemie": [
|
|
8183
|
+
{ id: "bindung", label: "Chemische Bindung" },
|
|
8184
|
+
{ id: "reaktionsgeschwindigkeit", label: "Reaktionsgeschwindigkeit" },
|
|
8185
|
+
{ id: "saeure-base", label: "S\xE4ure-Base-Reaktionen" }
|
|
8186
|
+
],
|
|
8187
|
+
"gymnasium|9|biologie": [
|
|
8188
|
+
{ id: "genetik", label: "Genetik" },
|
|
8189
|
+
{ id: "evolution", label: "Evolution" },
|
|
8190
|
+
{ id: "neurobiologie", label: "Neurobiologie" }
|
|
8191
|
+
]
|
|
8192
|
+
},
|
|
8193
|
+
contentUrls: {
|
|
8194
|
+
"realschule|10|mathematik": "https://kultusministerium.hessen.de/Unterricht/Kerncurricula-und-Lehrplaene/Kerncurricula/sekundarstufe-i-kerncurricula",
|
|
8195
|
+
"gymnasium|10|mathematik": "https://kultusministerium.hessen.de/Unterricht/Kerncurricula-und-Lehrplaene/Kerncurricula/sekundarstufe-i-kerncurricula",
|
|
8196
|
+
"realschule|9|informatik": "https://kultusministerium.hessen.de/Unterricht/Kerncurricula-und-Lehrplaene/Kerncurricula/sekundarstufe-i-kerncurricula",
|
|
8197
|
+
"realschule|9|physik": "https://kultusministerium.hessen.de/Unterricht/Kerncurricula-und-Lehrplaene/Kerncurricula/sekundarstufe-i-kerncurricula",
|
|
8198
|
+
"realschule|9|chemie": "https://kultusministerium.hessen.de/Unterricht/Kerncurricula-und-Lehrplaene/Kerncurricula/sekundarstufe-i-kerncurricula",
|
|
8199
|
+
"realschule|9|biologie": "https://kultusministerium.hessen.de/Unterricht/Kerncurricula-und-Lehrplaene/Kerncurricula/sekundarstufe-i-kerncurricula",
|
|
8200
|
+
"gymnasium|9|physik": "https://kultusministerium.hessen.de/Unterricht/Kerncurricula-und-Lehrplaene/Kerncurricula/sekundarstufe-i-kerncurricula",
|
|
8201
|
+
"gymnasium|9|chemie": "https://kultusministerium.hessen.de/Unterricht/Kerncurricula-und-Lehrplaene/Kerncurricula/sekundarstufe-i-kerncurricula",
|
|
8202
|
+
"gymnasium|9|biologie": "https://kultusministerium.hessen.de/Unterricht/Kerncurricula-und-Lehrplaene/Kerncurricula/sekundarstufe-i-kerncurricula"
|
|
8203
|
+
}
|
|
8204
|
+
};
|
|
8205
|
+
|
|
8206
|
+
// src/cli/curriculum/providers/kerncurriculum-hessen/index.ts
|
|
8207
|
+
var kerncurriculumHessenProvider = {
|
|
8208
|
+
id: "kerncurriculum-hessen",
|
|
8209
|
+
country: "DE",
|
|
8210
|
+
countryLabel: "Deutschland",
|
|
8211
|
+
region: "HE",
|
|
8212
|
+
regionLabel: "Hessen",
|
|
8213
|
+
label: "Kerncurriculum (Hessen)",
|
|
8214
|
+
listSchoolTypes() {
|
|
8215
|
+
return KERNCURRICULUM_HESSEN_MANIFEST.schoolTypes;
|
|
8216
|
+
},
|
|
8217
|
+
listGrades(schoolType) {
|
|
8218
|
+
return (KERNCURRICULUM_HESSEN_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
8219
|
+
id,
|
|
8220
|
+
label: `Klasse ${id}`
|
|
8221
|
+
}));
|
|
8222
|
+
},
|
|
8223
|
+
listSubjects(schoolType, _grade) {
|
|
8224
|
+
return KERNCURRICULUM_HESSEN_MANIFEST.subjects[schoolType] || [];
|
|
8225
|
+
},
|
|
8226
|
+
listTracks(schoolType, grade, subject) {
|
|
8227
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
8228
|
+
return KERNCURRICULUM_HESSEN_MANIFEST.tracks[key] || [];
|
|
8229
|
+
},
|
|
8230
|
+
listTopics(selection) {
|
|
8231
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
8232
|
+
const list = KERNCURRICULUM_HESSEN_MANIFEST.topics[key] || [];
|
|
8233
|
+
return list.map((t2) => ({
|
|
8234
|
+
...t2,
|
|
8235
|
+
sourceRef: key
|
|
8236
|
+
}));
|
|
8237
|
+
},
|
|
8238
|
+
resolveTopic(topic) {
|
|
8239
|
+
const uri = KERNCURRICULUM_HESSEN_MANIFEST.contentUrls[topic.sourceRef];
|
|
8240
|
+
if (!uri) {
|
|
8241
|
+
throw new Error(
|
|
8242
|
+
`Kerncurriculum Hessen: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
8243
|
+
);
|
|
8244
|
+
}
|
|
8245
|
+
return {
|
|
8246
|
+
provider: "kerncurriculum-hessen",
|
|
8247
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
8248
|
+
uri
|
|
8249
|
+
};
|
|
8250
|
+
},
|
|
8251
|
+
extractTopics(html, topicIds) {
|
|
8252
|
+
const results = {};
|
|
8253
|
+
const headingMatches = Array.from(
|
|
8254
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
8255
|
+
);
|
|
8256
|
+
const sections = [];
|
|
8257
|
+
for (const m of headingMatches) {
|
|
8258
|
+
const headerText = cleanHtmlText5(m[1]).trim();
|
|
8259
|
+
const start = m.index + m[0].length;
|
|
8260
|
+
const nextHeading = html.indexOf("<h", start);
|
|
8261
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 1500);
|
|
8262
|
+
const content = cleanHtmlText5(rawContent).slice(0, 1500);
|
|
8263
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
8264
|
+
}
|
|
8265
|
+
for (const topicId of topicIds) {
|
|
8266
|
+
const hashIdx = topicId.indexOf("#");
|
|
8267
|
+
if (hashIdx === -1) continue;
|
|
8268
|
+
const key = topicId.substring(0, hashIdx);
|
|
8269
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
8270
|
+
const list = KERNCURRICULUM_HESSEN_MANIFEST.topics[key] ?? [];
|
|
8271
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
8272
|
+
if (!match) continue;
|
|
8273
|
+
const label = match.label;
|
|
8274
|
+
const normalizedLabel = normalizeForComparison5(label);
|
|
8275
|
+
let section = sections.find(
|
|
8276
|
+
(s) => normalizeForComparison5(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
8277
|
+
);
|
|
8278
|
+
if (!section && sections.length > 0) {
|
|
8279
|
+
section = sections.find((s) => s.content.length > 80) || sections[0];
|
|
8280
|
+
}
|
|
8281
|
+
if (section) {
|
|
8282
|
+
results[topicId] = `${label}
|
|
8283
|
+
|
|
8284
|
+
${section.content}`.trim();
|
|
8285
|
+
} else {
|
|
8286
|
+
results[topicId] = label;
|
|
8287
|
+
}
|
|
8288
|
+
}
|
|
8289
|
+
return results;
|
|
8290
|
+
}
|
|
8291
|
+
};
|
|
8292
|
+
function cleanHtmlText5(html) {
|
|
8293
|
+
let text = html.replace(
|
|
8294
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
8295
|
+
" "
|
|
8296
|
+
);
|
|
8297
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
8298
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
8299
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
8300
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
8301
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
8302
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
8303
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
8304
|
+
}
|
|
8305
|
+
function normalizeForComparison5(str) {
|
|
8306
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
8307
|
+
}
|
|
8308
|
+
|
|
8309
|
+
// src/cli/curriculum/providers/kerncurriculum-niedersachsen/manifest.ts
|
|
8310
|
+
var KERNCURRICULUM_NIEDERSACHSEN_MANIFEST = {
|
|
8311
|
+
schoolYear: "2025/2026",
|
|
8312
|
+
capturedOn: "2026-07-02",
|
|
8313
|
+
sourceRevision: "Kerncurricula Niedersachsen (CuVo)",
|
|
8314
|
+
schoolTypes: [
|
|
8315
|
+
{ id: "realschule", label: "Realschule" },
|
|
8316
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
8317
|
+
],
|
|
8318
|
+
grades: {
|
|
8319
|
+
realschule: ["7", "8", "9", "10"],
|
|
8320
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
8321
|
+
},
|
|
8322
|
+
subjects: {
|
|
8323
|
+
realschule: [
|
|
8324
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
8325
|
+
{ id: "informatik", label: "Informatik" },
|
|
8326
|
+
{ id: "physik", label: "Physik" },
|
|
8327
|
+
{ id: "chemie", label: "Chemie" },
|
|
8328
|
+
{ id: "biologie", label: "Biologie" }
|
|
8329
|
+
],
|
|
8330
|
+
gymnasium: [
|
|
8331
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
8332
|
+
{ id: "informatik", label: "Informatik" },
|
|
8333
|
+
{ id: "physik", label: "Physik" },
|
|
8334
|
+
{ id: "chemie", label: "Chemie" },
|
|
8335
|
+
{ id: "biologie", label: "Biologie" }
|
|
8336
|
+
]
|
|
8337
|
+
},
|
|
8338
|
+
tracks: {},
|
|
8339
|
+
topics: {
|
|
8340
|
+
"realschule|10|mathematik": [
|
|
8341
|
+
{ id: "algebra-funktionen", label: "Algebra und Funktionen" },
|
|
8342
|
+
{ id: "geometrie", label: "Geometrie" }
|
|
8343
|
+
],
|
|
8344
|
+
"gymnasium|10|mathematik": [
|
|
8345
|
+
{ id: "analysis", label: "Analysis" },
|
|
8346
|
+
{ id: "lineare-algebra", label: "Lineare Algebra" }
|
|
8347
|
+
],
|
|
8348
|
+
"realschule|9|physik": [
|
|
8349
|
+
{ id: "mechanik", label: "Mechanik" },
|
|
8350
|
+
{ id: "energie", label: "Energie und W\xE4rme" }
|
|
8351
|
+
],
|
|
8352
|
+
"realschule|9|chemie": [
|
|
8353
|
+
{ id: "reaktionen", label: "Chemische Reaktionen" },
|
|
8354
|
+
{ id: "stoffkreislauf", label: "Stoffkreisl\xE4ufe" }
|
|
8355
|
+
],
|
|
8356
|
+
"realschule|9|biologie": [
|
|
8357
|
+
{ id: "zellen", label: "Zellen" },
|
|
8358
|
+
{ id: "oekologie", label: "\xD6kologie" }
|
|
8359
|
+
],
|
|
8360
|
+
"gymnasium|9|physik": [
|
|
8361
|
+
{ id: "elektrizitaet", label: "Elektrizit\xE4t und Magnetismus" }
|
|
8362
|
+
],
|
|
8363
|
+
"gymnasium|9|chemie": [{ id: "bindungen", label: "Chemische Bindungen" }],
|
|
8364
|
+
"gymnasium|9|biologie": [{ id: "genetik", label: "Genetik" }]
|
|
8365
|
+
},
|
|
8366
|
+
contentUrls: {
|
|
8367
|
+
"realschule|10|mathematik": "https://cuvo.nibis.de/cuvo.php",
|
|
8368
|
+
"gymnasium|10|mathematik": "https://cuvo.nibis.de/cuvo.php",
|
|
8369
|
+
"realschule|9|physik": "https://cuvo.nibis.de/cuvo.php",
|
|
8370
|
+
"realschule|9|chemie": "https://cuvo.nibis.de/cuvo.php",
|
|
8371
|
+
"realschule|9|biologie": "https://cuvo.nibis.de/cuvo.php",
|
|
8372
|
+
"gymnasium|9|physik": "https://cuvo.nibis.de/cuvo.php",
|
|
8373
|
+
"gymnasium|9|chemie": "https://cuvo.nibis.de/cuvo.php",
|
|
8374
|
+
"gymnasium|9|biologie": "https://cuvo.nibis.de/cuvo.php"
|
|
8375
|
+
}
|
|
8376
|
+
};
|
|
8377
|
+
|
|
8378
|
+
// src/cli/curriculum/providers/kerncurriculum-niedersachsen/index.ts
|
|
8379
|
+
var kerncurriculumNiedersachsenProvider = {
|
|
8380
|
+
id: "kerncurriculum-niedersachsen",
|
|
8381
|
+
country: "DE",
|
|
8382
|
+
countryLabel: "Deutschland",
|
|
8383
|
+
region: "NI",
|
|
8384
|
+
regionLabel: "Niedersachsen",
|
|
8385
|
+
label: "Kerncurriculum (Niedersachsen)",
|
|
8386
|
+
listSchoolTypes() {
|
|
8387
|
+
return KERNCURRICULUM_NIEDERSACHSEN_MANIFEST.schoolTypes;
|
|
8388
|
+
},
|
|
8389
|
+
listGrades(schoolType) {
|
|
8390
|
+
return (KERNCURRICULUM_NIEDERSACHSEN_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
8391
|
+
id,
|
|
8392
|
+
label: `Klasse ${id}`
|
|
8393
|
+
}));
|
|
8394
|
+
},
|
|
8395
|
+
listSubjects(schoolType, _grade) {
|
|
8396
|
+
return KERNCURRICULUM_NIEDERSACHSEN_MANIFEST.subjects[schoolType] || [];
|
|
8397
|
+
},
|
|
8398
|
+
listTracks(schoolType, grade, subject) {
|
|
8399
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
8400
|
+
return KERNCURRICULUM_NIEDERSACHSEN_MANIFEST.tracks[key] || [];
|
|
8401
|
+
},
|
|
8402
|
+
listTopics(selection) {
|
|
8403
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
8404
|
+
const list = KERNCURRICULUM_NIEDERSACHSEN_MANIFEST.topics[key] || [];
|
|
8405
|
+
return list.map((t2) => ({
|
|
8406
|
+
...t2,
|
|
8407
|
+
sourceRef: key
|
|
8408
|
+
}));
|
|
8409
|
+
},
|
|
8410
|
+
resolveTopic(topic) {
|
|
8411
|
+
const uri = KERNCURRICULUM_NIEDERSACHSEN_MANIFEST.contentUrls[topic.sourceRef];
|
|
8412
|
+
if (!uri) {
|
|
8413
|
+
throw new Error(
|
|
8414
|
+
`Kerncurriculum Niedersachsen: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
8415
|
+
);
|
|
8416
|
+
}
|
|
8417
|
+
return {
|
|
8418
|
+
provider: "kerncurriculum-niedersachsen",
|
|
8419
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
8420
|
+
uri
|
|
8421
|
+
};
|
|
8422
|
+
},
|
|
8423
|
+
extractTopics(html, topicIds) {
|
|
8424
|
+
const results = {};
|
|
8425
|
+
const headingMatches = Array.from(
|
|
8426
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
8427
|
+
);
|
|
8428
|
+
const sections = [];
|
|
8429
|
+
for (const m of headingMatches) {
|
|
8430
|
+
const headerText = cleanHtmlText6(m[1]).trim();
|
|
8431
|
+
const start = m.index + m[0].length;
|
|
8432
|
+
const nextHeading = html.indexOf("<h", start);
|
|
8433
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 1500);
|
|
8434
|
+
const content = cleanHtmlText6(rawContent).slice(0, 1500);
|
|
8435
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
8436
|
+
}
|
|
8437
|
+
for (const topicId of topicIds) {
|
|
8438
|
+
const hashIdx = topicId.indexOf("#");
|
|
8439
|
+
if (hashIdx === -1) continue;
|
|
8440
|
+
const key = topicId.substring(0, hashIdx);
|
|
8441
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
8442
|
+
const list = KERNCURRICULUM_NIEDERSACHSEN_MANIFEST.topics[key] ?? [];
|
|
8443
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
8444
|
+
if (!match) continue;
|
|
8445
|
+
const label = match.label;
|
|
8446
|
+
const normalizedLabel = normalizeForComparison6(label);
|
|
8447
|
+
let section = sections.find(
|
|
8448
|
+
(s) => normalizeForComparison6(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
8449
|
+
);
|
|
8450
|
+
if (!section && sections.length > 0) {
|
|
8451
|
+
section = sections.find((s) => s.content.length > 80) || sections[0];
|
|
8452
|
+
}
|
|
8453
|
+
if (section) {
|
|
8454
|
+
results[topicId] = `${label}
|
|
8455
|
+
|
|
8456
|
+
${section.content}`.trim();
|
|
8457
|
+
} else {
|
|
8458
|
+
results[topicId] = label;
|
|
8459
|
+
}
|
|
8460
|
+
}
|
|
8461
|
+
return results;
|
|
8462
|
+
}
|
|
8463
|
+
};
|
|
8464
|
+
function cleanHtmlText6(html) {
|
|
8465
|
+
let text = html.replace(
|
|
8466
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
8467
|
+
" "
|
|
8468
|
+
);
|
|
8469
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
8470
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
8471
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
8472
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
8473
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
8474
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
8475
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
8476
|
+
}
|
|
8477
|
+
function normalizeForComparison6(str) {
|
|
8478
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
8479
|
+
}
|
|
8480
|
+
|
|
8481
|
+
// src/cli/curriculum/providers/kernlehrplan-nrw/manifest.ts
|
|
8482
|
+
var KERNLEHRPLAN_NRW_MANIFEST = {
|
|
8483
|
+
schoolYear: "2025/2026",
|
|
8484
|
+
capturedOn: "2026-07-02",
|
|
8485
|
+
sourceRevision: "Kernlehrpl\xE4ne Sekundarstufe I NRW (Lehrplannavigator)",
|
|
8486
|
+
schoolTypes: [
|
|
8487
|
+
{ id: "realschule", label: "Realschule" },
|
|
8488
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
8489
|
+
],
|
|
8490
|
+
grades: {
|
|
8491
|
+
realschule: ["7", "8", "9", "10"],
|
|
8492
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
8493
|
+
},
|
|
8494
|
+
subjects: {
|
|
8495
|
+
realschule: [
|
|
8496
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
8497
|
+
{ id: "informatik", label: "Informatik" },
|
|
8498
|
+
{ id: "physik", label: "Physik" },
|
|
8499
|
+
{ id: "chemie", label: "Chemie" },
|
|
8500
|
+
{ id: "biologie", label: "Biologie" }
|
|
8501
|
+
],
|
|
8502
|
+
gymnasium: [
|
|
8503
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
8504
|
+
{ id: "informatik", label: "Informatik" },
|
|
8505
|
+
{ id: "physik", label: "Physik" },
|
|
8506
|
+
{ id: "chemie", label: "Chemie" },
|
|
8507
|
+
{ id: "biologie", label: "Biologie" }
|
|
8508
|
+
]
|
|
8509
|
+
},
|
|
8510
|
+
tracks: {},
|
|
8511
|
+
topics: {
|
|
8512
|
+
// Minimal starter set for Realschule Mathematik (example for one grade)
|
|
8513
|
+
"realschule|10|mathematik": [
|
|
8514
|
+
{ id: "arithmetik-algebra", label: "Arithmetik und Algebra" },
|
|
8515
|
+
{ id: "funktionen", label: "Funktionen" },
|
|
8516
|
+
{ id: "geometrie", label: "Geometrie" },
|
|
8517
|
+
{ id: "stochastik", label: "Stochastik" }
|
|
8518
|
+
],
|
|
8519
|
+
// Minimal starter for Realschule Informatik (from UV examples in 5/6 + typical)
|
|
8520
|
+
"realschule|8|informatik": [
|
|
8521
|
+
{ id: "daten-codierung", label: "Daten und Codierung" },
|
|
8522
|
+
{ id: "algorithmen", label: "Algorithmen" },
|
|
8523
|
+
{
|
|
8524
|
+
id: "ki-datenbewusstsein",
|
|
8525
|
+
label: "K\xFCnstliche Intelligenz und Datenbewusstsein"
|
|
8526
|
+
}
|
|
8527
|
+
],
|
|
8528
|
+
// Starter for Gymnasium Mathematik (higher track)
|
|
8529
|
+
"gymnasium|10|mathematik": [
|
|
8530
|
+
{ id: "analysis", label: "Analysis / Funktionen" },
|
|
8531
|
+
{
|
|
8532
|
+
id: "lineare-algebra-geometrie",
|
|
8533
|
+
label: "Lineare Algebra und Geometrie"
|
|
8534
|
+
},
|
|
8535
|
+
{ id: "stochastik", label: "Stochastik" }
|
|
8536
|
+
],
|
|
8537
|
+
// Starter for Gymnasium WP Informatik
|
|
8538
|
+
"gymnasium|10|informatik": [
|
|
8539
|
+
{ id: "logische-schaltungen", label: "Logische Schaltungen" },
|
|
8540
|
+
{
|
|
8541
|
+
id: "algorithmen-programmierung",
|
|
8542
|
+
label: "Algorithmen und Programmierung"
|
|
8543
|
+
},
|
|
8544
|
+
{ id: "daten-netze-sicherheit", label: "Daten, Netze und Sicherheit" }
|
|
8545
|
+
],
|
|
8546
|
+
// Naturwissenschaften for Realschule example
|
|
8547
|
+
"realschule|9|physik": [
|
|
8548
|
+
{ id: "mechanik-kraefte", label: "Mechanik und Kr\xE4fte" },
|
|
8549
|
+
{ id: "waerme-energie", label: "W\xE4rme und Energie" },
|
|
8550
|
+
{ id: "elektrizitaet", label: "Elektrizit\xE4t" }
|
|
8551
|
+
],
|
|
8552
|
+
"realschule|9|chemie": [
|
|
8553
|
+
{ id: "stoffe-eigenschaften", label: "Stoffe und ihre Eigenschaften" },
|
|
8554
|
+
{ id: "reaktionen", label: "Chemische Reaktionen" },
|
|
8555
|
+
{ id: "atombau", label: "Atombau und Periodensystem" }
|
|
8556
|
+
],
|
|
8557
|
+
"realschule|9|biologie": [
|
|
8558
|
+
{ id: "zellen-lebewesen", label: "Zellen und Lebewesen" },
|
|
8559
|
+
{ id: "vererbung", label: "Vererbung und Genetik" },
|
|
8560
|
+
{ id: "oekosysteme", label: "\xD6kosysteme" }
|
|
8561
|
+
],
|
|
8562
|
+
// Naturwissenschaften for Gymnasium
|
|
8563
|
+
"gymnasium|9|physik": [
|
|
8564
|
+
{ id: "optik", label: "Optik" },
|
|
8565
|
+
{ id: "elektromagnetismus", label: "Elektromagnetismus" },
|
|
8566
|
+
{ id: "atomphysik", label: "Atom- und Kernphysik" }
|
|
8567
|
+
],
|
|
8568
|
+
"gymnasium|9|chemie": [
|
|
8569
|
+
{ id: "chemische-bindung", label: "Chemische Bindung" },
|
|
8570
|
+
{ id: "saeuren-basen-salze", label: "S\xE4uren, Basen, Salze" },
|
|
8571
|
+
{ id: "organische", label: "Organische Chemie" }
|
|
8572
|
+
],
|
|
8573
|
+
"gymnasium|9|biologie": [
|
|
8574
|
+
{ id: "genetik-evolution", label: "Genetik und Evolution" },
|
|
8575
|
+
{ id: "physiologie", label: "Physiologie des Menschen" },
|
|
8576
|
+
{ id: "oekologie-umwelt", label: "\xD6kologie und Umweltschutz" }
|
|
8577
|
+
]
|
|
8578
|
+
},
|
|
8579
|
+
contentUrls: {
|
|
8580
|
+
"realschule|10|mathematik": "https://lehrplannavigator.nrw.de/sekundarstufe-i/realschule/mathematik-neu-ab-20222023",
|
|
8581
|
+
"realschule|8|informatik": "https://lehrplannavigator.nrw.de/sekundarstufe-i/realschule/informatik-realschule-neu-ab-20212022",
|
|
8582
|
+
"gymnasium|10|mathematik": "https://lehrplannavigator.nrw.de/sekundarstufe-i/kernlehrplaene-fuer-das-gymnasium-ab-20192020/mathematik",
|
|
8583
|
+
"gymnasium|10|informatik": "https://lehrplannavigator.nrw.de/sekundarstufe-i/kernlehrplaene-fuer-das-gymnasium-ab-20192020/wp-informatik-neu-ab-20232024",
|
|
8584
|
+
"realschule|9|physik": "https://lehrplannavigator.nrw.de/sekundarstufe-i/realschule/physik",
|
|
8585
|
+
"realschule|9|chemie": "https://lehrplannavigator.nrw.de/sekundarstufe-i/realschule/chemie",
|
|
8586
|
+
"realschule|9|biologie": "https://lehrplannavigator.nrw.de/sekundarstufe-i/realschule/biologie",
|
|
8587
|
+
"gymnasium|9|physik": "https://lehrplannavigator.nrw.de/sekundarstufe-i/kernlehrplaene-fuer-das-gymnasium-ab-20192020/physik",
|
|
8588
|
+
"gymnasium|9|chemie": "https://lehrplannavigator.nrw.de/sekundarstufe-i/kernlehrplaene-fuer-das-gymnasium-ab-20192020/chemie",
|
|
8589
|
+
"gymnasium|9|biologie": "https://lehrplannavigator.nrw.de/sekundarstufe-i/kernlehrplaene-fuer-das-gymnasium-ab-20192020/biologie"
|
|
8590
|
+
}
|
|
8591
|
+
};
|
|
8592
|
+
|
|
8593
|
+
// src/cli/curriculum/providers/kernlehrplan-nrw/index.ts
|
|
8594
|
+
var kernlehrplanNrwProvider = {
|
|
8595
|
+
id: "kernlehrplan-nrw",
|
|
8596
|
+
country: "DE",
|
|
8597
|
+
countryLabel: "Deutschland",
|
|
8598
|
+
region: "NW",
|
|
8599
|
+
regionLabel: "Nordrhein-Westfalen",
|
|
8600
|
+
label: "Kernlehrplan (Nordrhein-Westfalen)",
|
|
8601
|
+
listSchoolTypes() {
|
|
8602
|
+
return KERNLEHRPLAN_NRW_MANIFEST.schoolTypes;
|
|
8603
|
+
},
|
|
8604
|
+
listGrades(schoolType) {
|
|
8605
|
+
return (KERNLEHRPLAN_NRW_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
8606
|
+
id,
|
|
8607
|
+
label: `Klasse ${id}`
|
|
8608
|
+
}));
|
|
8609
|
+
},
|
|
8610
|
+
listSubjects(schoolType, _grade) {
|
|
8611
|
+
return KERNLEHRPLAN_NRW_MANIFEST.subjects[schoolType] || [];
|
|
8612
|
+
},
|
|
8613
|
+
listTracks(schoolType, grade, subject) {
|
|
8614
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
8615
|
+
return KERNLEHRPLAN_NRW_MANIFEST.tracks[key] || [];
|
|
8616
|
+
},
|
|
8617
|
+
listTopics(selection) {
|
|
8618
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
8619
|
+
const list = KERNLEHRPLAN_NRW_MANIFEST.topics[key] || [];
|
|
8620
|
+
return list.map((t2) => ({
|
|
8621
|
+
...t2,
|
|
8622
|
+
sourceRef: key
|
|
8623
|
+
}));
|
|
8624
|
+
},
|
|
8625
|
+
resolveTopic(topic) {
|
|
8626
|
+
const uri = KERNLEHRPLAN_NRW_MANIFEST.contentUrls[topic.sourceRef];
|
|
8627
|
+
if (!uri) {
|
|
8628
|
+
throw new Error(
|
|
8629
|
+
`Kernlehrplan NRW: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
8630
|
+
);
|
|
8631
|
+
}
|
|
8632
|
+
return {
|
|
8633
|
+
provider: "kernlehrplan-nrw",
|
|
8634
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
8635
|
+
uri
|
|
8636
|
+
};
|
|
8637
|
+
},
|
|
8638
|
+
extractTopics(html, topicIds) {
|
|
8639
|
+
const results = {};
|
|
8640
|
+
const headingMatches = Array.from(
|
|
8641
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
8642
|
+
);
|
|
8643
|
+
const sections = [];
|
|
8644
|
+
for (const m of headingMatches) {
|
|
8645
|
+
const headerText = cleanHtmlText7(m[1]).trim();
|
|
8646
|
+
const start = m.index + m[0].length;
|
|
8647
|
+
const nextHeading = html.indexOf("<h", start);
|
|
8648
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 2e3);
|
|
8649
|
+
const content = cleanHtmlText7(rawContent).slice(0, 2e3);
|
|
8650
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
8651
|
+
}
|
|
8652
|
+
for (const topicId of topicIds) {
|
|
8653
|
+
const hashIdx = topicId.indexOf("#");
|
|
8654
|
+
if (hashIdx === -1) continue;
|
|
8655
|
+
const key = topicId.substring(0, hashIdx);
|
|
8656
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
8657
|
+
const list = KERNLEHRPLAN_NRW_MANIFEST.topics[key] ?? [];
|
|
8658
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
8659
|
+
if (!match) continue;
|
|
8660
|
+
const label = match.label;
|
|
8661
|
+
const normalizedLabel = normalizeForComparison7(label);
|
|
8662
|
+
let section = sections.find(
|
|
8663
|
+
(s) => normalizeForComparison7(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
8664
|
+
);
|
|
8665
|
+
if (!section && sections.length > 0) {
|
|
8666
|
+
section = sections.find((s) => s.content.length > 100) || sections[0];
|
|
8667
|
+
}
|
|
8668
|
+
if (section) {
|
|
8669
|
+
results[topicId] = `${label}
|
|
8670
|
+
|
|
8671
|
+
${section.content}`.trim();
|
|
8672
|
+
} else {
|
|
8673
|
+
results[topicId] = label;
|
|
8674
|
+
}
|
|
8675
|
+
}
|
|
8676
|
+
return results;
|
|
8677
|
+
}
|
|
8678
|
+
};
|
|
8679
|
+
function cleanHtmlText7(html) {
|
|
8680
|
+
let text = html.replace(
|
|
8681
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
8682
|
+
" "
|
|
8683
|
+
);
|
|
8684
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
8685
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
8686
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
8687
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
8688
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
8689
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'").replace(/–/g, "\u2013").replace(/—/g, "\u2014");
|
|
8690
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
8691
|
+
}
|
|
8692
|
+
function normalizeForComparison7(str) {
|
|
8693
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
8694
|
+
}
|
|
8695
|
+
|
|
8696
|
+
// src/cli/curriculum/providers/lehrplaene-rp/manifest.ts
|
|
8697
|
+
var LEHRPLAENE_RP_MANIFEST = {
|
|
8698
|
+
schoolYear: "2025/2026",
|
|
8699
|
+
capturedOn: "2026-07-02",
|
|
8700
|
+
sourceRevision: "Lehrpl\xE4ne Rheinland-Pfalz",
|
|
8701
|
+
schoolTypes: [
|
|
8702
|
+
{ id: "realschule-plus", label: "Realschule plus" },
|
|
8703
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
8704
|
+
],
|
|
8705
|
+
grades: {
|
|
8706
|
+
"realschule-plus": ["7", "8", "9", "10"],
|
|
8707
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
8708
|
+
},
|
|
8709
|
+
subjects: {
|
|
8710
|
+
"realschule-plus": [
|
|
8711
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
8712
|
+
{ id: "informatik", label: "Informatik" },
|
|
8713
|
+
{ id: "physik", label: "Physik" },
|
|
8714
|
+
{ id: "chemie", label: "Chemie" },
|
|
8715
|
+
{ id: "biologie", label: "Biologie" }
|
|
8716
|
+
],
|
|
8717
|
+
gymnasium: [
|
|
8718
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
8719
|
+
{ id: "informatik", label: "Informatik" },
|
|
8720
|
+
{ id: "physik", label: "Physik" },
|
|
8721
|
+
{ id: "chemie", label: "Chemie" },
|
|
8722
|
+
{ id: "biologie", label: "Biologie" }
|
|
8723
|
+
]
|
|
8724
|
+
},
|
|
8725
|
+
tracks: {},
|
|
8726
|
+
topics: {
|
|
8727
|
+
"realschule-plus|10|mathematik": [
|
|
8728
|
+
{ id: "funktionen", label: "Funktionen" }
|
|
8729
|
+
],
|
|
8730
|
+
"gymnasium|10|mathematik": [{ id: "analysis", label: "Analysis" }],
|
|
8731
|
+
"realschule-plus|9|physik": [{ id: "mechanik", label: "Mechanik" }],
|
|
8732
|
+
"realschule-plus|9|chemie": [{ id: "reaktionen", label: "Reaktionen" }],
|
|
8733
|
+
"realschule-plus|9|biologie": [{ id: "zellen", label: "Zellen" }],
|
|
8734
|
+
"gymnasium|9|physik": [{ id: "elektrizitaet", label: "Elektrizit\xE4t" }],
|
|
8735
|
+
"gymnasium|9|chemie": [{ id: "bindungen", label: "Bindungen" }],
|
|
8736
|
+
"gymnasium|9|biologie": [{ id: "genetik", label: "Genetik" }]
|
|
8737
|
+
},
|
|
8738
|
+
contentUrls: {
|
|
8739
|
+
"realschule-plus|10|mathematik": "https://lehrplaene.bildung-rp.de/",
|
|
8740
|
+
"gymnasium|10|mathematik": "https://lehrplaene.bildung-rp.de/",
|
|
8741
|
+
"realschule-plus|9|physik": "https://lehrplaene.bildung-rp.de/",
|
|
8742
|
+
"realschule-plus|9|chemie": "https://lehrplaene.bildung-rp.de/",
|
|
8743
|
+
"realschule-plus|9|biologie": "https://lehrplaene.bildung-rp.de/",
|
|
8744
|
+
"gymnasium|9|physik": "https://lehrplaene.bildung-rp.de/",
|
|
8745
|
+
"gymnasium|9|chemie": "https://lehrplaene.bildung-rp.de/",
|
|
8746
|
+
"gymnasium|9|biologie": "https://lehrplaene.bildung-rp.de/"
|
|
8747
|
+
}
|
|
8748
|
+
};
|
|
8749
|
+
|
|
8750
|
+
// src/cli/curriculum/providers/lehrplaene-rp/index.ts
|
|
8751
|
+
var lehrplaeneRpProvider = {
|
|
8752
|
+
id: "lehrplaene-rp",
|
|
8753
|
+
country: "DE",
|
|
8754
|
+
countryLabel: "Deutschland",
|
|
8755
|
+
region: "RP",
|
|
8756
|
+
regionLabel: "Rheinland-Pfalz",
|
|
8757
|
+
label: "Lehrpl\xE4ne (Rheinland-Pfalz)",
|
|
8758
|
+
listSchoolTypes() {
|
|
8759
|
+
return LEHRPLAENE_RP_MANIFEST.schoolTypes;
|
|
8760
|
+
},
|
|
8761
|
+
listGrades(schoolType) {
|
|
8762
|
+
return (LEHRPLAENE_RP_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
8763
|
+
id,
|
|
8764
|
+
label: `Klasse ${id}`
|
|
8765
|
+
}));
|
|
8766
|
+
},
|
|
8767
|
+
listSubjects(schoolType, _grade) {
|
|
8768
|
+
return LEHRPLAENE_RP_MANIFEST.subjects[schoolType] || [];
|
|
8769
|
+
},
|
|
8770
|
+
listTracks(schoolType, grade, subject) {
|
|
8771
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
8772
|
+
return LEHRPLAENE_RP_MANIFEST.tracks[key] || [];
|
|
8773
|
+
},
|
|
8774
|
+
listTopics(selection) {
|
|
8775
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
8776
|
+
const list = LEHRPLAENE_RP_MANIFEST.topics[key] || [];
|
|
8777
|
+
return list.map((t2) => ({
|
|
8778
|
+
...t2,
|
|
8779
|
+
sourceRef: key
|
|
8780
|
+
}));
|
|
8781
|
+
},
|
|
8782
|
+
resolveTopic(topic) {
|
|
8783
|
+
const uri = LEHRPLAENE_RP_MANIFEST.contentUrls[topic.sourceRef];
|
|
8784
|
+
if (!uri) {
|
|
8785
|
+
throw new Error(
|
|
8786
|
+
`Lehrpl\xE4ne RP: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
8787
|
+
);
|
|
8788
|
+
}
|
|
8789
|
+
return {
|
|
8790
|
+
provider: "lehrplaene-rp",
|
|
8791
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
8792
|
+
uri
|
|
8793
|
+
};
|
|
8794
|
+
},
|
|
8795
|
+
extractTopics(html, topicIds) {
|
|
8796
|
+
const results = {};
|
|
8797
|
+
const headingMatches = Array.from(
|
|
8798
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
8799
|
+
);
|
|
8800
|
+
const sections = [];
|
|
8801
|
+
for (const m of headingMatches) {
|
|
8802
|
+
const headerText = cleanHtmlText8(m[1]).trim();
|
|
8803
|
+
const start = m.index + m[0].length;
|
|
8804
|
+
const nextHeading = html.indexOf("<h", start);
|
|
8805
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 1500);
|
|
8806
|
+
const content = cleanHtmlText8(rawContent).slice(0, 1500);
|
|
8807
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
8808
|
+
}
|
|
8809
|
+
for (const topicId of topicIds) {
|
|
8810
|
+
const hashIdx = topicId.indexOf("#");
|
|
8811
|
+
if (hashIdx === -1) continue;
|
|
8812
|
+
const key = topicId.substring(0, hashIdx);
|
|
8813
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
8814
|
+
const list = LEHRPLAENE_RP_MANIFEST.topics[key] ?? [];
|
|
8815
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
8816
|
+
if (!match) continue;
|
|
8817
|
+
const label = match.label;
|
|
8818
|
+
const normalizedLabel = normalizeForComparison8(label);
|
|
8819
|
+
let section = sections.find(
|
|
8820
|
+
(s) => normalizeForComparison8(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
8821
|
+
);
|
|
8822
|
+
if (!section && sections.length > 0) {
|
|
8823
|
+
section = sections.find((s) => s.content.length > 80) || sections[0];
|
|
8824
|
+
}
|
|
8825
|
+
if (section) {
|
|
8826
|
+
results[topicId] = `${label}
|
|
8827
|
+
|
|
8828
|
+
${section.content}`.trim();
|
|
8829
|
+
} else {
|
|
8830
|
+
results[topicId] = label;
|
|
8831
|
+
}
|
|
8832
|
+
}
|
|
8833
|
+
return results;
|
|
8834
|
+
}
|
|
8835
|
+
};
|
|
8836
|
+
function cleanHtmlText8(html) {
|
|
8837
|
+
let text = html.replace(
|
|
8838
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
8839
|
+
" "
|
|
8840
|
+
);
|
|
8841
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
8842
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
8843
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
8844
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
8845
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
8846
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
8847
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
8848
|
+
}
|
|
8849
|
+
function normalizeForComparison8(str) {
|
|
8850
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
8851
|
+
}
|
|
8852
|
+
|
|
8853
|
+
// src/cli/curriculum/providers/lehrplan-saarland/manifest.ts
|
|
8854
|
+
var LEHRPLAN_SAARLAND_MANIFEST = {
|
|
8855
|
+
schoolYear: "2025/2026",
|
|
8856
|
+
capturedOn: "2026-07-02",
|
|
8857
|
+
sourceRevision: "Lehrpl\xE4ne Saarland",
|
|
8858
|
+
schoolTypes: [
|
|
8859
|
+
{ id: "gemeinschaftsschule", label: "Gemeinschaftsschule" },
|
|
8860
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
8861
|
+
],
|
|
8862
|
+
grades: {
|
|
8863
|
+
gemeinschaftsschule: ["7", "8", "9", "10"],
|
|
8864
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
8865
|
+
},
|
|
8866
|
+
subjects: {
|
|
8867
|
+
gemeinschaftsschule: [
|
|
8868
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
8869
|
+
{ id: "informatik", label: "Informatik" },
|
|
8870
|
+
{ id: "physik", label: "Physik" },
|
|
8871
|
+
{ id: "chemie", label: "Chemie" },
|
|
8872
|
+
{ id: "biologie", label: "Biologie" }
|
|
8873
|
+
],
|
|
8874
|
+
gymnasium: [
|
|
8875
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
8876
|
+
{ id: "informatik", label: "Informatik" },
|
|
8877
|
+
{ id: "physik", label: "Physik" },
|
|
8878
|
+
{ id: "chemie", label: "Chemie" },
|
|
8879
|
+
{ id: "biologie", label: "Biologie" }
|
|
8880
|
+
]
|
|
8881
|
+
},
|
|
8882
|
+
tracks: {},
|
|
8883
|
+
topics: {
|
|
8884
|
+
"gemeinschaftsschule|10|mathematik": [
|
|
8885
|
+
{ id: "funktionen", label: "Funktionen" }
|
|
8886
|
+
],
|
|
8887
|
+
"gymnasium|10|mathematik": [{ id: "analysis", label: "Analysis" }],
|
|
8888
|
+
"gemeinschaftsschule|9|physik": [{ id: "mechanik", label: "Mechanik" }],
|
|
8889
|
+
"gemeinschaftsschule|9|chemie": [{ id: "reaktionen", label: "Reaktionen" }],
|
|
8890
|
+
"gemeinschaftsschule|9|biologie": [{ id: "zellen", label: "Zellen" }],
|
|
8891
|
+
"gymnasium|9|physik": [{ id: "elektrizitaet", label: "Elektrizit\xE4t" }],
|
|
8892
|
+
"gymnasium|9|chemie": [{ id: "bindungen", label: "Bindungen" }],
|
|
8893
|
+
"gymnasium|9|biologie": [{ id: "genetik", label: "Genetik" }]
|
|
8894
|
+
},
|
|
8895
|
+
contentUrls: {
|
|
8896
|
+
"gemeinschaftsschule|10|mathematik": "https://www.saarland.de/mbk/DE/portale/bildungsserver/bildungsthemen/lehrplaenehandreichungen",
|
|
8897
|
+
"gymnasium|10|mathematik": "https://www.saarland.de/mbk/DE/portale/bildungsserver/bildungsthemen/lehrplaenehandreichungen",
|
|
8898
|
+
"gemeinschaftsschule|9|physik": "https://www.saarland.de/mbk/DE/portale/bildungsserver/bildungsthemen/lehrplaenehandreichungen",
|
|
8899
|
+
"gemeinschaftsschule|9|chemie": "https://www.saarland.de/mbk/DE/portale/bildungsserver/bildungsthemen/lehrplaenehandreichungen",
|
|
8900
|
+
"gemeinschaftsschule|9|biologie": "https://www.saarland.de/mbk/DE/portale/bildungsserver/bildungsthemen/lehrplaenehandreichungen",
|
|
8901
|
+
"gymnasium|9|physik": "https://www.saarland.de/mbk/DE/portale/bildungsserver/bildungsthemen/lehrplaenehandreichungen",
|
|
8902
|
+
"gymnasium|9|chemie": "https://www.saarland.de/mbk/DE/portale/bildungsserver/bildungsthemen/lehrplaenehandreichungen",
|
|
8903
|
+
"gymnasium|9|biologie": "https://www.saarland.de/mbk/DE/portale/bildungsserver/bildungsthemen/lehrplaenehandreichungen"
|
|
8904
|
+
}
|
|
8905
|
+
};
|
|
8906
|
+
|
|
8907
|
+
// src/cli/curriculum/providers/lehrplan-saarland/index.ts
|
|
8908
|
+
var lehrplanSaarlandProvider = {
|
|
8909
|
+
id: "lehrplan-saarland",
|
|
8910
|
+
country: "DE",
|
|
8911
|
+
countryLabel: "Deutschland",
|
|
8912
|
+
region: "SL",
|
|
8913
|
+
regionLabel: "Saarland",
|
|
8914
|
+
label: "Lehrplan (Saarland)",
|
|
8915
|
+
listSchoolTypes() {
|
|
8916
|
+
return LEHRPLAN_SAARLAND_MANIFEST.schoolTypes;
|
|
8917
|
+
},
|
|
8918
|
+
listGrades(schoolType) {
|
|
8919
|
+
return (LEHRPLAN_SAARLAND_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
8920
|
+
id,
|
|
8921
|
+
label: `Klasse ${id}`
|
|
8922
|
+
}));
|
|
8923
|
+
},
|
|
8924
|
+
listSubjects(schoolType, _grade) {
|
|
8925
|
+
return LEHRPLAN_SAARLAND_MANIFEST.subjects[schoolType] || [];
|
|
8926
|
+
},
|
|
8927
|
+
listTracks(schoolType, grade, subject) {
|
|
8928
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
8929
|
+
return LEHRPLAN_SAARLAND_MANIFEST.tracks[key] || [];
|
|
8930
|
+
},
|
|
8931
|
+
listTopics(selection) {
|
|
8932
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
8933
|
+
const list = LEHRPLAN_SAARLAND_MANIFEST.topics[key] || [];
|
|
8934
|
+
return list.map((t2) => ({
|
|
8935
|
+
...t2,
|
|
8936
|
+
sourceRef: key
|
|
8937
|
+
}));
|
|
8938
|
+
},
|
|
8939
|
+
resolveTopic(topic) {
|
|
8940
|
+
const uri = LEHRPLAN_SAARLAND_MANIFEST.contentUrls[topic.sourceRef];
|
|
8941
|
+
if (!uri) {
|
|
8942
|
+
throw new Error(
|
|
8943
|
+
`Lehrplan Saarland: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
8944
|
+
);
|
|
8945
|
+
}
|
|
8946
|
+
return {
|
|
8947
|
+
provider: "lehrplan-saarland",
|
|
8948
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
8949
|
+
uri
|
|
8950
|
+
};
|
|
8951
|
+
},
|
|
8952
|
+
extractTopics(html, topicIds) {
|
|
8953
|
+
const results = {};
|
|
8954
|
+
const headingMatches = Array.from(
|
|
8955
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
8956
|
+
);
|
|
8957
|
+
const sections = [];
|
|
8958
|
+
for (const m of headingMatches) {
|
|
8959
|
+
const headerText = cleanHtmlText9(m[1]).trim();
|
|
8960
|
+
const start = m.index + m[0].length;
|
|
8961
|
+
const nextHeading = html.indexOf("<h", start);
|
|
8962
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 1500);
|
|
8963
|
+
const content = cleanHtmlText9(rawContent).slice(0, 1500);
|
|
8964
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
8965
|
+
}
|
|
8966
|
+
for (const topicId of topicIds) {
|
|
8967
|
+
const hashIdx = topicId.indexOf("#");
|
|
8968
|
+
if (hashIdx === -1) continue;
|
|
8969
|
+
const key = topicId.substring(0, hashIdx);
|
|
8970
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
8971
|
+
const list = LEHRPLAN_SAARLAND_MANIFEST.topics[key] ?? [];
|
|
8972
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
8973
|
+
if (!match) continue;
|
|
8974
|
+
const label = match.label;
|
|
8975
|
+
const normalizedLabel = normalizeForComparison9(label);
|
|
8976
|
+
let section = sections.find(
|
|
8977
|
+
(s) => normalizeForComparison9(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
8978
|
+
);
|
|
8979
|
+
if (!section && sections.length > 0) {
|
|
8980
|
+
section = sections.find((s) => s.content.length > 80) || sections[0];
|
|
8981
|
+
}
|
|
8982
|
+
if (section) {
|
|
8983
|
+
results[topicId] = `${label}
|
|
8984
|
+
|
|
8985
|
+
${section.content}`.trim();
|
|
8986
|
+
} else {
|
|
8987
|
+
results[topicId] = label;
|
|
8988
|
+
}
|
|
8989
|
+
}
|
|
8990
|
+
return results;
|
|
8991
|
+
}
|
|
8992
|
+
};
|
|
8993
|
+
function cleanHtmlText9(html) {
|
|
8994
|
+
let text = html.replace(
|
|
8995
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
8996
|
+
" "
|
|
8997
|
+
);
|
|
8998
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
8999
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
9000
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
9001
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
9002
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
9003
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
9004
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
9005
|
+
}
|
|
9006
|
+
function normalizeForComparison9(str) {
|
|
9007
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
9008
|
+
}
|
|
9009
|
+
|
|
9010
|
+
// src/cli/curriculum/providers/lehrplan-sachsen/manifest.ts
|
|
9011
|
+
var LEHRPLAN_SACHSEN_MANIFEST = {
|
|
9012
|
+
schoolYear: "2025/2026",
|
|
9013
|
+
capturedOn: "2026-07-02",
|
|
9014
|
+
sourceRevision: "S\xE4chsische Lehrpl\xE4ne (Datenbank)",
|
|
9015
|
+
schoolTypes: [
|
|
9016
|
+
{ id: "oberschule", label: "Oberschule (Realschule)" },
|
|
9017
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
9018
|
+
],
|
|
9019
|
+
grades: {
|
|
9020
|
+
oberschule: ["7", "8", "9", "10"],
|
|
9021
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
9022
|
+
},
|
|
9023
|
+
subjects: {
|
|
9024
|
+
oberschule: [
|
|
9025
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
9026
|
+
{ id: "informatik", label: "Informatik" },
|
|
9027
|
+
{ id: "physik", label: "Physik" },
|
|
9028
|
+
{ id: "chemie", label: "Chemie" },
|
|
9029
|
+
{ id: "biologie", label: "Biologie" }
|
|
9030
|
+
],
|
|
9031
|
+
gymnasium: [
|
|
9032
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
9033
|
+
{ id: "informatik", label: "Informatik" },
|
|
9034
|
+
{ id: "physik", label: "Physik" },
|
|
9035
|
+
{ id: "chemie", label: "Chemie" },
|
|
9036
|
+
{ id: "biologie", label: "Biologie" }
|
|
9037
|
+
]
|
|
9038
|
+
},
|
|
9039
|
+
tracks: {},
|
|
9040
|
+
topics: {
|
|
9041
|
+
"oberschule|10|mathematik": [
|
|
9042
|
+
{ id: "prozentrechnung", label: "Prozentrechnung und Zinsrechnung" },
|
|
9043
|
+
{ id: "funktionen", label: "Funktionale Zusammenh\xE4nge" },
|
|
9044
|
+
{ id: "geometrie", label: "Geometrie und Messen" }
|
|
9045
|
+
],
|
|
9046
|
+
"gymnasium|10|mathematik": [
|
|
9047
|
+
{ id: "analysis", label: "Analysis und Funktionen" },
|
|
9048
|
+
{ id: "geometrie", label: "Analytische Geometrie" }
|
|
9049
|
+
],
|
|
9050
|
+
"oberschule|9|informatik": [
|
|
9051
|
+
{ id: "algorithmen", label: "Algorithmen und Programmierung" },
|
|
9052
|
+
{ id: "daten", label: "Daten und Datenschutz" }
|
|
9053
|
+
],
|
|
9054
|
+
"oberschule|9|physik": [
|
|
9055
|
+
{ id: "mechanik", label: "Mechanik und Bewegung" },
|
|
9056
|
+
{ id: "energie", label: "Energie und W\xE4rme" }
|
|
9057
|
+
],
|
|
9058
|
+
"oberschule|9|chemie": [
|
|
9059
|
+
{ id: "stoffe", label: "Stoffe und chemische Reaktionen" },
|
|
9060
|
+
{ id: "atome", label: "Atome und Periodensystem" }
|
|
9061
|
+
],
|
|
9062
|
+
"oberschule|9|biologie": [
|
|
9063
|
+
{ id: "zellen", label: "Zellen und Gewebe" },
|
|
9064
|
+
{ id: "oekologie", label: "\xD6kologie und Umwelt" }
|
|
9065
|
+
],
|
|
9066
|
+
"gymnasium|9|physik": [
|
|
9067
|
+
{ id: "elektrizitaet", label: "Elektrizit\xE4t und Magnetismus" },
|
|
9068
|
+
{ id: "optik", label: "Optik" }
|
|
9069
|
+
],
|
|
9070
|
+
"gymnasium|9|chemie": [
|
|
9071
|
+
{ id: "bindungen", label: "Chemische Bindungen" },
|
|
9072
|
+
{ id: "reaktionen", label: "Chemische Reaktionen" }
|
|
9073
|
+
],
|
|
9074
|
+
"gymnasium|9|biologie": [
|
|
9075
|
+
{ id: "genetik", label: "Genetik und Vererbung" },
|
|
9076
|
+
{ id: "evolution", label: "Evolution" }
|
|
9077
|
+
]
|
|
9078
|
+
},
|
|
9079
|
+
contentUrls: {
|
|
9080
|
+
"oberschule|10|mathematik": "https://www.schulportal.sachsen.de/lplandb/",
|
|
9081
|
+
"gymnasium|10|mathematik": "https://www.schulportal.sachsen.de/lplandb/",
|
|
9082
|
+
"oberschule|9|informatik": "https://www.schulportal.sachsen.de/lplandb/",
|
|
9083
|
+
"oberschule|9|physik": "https://www.schulportal.sachsen.de/lplandb/",
|
|
9084
|
+
"oberschule|9|chemie": "https://www.schulportal.sachsen.de/lplandb/",
|
|
9085
|
+
"oberschule|9|biologie": "https://www.schulportal.sachsen.de/lplandb/",
|
|
9086
|
+
"gymnasium|9|physik": "https://www.schulportal.sachsen.de/lplandb/",
|
|
9087
|
+
"gymnasium|9|chemie": "https://www.schulportal.sachsen.de/lplandb/",
|
|
9088
|
+
"gymnasium|9|biologie": "https://www.schulportal.sachsen.de/lplandb/"
|
|
9089
|
+
}
|
|
9090
|
+
};
|
|
9091
|
+
|
|
9092
|
+
// src/cli/curriculum/providers/lehrplan-sachsen/index.ts
|
|
9093
|
+
var lehrplanSachsenProvider = {
|
|
9094
|
+
id: "lehrplan-sachsen",
|
|
9095
|
+
country: "DE",
|
|
9096
|
+
countryLabel: "Deutschland",
|
|
9097
|
+
region: "SN",
|
|
9098
|
+
regionLabel: "Sachsen",
|
|
9099
|
+
label: "Lehrplan (Sachsen)",
|
|
9100
|
+
listSchoolTypes() {
|
|
9101
|
+
return LEHRPLAN_SACHSEN_MANIFEST.schoolTypes;
|
|
9102
|
+
},
|
|
9103
|
+
listGrades(schoolType) {
|
|
9104
|
+
return (LEHRPLAN_SACHSEN_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
9105
|
+
id,
|
|
9106
|
+
label: `Klasse ${id}`
|
|
9107
|
+
}));
|
|
9108
|
+
},
|
|
9109
|
+
listSubjects(schoolType, _grade) {
|
|
9110
|
+
return LEHRPLAN_SACHSEN_MANIFEST.subjects[schoolType] || [];
|
|
9111
|
+
},
|
|
9112
|
+
listTracks(schoolType, grade, subject) {
|
|
9113
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
9114
|
+
return LEHRPLAN_SACHSEN_MANIFEST.tracks[key] || [];
|
|
9115
|
+
},
|
|
9116
|
+
listTopics(selection) {
|
|
9117
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
9118
|
+
const list = LEHRPLAN_SACHSEN_MANIFEST.topics[key] || [];
|
|
9119
|
+
return list.map((t2) => ({
|
|
9120
|
+
...t2,
|
|
9121
|
+
sourceRef: key
|
|
9122
|
+
}));
|
|
9123
|
+
},
|
|
9124
|
+
resolveTopic(topic) {
|
|
9125
|
+
const uri = LEHRPLAN_SACHSEN_MANIFEST.contentUrls[topic.sourceRef];
|
|
9126
|
+
if (!uri) {
|
|
9127
|
+
throw new Error(
|
|
9128
|
+
`Lehrplan Sachsen: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
9129
|
+
);
|
|
9130
|
+
}
|
|
9131
|
+
return {
|
|
9132
|
+
provider: "lehrplan-sachsen",
|
|
9133
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
9134
|
+
uri
|
|
9135
|
+
};
|
|
9136
|
+
},
|
|
9137
|
+
extractTopics(html, topicIds) {
|
|
9138
|
+
const results = {};
|
|
9139
|
+
const headingMatches = Array.from(
|
|
9140
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
9141
|
+
);
|
|
9142
|
+
const sections = [];
|
|
9143
|
+
for (const m of headingMatches) {
|
|
9144
|
+
const headerText = cleanHtmlText10(m[1]).trim();
|
|
9145
|
+
const start = m.index + m[0].length;
|
|
9146
|
+
const nextHeading = html.indexOf("<h", start);
|
|
9147
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 1500);
|
|
9148
|
+
const content = cleanHtmlText10(rawContent).slice(0, 1500);
|
|
9149
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
9150
|
+
}
|
|
9151
|
+
for (const topicId of topicIds) {
|
|
9152
|
+
const hashIdx = topicId.indexOf("#");
|
|
9153
|
+
if (hashIdx === -1) continue;
|
|
9154
|
+
const key = topicId.substring(0, hashIdx);
|
|
9155
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
9156
|
+
const list = LEHRPLAN_SACHSEN_MANIFEST.topics[key] ?? [];
|
|
9157
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
9158
|
+
if (!match) continue;
|
|
9159
|
+
const label = match.label;
|
|
9160
|
+
const normalizedLabel = normalizeForComparison10(label);
|
|
9161
|
+
let section = sections.find(
|
|
9162
|
+
(s) => normalizeForComparison10(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
9163
|
+
);
|
|
9164
|
+
if (!section && sections.length > 0) {
|
|
9165
|
+
section = sections.find((s) => s.content.length > 80) || sections[0];
|
|
9166
|
+
}
|
|
9167
|
+
if (section) {
|
|
9168
|
+
results[topicId] = `${label}
|
|
9169
|
+
|
|
9170
|
+
${section.content}`.trim();
|
|
9171
|
+
} else {
|
|
9172
|
+
results[topicId] = label;
|
|
9173
|
+
}
|
|
9174
|
+
}
|
|
9175
|
+
return results;
|
|
9176
|
+
}
|
|
9177
|
+
};
|
|
9178
|
+
function cleanHtmlText10(html) {
|
|
9179
|
+
let text = html.replace(
|
|
9180
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
9181
|
+
" "
|
|
9182
|
+
);
|
|
9183
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
9184
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
9185
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
9186
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
9187
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
9188
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
9189
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
9190
|
+
}
|
|
9191
|
+
function normalizeForComparison10(str) {
|
|
9192
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
9193
|
+
}
|
|
9194
|
+
|
|
9195
|
+
// src/cli/curriculum/providers/lehrplan-thueringen/manifest.ts
|
|
9196
|
+
var LEHRPLAN_THUERINGEN_MANIFEST = {
|
|
9197
|
+
schoolYear: "2025/2026",
|
|
9198
|
+
capturedOn: "2026-07-02",
|
|
9199
|
+
sourceRevision: "Lehrpl\xE4ne Th\xFCringen",
|
|
9200
|
+
schoolTypes: [
|
|
9201
|
+
{ id: "regelschule", label: "Regelschule" },
|
|
9202
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
9203
|
+
],
|
|
9204
|
+
grades: {
|
|
9205
|
+
regelschule: ["7", "8", "9", "10"],
|
|
9206
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
9207
|
+
},
|
|
9208
|
+
subjects: {
|
|
9209
|
+
regelschule: [
|
|
9210
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
9211
|
+
{ id: "informatik", label: "Informatik" },
|
|
9212
|
+
{ id: "physik", label: "Physik" },
|
|
9213
|
+
{ id: "chemie", label: "Chemie" },
|
|
9214
|
+
{ id: "biologie", label: "Biologie" }
|
|
9215
|
+
],
|
|
9216
|
+
gymnasium: [
|
|
9217
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
9218
|
+
{ id: "informatik", label: "Informatik" },
|
|
9219
|
+
{ id: "physik", label: "Physik" },
|
|
9220
|
+
{ id: "chemie", label: "Chemie" },
|
|
9221
|
+
{ id: "biologie", label: "Biologie" }
|
|
9222
|
+
]
|
|
9223
|
+
},
|
|
9224
|
+
tracks: {},
|
|
9225
|
+
topics: {
|
|
9226
|
+
"regelschule|10|mathematik": [{ id: "funktionen", label: "Funktionen" }],
|
|
9227
|
+
"gymnasium|10|mathematik": [{ id: "analysis", label: "Analysis" }],
|
|
9228
|
+
"regelschule|9|physik": [{ id: "mechanik", label: "Mechanik" }],
|
|
9229
|
+
"regelschule|9|chemie": [{ id: "reaktionen", label: "Reaktionen" }],
|
|
9230
|
+
"regelschule|9|biologie": [{ id: "zellen", label: "Zellen" }],
|
|
9231
|
+
"gymnasium|9|physik": [{ id: "elektrizitaet", label: "Elektrizit\xE4t" }],
|
|
9232
|
+
"gymnasium|9|chemie": [{ id: "bindungen", label: "Bindungen" }],
|
|
9233
|
+
"gymnasium|9|biologie": [{ id: "genetik", label: "Genetik" }]
|
|
9234
|
+
},
|
|
9235
|
+
contentUrls: {
|
|
9236
|
+
"regelschule|10|mathematik": "https://www.schulportal-thueringen.de/web/guest/lehrplaene",
|
|
9237
|
+
"gymnasium|10|mathematik": "https://www.schulportal-thueringen.de/web/guest/lehrplaene",
|
|
9238
|
+
"regelschule|9|physik": "https://www.schulportal-thueringen.de/web/guest/lehrplaene",
|
|
9239
|
+
"regelschule|9|chemie": "https://www.schulportal-thueringen.de/web/guest/lehrplaene",
|
|
9240
|
+
"regelschule|9|biologie": "https://www.schulportal-thueringen.de/web/guest/lehrplaene",
|
|
9241
|
+
"gymnasium|9|physik": "https://www.schulportal-thueringen.de/web/guest/lehrplaene",
|
|
9242
|
+
"gymnasium|9|chemie": "https://www.schulportal-thueringen.de/web/guest/lehrplaene",
|
|
9243
|
+
"gymnasium|9|biologie": "https://www.schulportal-thueringen.de/web/guest/lehrplaene"
|
|
9244
|
+
}
|
|
9245
|
+
};
|
|
9246
|
+
|
|
9247
|
+
// src/cli/curriculum/providers/lehrplan-thueringen/index.ts
|
|
9248
|
+
var lehrplanThueringenProvider = {
|
|
9249
|
+
id: "lehrplan-thueringen",
|
|
9250
|
+
country: "DE",
|
|
9251
|
+
countryLabel: "Deutschland",
|
|
9252
|
+
region: "TH",
|
|
9253
|
+
regionLabel: "Th\xFCringen",
|
|
9254
|
+
label: "Lehrplan (Th\xFCringen)",
|
|
9255
|
+
listSchoolTypes() {
|
|
9256
|
+
return LEHRPLAN_THUERINGEN_MANIFEST.schoolTypes;
|
|
9257
|
+
},
|
|
9258
|
+
listGrades(schoolType) {
|
|
9259
|
+
return (LEHRPLAN_THUERINGEN_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
9260
|
+
id,
|
|
9261
|
+
label: `Klasse ${id}`
|
|
9262
|
+
}));
|
|
9263
|
+
},
|
|
9264
|
+
listSubjects(schoolType, _grade) {
|
|
9265
|
+
return LEHRPLAN_THUERINGEN_MANIFEST.subjects[schoolType] || [];
|
|
9266
|
+
},
|
|
9267
|
+
listTracks(schoolType, grade, subject) {
|
|
9268
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
9269
|
+
return LEHRPLAN_THUERINGEN_MANIFEST.tracks[key] || [];
|
|
9270
|
+
},
|
|
9271
|
+
listTopics(selection) {
|
|
9272
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
9273
|
+
const list = LEHRPLAN_THUERINGEN_MANIFEST.topics[key] || [];
|
|
9274
|
+
return list.map((t2) => ({
|
|
9275
|
+
...t2,
|
|
9276
|
+
sourceRef: key
|
|
9277
|
+
}));
|
|
9278
|
+
},
|
|
9279
|
+
resolveTopic(topic) {
|
|
9280
|
+
const uri = LEHRPLAN_THUERINGEN_MANIFEST.contentUrls[topic.sourceRef];
|
|
9281
|
+
if (!uri) {
|
|
9282
|
+
throw new Error(
|
|
9283
|
+
`Lehrplan Th\xFCringen: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
9284
|
+
);
|
|
9285
|
+
}
|
|
9286
|
+
return {
|
|
9287
|
+
provider: "lehrplan-thueringen",
|
|
9288
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
9289
|
+
uri
|
|
9290
|
+
};
|
|
9291
|
+
},
|
|
9292
|
+
extractTopics(html, topicIds) {
|
|
9293
|
+
const results = {};
|
|
9294
|
+
const headingMatches = Array.from(
|
|
9295
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
9296
|
+
);
|
|
9297
|
+
const sections = [];
|
|
9298
|
+
for (const m of headingMatches) {
|
|
9299
|
+
const headerText = cleanHtmlText11(m[1]).trim();
|
|
9300
|
+
const start = m.index + m[0].length;
|
|
9301
|
+
const nextHeading = html.indexOf("<h", start);
|
|
9302
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 1500);
|
|
9303
|
+
const content = cleanHtmlText11(rawContent).slice(0, 1500);
|
|
9304
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
9305
|
+
}
|
|
9306
|
+
for (const topicId of topicIds) {
|
|
9307
|
+
const hashIdx = topicId.indexOf("#");
|
|
9308
|
+
if (hashIdx === -1) continue;
|
|
9309
|
+
const key = topicId.substring(0, hashIdx);
|
|
9310
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
9311
|
+
const list = LEHRPLAN_THUERINGEN_MANIFEST.topics[key] ?? [];
|
|
9312
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
9313
|
+
if (!match) continue;
|
|
9314
|
+
const label = match.label;
|
|
9315
|
+
const normalizedLabel = normalizeForComparison11(label);
|
|
9316
|
+
let section = sections.find(
|
|
9317
|
+
(s) => normalizeForComparison11(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
9318
|
+
);
|
|
9319
|
+
if (!section && sections.length > 0) {
|
|
9320
|
+
section = sections.find((s) => s.content.length > 80) || sections[0];
|
|
9321
|
+
}
|
|
9322
|
+
if (section) {
|
|
9323
|
+
results[topicId] = `${label}
|
|
9324
|
+
|
|
9325
|
+
${section.content}`.trim();
|
|
9326
|
+
} else {
|
|
9327
|
+
results[topicId] = label;
|
|
9328
|
+
}
|
|
9329
|
+
}
|
|
9330
|
+
return results;
|
|
9331
|
+
}
|
|
9332
|
+
};
|
|
9333
|
+
function cleanHtmlText11(html) {
|
|
9334
|
+
let text = html.replace(
|
|
9335
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
9336
|
+
" "
|
|
9337
|
+
);
|
|
9338
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
9339
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
9340
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
9341
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
9342
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
9343
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
9344
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
9345
|
+
}
|
|
9346
|
+
function normalizeForComparison11(str) {
|
|
9347
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
9348
|
+
}
|
|
9349
|
+
|
|
9350
|
+
// src/cli/curriculum/providers/lehrplanplus-bayern/manifest.ts
|
|
9351
|
+
var LEHRPLANPLUS_BAYERN_MANIFEST = {
|
|
9352
|
+
schoolYear: "2026/2027",
|
|
9353
|
+
capturedOn: "2026-07-02",
|
|
9354
|
+
sourceRevision: "LehrplanPLUS Realschule \u2013 Oktober 2023",
|
|
9355
|
+
schoolTypes: [
|
|
9356
|
+
{ id: "grundschule", label: "Grundschule" },
|
|
9357
|
+
{ id: "mittelschule", label: "Mittelschule" },
|
|
9358
|
+
{ id: "foerderschule", label: "F\xF6rderschule" },
|
|
9359
|
+
{ id: "realschule", label: "Realschule" },
|
|
9360
|
+
{ id: "gymnasium", label: "Gymnasium" },
|
|
9361
|
+
{ id: "wirtschaftsschule", label: "Wirtschaftsschule" },
|
|
9362
|
+
{ id: "fos", label: "Fachoberschule" },
|
|
9363
|
+
{ id: "bos", label: "Berufsoberschule" }
|
|
9364
|
+
],
|
|
9365
|
+
grades: {
|
|
9366
|
+
realschule: ["5", "6", "7", "8", "9", "10"]
|
|
9367
|
+
},
|
|
9368
|
+
subjects: {
|
|
9369
|
+
realschule: [
|
|
9370
|
+
{
|
|
9371
|
+
id: "bwl-rechnungswesen",
|
|
9372
|
+
label: "Betriebswirtschaftslehre / Rechnungswesen"
|
|
9373
|
+
},
|
|
9374
|
+
{ id: "biologie", label: "Biologie" },
|
|
9375
|
+
{ id: "chemie", label: "Chemie" },
|
|
9376
|
+
{ id: "deutsch", label: "Deutsch" },
|
|
9377
|
+
{ id: "englisch", label: "Englisch" },
|
|
9378
|
+
{ id: "ernaehrung_und_gesundheit", label: "Ern\xE4hrung und Gesundheit" },
|
|
9379
|
+
{ id: "ethik", label: "Ethik" },
|
|
9380
|
+
{
|
|
9381
|
+
id: "evangelische-religionslehre",
|
|
9382
|
+
label: "Evangelische Religionslehre"
|
|
9383
|
+
},
|
|
9384
|
+
{ id: "franzoesisch", label: "Franz\xF6sisch" },
|
|
9385
|
+
{ id: "geographie", label: "Geographie" },
|
|
9386
|
+
{ id: "geschichte", label: "Geschichte" },
|
|
9387
|
+
{ id: "it", label: "Informationstechnologie" },
|
|
9388
|
+
{ id: "iu", label: "Islamischer Unterricht" },
|
|
9389
|
+
{ id: "ir", label: "Israelitische Religionslehre" },
|
|
9390
|
+
{
|
|
9391
|
+
id: "katholische-religionslehre",
|
|
9392
|
+
label: "Katholische Religionslehre"
|
|
9393
|
+
},
|
|
9394
|
+
{ id: "kunst", label: "Kunst" },
|
|
9395
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
9396
|
+
{ id: "musik", label: "Musik" },
|
|
9397
|
+
{ id: "or", label: "Orthodoxe Religionslehre" },
|
|
9398
|
+
{ id: "physik", label: "Physik" },
|
|
9399
|
+
{ id: "pug", label: "Politik und Gesellschaft" },
|
|
9400
|
+
{ id: "soziallehre", label: "Soziallehre" },
|
|
9401
|
+
{ id: "sozialwesen", label: "Sozialwesen" },
|
|
9402
|
+
{ id: "spanisch", label: "Spanisch" },
|
|
9403
|
+
{ id: "sport", label: "Sport" },
|
|
9404
|
+
{ id: "textiles-gestalten", label: "Textiles Gestalten" },
|
|
9405
|
+
{ id: "werken", label: "Werken" },
|
|
9406
|
+
{ id: "wirtschaft-und-recht", label: "Wirtschaft und Recht" }
|
|
9407
|
+
]
|
|
9408
|
+
},
|
|
9409
|
+
tracks: {
|
|
9410
|
+
"realschule|9|mathematik": [
|
|
9411
|
+
{ id: "wpfg1", label: "Mathematik 9 (I)" },
|
|
9412
|
+
{ id: "wpfg2-3", label: "Mathematik 9 (II/III)" }
|
|
9413
|
+
]
|
|
9414
|
+
},
|
|
9415
|
+
topics: {
|
|
9416
|
+
"realschule|9|mathematik|wpfg1": [
|
|
9417
|
+
{ id: "lb1", label: "Reelle Zahlen", hours: 10 },
|
|
9418
|
+
{ id: "lb2", label: "Zentrische Streckung", hours: 17 },
|
|
9419
|
+
{ id: "lb3", label: "Rechtwinklige Dreiecke", hours: 20 },
|
|
9420
|
+
{ id: "lb4", label: "Kreis", hours: 10 },
|
|
9421
|
+
{ id: "lb5", label: "Raumgeometrie", hours: 20 },
|
|
9422
|
+
{ id: "lb6", label: "Systeme linearer Gleichungen", hours: 12 },
|
|
9423
|
+
{
|
|
9424
|
+
id: "lb7",
|
|
9425
|
+
label: "Quadratische Funktionen und quadratische Gleichungen",
|
|
9426
|
+
hours: 42
|
|
9427
|
+
},
|
|
9428
|
+
{ id: "lb8", label: "Daten und Zufall", hours: 9 }
|
|
9429
|
+
],
|
|
9430
|
+
"realschule|9|mathematik|wpfg2-3": [
|
|
9431
|
+
{ id: "lb1", label: "Reelle Zahlen", hours: 7 },
|
|
9432
|
+
{ id: "lb2", label: "Zentrische Streckung", hours: 13 },
|
|
9433
|
+
{ id: "lb3", label: "Rechtwinklige Dreiecke", hours: 20 },
|
|
9434
|
+
{ id: "lb4", label: "Kreis", hours: 10 },
|
|
9435
|
+
{ id: "lb5", label: "Lineare Funktionen", hours: 15 },
|
|
9436
|
+
{ id: "lb6", label: "Systeme linearer Gleichungen", hours: 10 },
|
|
9437
|
+
{ id: "lb7", label: "Daten und Zufall", hours: 9 }
|
|
9438
|
+
],
|
|
9439
|
+
"realschule|9|deutsch": [
|
|
9440
|
+
{ id: "lb1", label: "Sprechen und Zuh\xF6ren" },
|
|
9441
|
+
{ id: "lb2", label: "Lesen \u2013 mit Texten und weiteren Medien umgehen" },
|
|
9442
|
+
{ id: "lb3", label: "Schreiben" },
|
|
9443
|
+
{
|
|
9444
|
+
id: "lb4",
|
|
9445
|
+
label: "Sprachgebrauch und Sprache untersuchen und reflektieren"
|
|
9446
|
+
}
|
|
9447
|
+
],
|
|
9448
|
+
"realschule|9|englisch": [
|
|
9449
|
+
{ id: "lb1", label: "Kommunikative Kompetenzen" },
|
|
9450
|
+
{ id: "lb2", label: "Interkulturelle Kompetenzen" },
|
|
9451
|
+
{ id: "lb3", label: "Text- und Medienkompetenzen" },
|
|
9452
|
+
{ id: "lb4", label: "Methodische Kompetenzen" },
|
|
9453
|
+
{ id: "lb5", label: "Themengebiete" }
|
|
9454
|
+
]
|
|
9455
|
+
},
|
|
9456
|
+
contentUrls: {
|
|
9457
|
+
"realschule|9|mathematik|wpfg1": "https://www.lehrplanplus.bayern.de/schulart/realschule/jgs/9/fach/mathematik/inhalt/fachlehrplaene?w_schulart=realschule&wt_1=schulart&w_fach=mathematik&wt_2=fach&w_jgs=9&wt_3=jgs&w_auspraegung=wpfg1",
|
|
9458
|
+
"realschule|9|mathematik|wpfg2-3": "https://www.lehrplanplus.bayern.de/schulart/realschule/jgs/9/fach/mathematik/inhalt/fachlehrplaene?w_schulart=realschule&wt_1=schulart&w_fach=mathematik&wt_2=fach&w_jgs=9&wt_3=jgs&w_auspraegung=wpfg2-3",
|
|
9459
|
+
"realschule|9|deutsch": "https://www.lehrplanplus.bayern.de/schulart/realschule/jgs/9/fach/deutsch/inhalt/fachlehrplaene",
|
|
9460
|
+
"realschule|9|englisch": "https://www.lehrplanplus.bayern.de/schulart/realschule/jgs/9/fach/englisch/inhalt/fachlehrplaene"
|
|
9461
|
+
}
|
|
9462
|
+
};
|
|
9463
|
+
|
|
9464
|
+
// src/cli/curriculum/providers/lehrplanplus-bayern/index.ts
|
|
9465
|
+
function levelKey(schoolType, grade, subject, track) {
|
|
9466
|
+
return track ? `${schoolType}|${grade}|${subject}|${track}` : `${schoolType}|${grade}|${subject}`;
|
|
9467
|
+
}
|
|
9468
|
+
var lehrplanplusBayernProvider = {
|
|
9469
|
+
id: "lehrplanplus-bayern",
|
|
9470
|
+
country: "DE",
|
|
9471
|
+
countryLabel: "Deutschland",
|
|
9472
|
+
region: "BY",
|
|
9473
|
+
regionLabel: "Bayern",
|
|
9474
|
+
label: "LehrplanPLUS (Bayern)",
|
|
9475
|
+
listSchoolTypes() {
|
|
9476
|
+
return LEHRPLANPLUS_BAYERN_MANIFEST.schoolTypes;
|
|
9477
|
+
},
|
|
9478
|
+
listGrades(schoolType) {
|
|
9479
|
+
return (LEHRPLANPLUS_BAYERN_MANIFEST.grades[schoolType] ?? []).map((grade) => ({
|
|
9480
|
+
id: grade,
|
|
9481
|
+
label: grade
|
|
9482
|
+
}));
|
|
9483
|
+
},
|
|
9484
|
+
listSubjects(schoolType, _grade) {
|
|
9485
|
+
return LEHRPLANPLUS_BAYERN_MANIFEST.subjects[schoolType] ?? [];
|
|
9486
|
+
},
|
|
9487
|
+
listTracks(schoolType, grade, subject) {
|
|
9488
|
+
return LEHRPLANPLUS_BAYERN_MANIFEST.tracks[levelKey(schoolType, grade, subject)] ?? [];
|
|
9489
|
+
},
|
|
9490
|
+
listTopics(selection) {
|
|
9491
|
+
const { schoolType, grade, subject, track } = selection;
|
|
9492
|
+
if (!schoolType || !grade || !subject) return [];
|
|
9493
|
+
const key = levelKey(schoolType, grade, subject, track);
|
|
9494
|
+
return (LEHRPLANPLUS_BAYERN_MANIFEST.topics[key] ?? []).map((topic) => ({
|
|
9495
|
+
...topic,
|
|
9496
|
+
sourceRef: key
|
|
9497
|
+
}));
|
|
9498
|
+
},
|
|
9499
|
+
resolveTopic(topic) {
|
|
9500
|
+
const uri = LEHRPLANPLUS_BAYERN_MANIFEST.contentUrls[topic.sourceRef];
|
|
9501
|
+
if (!uri) {
|
|
9502
|
+
throw new Error(
|
|
9503
|
+
`LehrplanPLUS Bayern: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}). The manifest only covers the combinations curated as of ${LEHRPLANPLUS_BAYERN_MANIFEST.capturedOn}.`
|
|
9504
|
+
);
|
|
9505
|
+
}
|
|
9506
|
+
return {
|
|
9507
|
+
provider: "lehrplanplus-bayern",
|
|
9508
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
9509
|
+
uri
|
|
9510
|
+
};
|
|
9511
|
+
},
|
|
9512
|
+
extractTopics(html, topicIds) {
|
|
9513
|
+
const chunks = html.split('<div id="thema_');
|
|
9514
|
+
const sections = [];
|
|
9515
|
+
for (let i = 1; i < chunks.length; i++) {
|
|
9516
|
+
const chunk = chunks[i];
|
|
9517
|
+
const quoteIdx = chunk.indexOf('"');
|
|
9518
|
+
if (quoteIdx === -1) continue;
|
|
9519
|
+
const id = chunk.slice(0, quoteIdx);
|
|
9520
|
+
const classStart = chunk.indexOf('class="');
|
|
7776
9521
|
if (classStart === -1) continue;
|
|
7777
9522
|
const classEnd = chunk.indexOf('"', classStart + 7);
|
|
7778
9523
|
const classContent = chunk.slice(classStart + 7, classEnd);
|
|
@@ -7783,53 +9528,395 @@ var lehrplanplusBayernProvider = {
|
|
|
7783
9528
|
const headerMatch = contentHtml.match(
|
|
7784
9529
|
/<a[^>]*class="paragraph_toggle"[^>]*>([\s\S]*?)<\/a>/i
|
|
7785
9530
|
);
|
|
7786
|
-
const headerText = headerMatch ?
|
|
7787
|
-
sections.push({
|
|
7788
|
-
id,
|
|
7789
|
-
level,
|
|
7790
|
-
headerText,
|
|
7791
|
-
contentHtml
|
|
7792
|
-
});
|
|
9531
|
+
const headerText = headerMatch ? cleanHtmlText12(headerMatch[1]).trim() : "";
|
|
9532
|
+
sections.push({
|
|
9533
|
+
id,
|
|
9534
|
+
level,
|
|
9535
|
+
headerText,
|
|
9536
|
+
contentHtml
|
|
9537
|
+
});
|
|
9538
|
+
}
|
|
9539
|
+
const results = {};
|
|
9540
|
+
for (const topicId of topicIds) {
|
|
9541
|
+
let label = "";
|
|
9542
|
+
for (const key of Object.keys(LEHRPLANPLUS_BAYERN_MANIFEST.topics)) {
|
|
9543
|
+
const list = LEHRPLANPLUS_BAYERN_MANIFEST.topics[key];
|
|
9544
|
+
const match = list.find(
|
|
9545
|
+
(t2) => t2.id === topicId || `${key}#${t2.id}` === topicId
|
|
9546
|
+
);
|
|
9547
|
+
if (match) {
|
|
9548
|
+
label = match.label;
|
|
9549
|
+
break;
|
|
9550
|
+
}
|
|
9551
|
+
}
|
|
9552
|
+
if (!label) {
|
|
9553
|
+
continue;
|
|
9554
|
+
}
|
|
9555
|
+
const normalizedLabel = normalizeForComparison12(label);
|
|
9556
|
+
const lvl1Index = sections.findIndex((s) => {
|
|
9557
|
+
if (s.level !== 1) return false;
|
|
9558
|
+
const normalizedHeader = normalizeForComparison12(s.headerText);
|
|
9559
|
+
return normalizedHeader.includes(normalizedLabel);
|
|
9560
|
+
});
|
|
9561
|
+
if (lvl1Index === -1) {
|
|
9562
|
+
continue;
|
|
9563
|
+
}
|
|
9564
|
+
const collectedSections = [sections[lvl1Index]];
|
|
9565
|
+
for (let i = lvl1Index + 1; i < sections.length; i++) {
|
|
9566
|
+
if (sections[i].level === 1) {
|
|
9567
|
+
break;
|
|
9568
|
+
}
|
|
9569
|
+
collectedSections.push(sections[i]);
|
|
9570
|
+
}
|
|
9571
|
+
const fullHtml = collectedSections.map((s) => s.contentHtml).join("\n");
|
|
9572
|
+
results[topicId] = cleanHtmlText12(fullHtml);
|
|
9573
|
+
}
|
|
9574
|
+
return results;
|
|
9575
|
+
}
|
|
9576
|
+
};
|
|
9577
|
+
function cleanHtmlText12(html) {
|
|
9578
|
+
let text = html.replace(
|
|
9579
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
9580
|
+
" "
|
|
9581
|
+
);
|
|
9582
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
9583
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
9584
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
9585
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
9586
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
9587
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'").replace(/–/g, "\u2013").replace(/—/g, "\u2014").replace(/·/g, "\xB7");
|
|
9588
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
9589
|
+
}
|
|
9590
|
+
function normalizeForComparison12(str) {
|
|
9591
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
9592
|
+
}
|
|
9593
|
+
|
|
9594
|
+
// src/cli/curriculum/providers/rahmenlehrplan-berlin-brandenburg/manifest.ts
|
|
9595
|
+
var RAHMENLEHRPLAN_BERLIN_BRANDENBURG_MANIFEST = {
|
|
9596
|
+
schoolYear: "2025/2026",
|
|
9597
|
+
capturedOn: "2026-07-02",
|
|
9598
|
+
sourceRevision: "Gemeinsamer Rahmenlehrplan Berlin-Brandenburg",
|
|
9599
|
+
schoolTypes: [
|
|
9600
|
+
{ id: "realschule", label: "Realschule" },
|
|
9601
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
9602
|
+
],
|
|
9603
|
+
grades: {
|
|
9604
|
+
realschule: ["7", "8", "9", "10"],
|
|
9605
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
9606
|
+
},
|
|
9607
|
+
subjects: {
|
|
9608
|
+
realschule: [
|
|
9609
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
9610
|
+
{ id: "informatik", label: "Informatik" },
|
|
9611
|
+
{ id: "physik", label: "Physik" },
|
|
9612
|
+
{ id: "chemie", label: "Chemie" },
|
|
9613
|
+
{ id: "biologie", label: "Biologie" }
|
|
9614
|
+
],
|
|
9615
|
+
gymnasium: [
|
|
9616
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
9617
|
+
{ id: "informatik", label: "Informatik" },
|
|
9618
|
+
{ id: "physik", label: "Physik" },
|
|
9619
|
+
{ id: "chemie", label: "Chemie" },
|
|
9620
|
+
{ id: "biologie", label: "Biologie" }
|
|
9621
|
+
]
|
|
9622
|
+
},
|
|
9623
|
+
tracks: {},
|
|
9624
|
+
topics: {
|
|
9625
|
+
"realschule|10|mathematik": [
|
|
9626
|
+
{ id: "funktionen", label: "Funktionen" },
|
|
9627
|
+
{ id: "geometrie", label: "Geometrie" },
|
|
9628
|
+
{ id: "stochastik", label: "Stochastik" }
|
|
9629
|
+
],
|
|
9630
|
+
"gymnasium|10|mathematik": [
|
|
9631
|
+
{ id: "analysis", label: "Analysis" },
|
|
9632
|
+
{ id: "vektoren", label: "Vektoren" }
|
|
9633
|
+
],
|
|
9634
|
+
"realschule|9|informatik": [
|
|
9635
|
+
{ id: "algorithmen", label: "Algorithmen" },
|
|
9636
|
+
{ id: "daten", label: "Daten und Information" }
|
|
9637
|
+
],
|
|
9638
|
+
"realschule|9|physik": [
|
|
9639
|
+
{ id: "mechanik", label: "Mechanik" },
|
|
9640
|
+
{ id: "elektrizitaet", label: "Elektrizit\xE4t" }
|
|
9641
|
+
],
|
|
9642
|
+
"realschule|9|chemie": [
|
|
9643
|
+
{ id: "reaktionen", label: "Chemische Reaktionen" },
|
|
9644
|
+
{ id: "stoffe", label: "Stoffe" }
|
|
9645
|
+
],
|
|
9646
|
+
"realschule|9|biologie": [
|
|
9647
|
+
{ id: "zellen", label: "Zellen" },
|
|
9648
|
+
{ id: "oekologie", label: "\xD6kologie" }
|
|
9649
|
+
],
|
|
9650
|
+
"gymnasium|9|physik": [
|
|
9651
|
+
{ id: "optik", label: "Optik" },
|
|
9652
|
+
{ id: "felder", label: "Felder" }
|
|
9653
|
+
],
|
|
9654
|
+
"gymnasium|9|chemie": [
|
|
9655
|
+
{ id: "bindungen", label: "Bindungen" },
|
|
9656
|
+
{ id: "saeuren", label: "S\xE4uren und Basen" }
|
|
9657
|
+
],
|
|
9658
|
+
"gymnasium|9|biologie": [
|
|
9659
|
+
{ id: "genetik", label: "Genetik" },
|
|
9660
|
+
{ id: "evolution", label: "Evolution" }
|
|
9661
|
+
]
|
|
9662
|
+
},
|
|
9663
|
+
contentUrls: {
|
|
9664
|
+
"realschule|10|mathematik": "https://bildungsserver.berlin-brandenburg.de/rlp-online/c-faecher/mathematik",
|
|
9665
|
+
"gymnasium|10|mathematik": "https://bildungsserver.berlin-brandenburg.de/rlp-online/c-faecher/mathematik",
|
|
9666
|
+
"realschule|9|informatik": "https://bildungsserver.berlin-brandenburg.de/rlp-online/c-faecher/informatik",
|
|
9667
|
+
"realschule|9|physik": "https://bildungsserver.berlin-brandenburg.de/rlp-online/c-faecher/physik",
|
|
9668
|
+
"realschule|9|chemie": "https://bildungsserver.berlin-brandenburg.de/rlp-online/c-faecher/chemie",
|
|
9669
|
+
"realschule|9|biologie": "https://bildungsserver.berlin-brandenburg.de/rlp-online/c-faecher/biologie",
|
|
9670
|
+
"gymnasium|9|physik": "https://bildungsserver.berlin-brandenburg.de/rlp-online/c-faecher/physik",
|
|
9671
|
+
"gymnasium|9|chemie": "https://bildungsserver.berlin-brandenburg.de/rlp-online/c-faecher/chemie",
|
|
9672
|
+
"gymnasium|9|biologie": "https://bildungsserver.berlin-brandenburg.de/rlp-online/c-faecher/biologie"
|
|
9673
|
+
}
|
|
9674
|
+
};
|
|
9675
|
+
|
|
9676
|
+
// src/cli/curriculum/providers/rahmenlehrplan-berlin-brandenburg/index.ts
|
|
9677
|
+
var rahmenlehrplanBerlinBrandenburgProvider = {
|
|
9678
|
+
id: "rahmenlehrplan-berlin-brandenburg",
|
|
9679
|
+
country: "DE",
|
|
9680
|
+
countryLabel: "Deutschland",
|
|
9681
|
+
region: "BE-BB",
|
|
9682
|
+
regionLabel: "Berlin / Brandenburg",
|
|
9683
|
+
label: "Rahmenlehrplan (Berlin-Brandenburg)",
|
|
9684
|
+
listSchoolTypes() {
|
|
9685
|
+
return RAHMENLEHRPLAN_BERLIN_BRANDENBURG_MANIFEST.schoolTypes;
|
|
9686
|
+
},
|
|
9687
|
+
listGrades(schoolType) {
|
|
9688
|
+
return (RAHMENLEHRPLAN_BERLIN_BRANDENBURG_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
9689
|
+
id,
|
|
9690
|
+
label: `Klasse ${id}`
|
|
9691
|
+
}));
|
|
9692
|
+
},
|
|
9693
|
+
listSubjects(schoolType, _grade) {
|
|
9694
|
+
return RAHMENLEHRPLAN_BERLIN_BRANDENBURG_MANIFEST.subjects[schoolType] || [];
|
|
9695
|
+
},
|
|
9696
|
+
listTracks(schoolType, grade, subject) {
|
|
9697
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
9698
|
+
return RAHMENLEHRPLAN_BERLIN_BRANDENBURG_MANIFEST.tracks[key] || [];
|
|
9699
|
+
},
|
|
9700
|
+
listTopics(selection) {
|
|
9701
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
9702
|
+
const list = RAHMENLEHRPLAN_BERLIN_BRANDENBURG_MANIFEST.topics[key] || [];
|
|
9703
|
+
return list.map((t2) => ({
|
|
9704
|
+
...t2,
|
|
9705
|
+
sourceRef: key
|
|
9706
|
+
}));
|
|
9707
|
+
},
|
|
9708
|
+
resolveTopic(topic) {
|
|
9709
|
+
const uri = RAHMENLEHRPLAN_BERLIN_BRANDENBURG_MANIFEST.contentUrls[topic.sourceRef];
|
|
9710
|
+
if (!uri) {
|
|
9711
|
+
throw new Error(
|
|
9712
|
+
`Rahmenlehrplan Berlin-Brandenburg: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
9713
|
+
);
|
|
7793
9714
|
}
|
|
9715
|
+
return {
|
|
9716
|
+
provider: "rahmenlehrplan-berlin-brandenburg",
|
|
9717
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
9718
|
+
uri
|
|
9719
|
+
};
|
|
9720
|
+
},
|
|
9721
|
+
extractTopics(html, topicIds) {
|
|
7794
9722
|
const results = {};
|
|
9723
|
+
const headingMatches = Array.from(
|
|
9724
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
9725
|
+
);
|
|
9726
|
+
const sections = [];
|
|
9727
|
+
for (const m of headingMatches) {
|
|
9728
|
+
const headerText = cleanHtmlText13(m[1]).trim();
|
|
9729
|
+
const start = m.index + m[0].length;
|
|
9730
|
+
const nextHeading = html.indexOf("<h", start);
|
|
9731
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 1500);
|
|
9732
|
+
const content = cleanHtmlText13(rawContent).slice(0, 1500);
|
|
9733
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
9734
|
+
}
|
|
7795
9735
|
for (const topicId of topicIds) {
|
|
7796
|
-
|
|
7797
|
-
|
|
7798
|
-
|
|
7799
|
-
|
|
7800
|
-
|
|
7801
|
-
|
|
7802
|
-
|
|
7803
|
-
|
|
7804
|
-
|
|
7805
|
-
|
|
9736
|
+
const hashIdx = topicId.indexOf("#");
|
|
9737
|
+
if (hashIdx === -1) continue;
|
|
9738
|
+
const key = topicId.substring(0, hashIdx);
|
|
9739
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
9740
|
+
const list = RAHMENLEHRPLAN_BERLIN_BRANDENBURG_MANIFEST.topics[key] ?? [];
|
|
9741
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
9742
|
+
if (!match) continue;
|
|
9743
|
+
const label = match.label;
|
|
9744
|
+
const normalizedLabel = normalizeForComparison13(label);
|
|
9745
|
+
let section = sections.find(
|
|
9746
|
+
(s) => normalizeForComparison13(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
9747
|
+
);
|
|
9748
|
+
if (!section && sections.length > 0) {
|
|
9749
|
+
section = sections.find((s) => s.content.length > 80) || sections[0];
|
|
7806
9750
|
}
|
|
7807
|
-
if (
|
|
7808
|
-
|
|
9751
|
+
if (section) {
|
|
9752
|
+
results[topicId] = `${label}
|
|
9753
|
+
|
|
9754
|
+
${section.content}`.trim();
|
|
9755
|
+
} else {
|
|
9756
|
+
results[topicId] = label;
|
|
7809
9757
|
}
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7814
|
-
|
|
7815
|
-
|
|
7816
|
-
|
|
7817
|
-
|
|
9758
|
+
}
|
|
9759
|
+
return results;
|
|
9760
|
+
}
|
|
9761
|
+
};
|
|
9762
|
+
function cleanHtmlText13(html) {
|
|
9763
|
+
let text = html.replace(
|
|
9764
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
9765
|
+
" "
|
|
9766
|
+
);
|
|
9767
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
9768
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
9769
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
9770
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
9771
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
9772
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
9773
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
9774
|
+
}
|
|
9775
|
+
function normalizeForComparison13(str) {
|
|
9776
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
9777
|
+
}
|
|
9778
|
+
|
|
9779
|
+
// src/cli/curriculum/providers/rahmenplan-mv/manifest.ts
|
|
9780
|
+
var RAHMENPLAN_MV_MANIFEST = {
|
|
9781
|
+
schoolYear: "2025/2026",
|
|
9782
|
+
capturedOn: "2026-07-02",
|
|
9783
|
+
sourceRevision: "Rahmenpl\xE4ne Mecklenburg-Vorpommern",
|
|
9784
|
+
schoolTypes: [
|
|
9785
|
+
{ id: "regionale-schule", label: "Regionale Schule" },
|
|
9786
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
9787
|
+
],
|
|
9788
|
+
grades: {
|
|
9789
|
+
"regionale-schule": ["7", "8", "9", "10"],
|
|
9790
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
9791
|
+
},
|
|
9792
|
+
subjects: {
|
|
9793
|
+
"regionale-schule": [
|
|
9794
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
9795
|
+
{ id: "informatik", label: "Informatik" },
|
|
9796
|
+
{ id: "physik", label: "Physik" },
|
|
9797
|
+
{ id: "chemie", label: "Chemie" },
|
|
9798
|
+
{ id: "biologie", label: "Biologie" }
|
|
9799
|
+
],
|
|
9800
|
+
gymnasium: [
|
|
9801
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
9802
|
+
{ id: "informatik", label: "Informatik" },
|
|
9803
|
+
{ id: "physik", label: "Physik" },
|
|
9804
|
+
{ id: "chemie", label: "Chemie" },
|
|
9805
|
+
{ id: "biologie", label: "Biologie" }
|
|
9806
|
+
]
|
|
9807
|
+
},
|
|
9808
|
+
tracks: {},
|
|
9809
|
+
topics: {
|
|
9810
|
+
"regionale-schule|10|mathematik": [
|
|
9811
|
+
{ id: "funktionen", label: "Funktionen" }
|
|
9812
|
+
],
|
|
9813
|
+
"gymnasium|10|mathematik": [{ id: "analysis", label: "Analysis" }],
|
|
9814
|
+
"regionale-schule|9|physik": [{ id: "mechanik", label: "Mechanik" }],
|
|
9815
|
+
"regionale-schule|9|chemie": [{ id: "reaktionen", label: "Reaktionen" }],
|
|
9816
|
+
"regionale-schule|9|biologie": [{ id: "zellen", label: "Zellen" }],
|
|
9817
|
+
"gymnasium|9|physik": [{ id: "elektrizitaet", label: "Elektrizit\xE4t" }],
|
|
9818
|
+
"gymnasium|9|chemie": [{ id: "bindungen", label: "Bindungen" }],
|
|
9819
|
+
"gymnasium|9|biologie": [{ id: "genetik", label: "Genetik" }]
|
|
9820
|
+
},
|
|
9821
|
+
contentUrls: {
|
|
9822
|
+
"regionale-schule|10|mathematik": "https://www.bildung-mv.de/schueler/schule-und-unterricht/faecher-und-rahmenplaene/rahmenplaene-an-allgemeinbildenden-schulen/",
|
|
9823
|
+
"gymnasium|10|mathematik": "https://www.bildung-mv.de/schueler/schule-und-unterricht/faecher-und-rahmenplaene/rahmenplaene-an-allgemeinbildenden-schulen/",
|
|
9824
|
+
"regionale-schule|9|physik": "https://www.bildung-mv.de/schueler/schule-und-unterricht/faecher-und-rahmenplaene/rahmenplaene-an-allgemeinbildenden-schulen/",
|
|
9825
|
+
"regionale-schule|9|chemie": "https://www.bildung-mv.de/schueler/schule-und-unterricht/faecher-und-rahmenplaene/rahmenplaene-an-allgemeinbildenden-schulen/",
|
|
9826
|
+
"regionale-schule|9|biologie": "https://www.bildung-mv.de/schueler/schule-und-unterricht/faecher-und-rahmenplaene/rahmenplaene-an-allgemeinbildenden-schulen/",
|
|
9827
|
+
"gymnasium|9|physik": "https://www.bildung-mv.de/schueler/schule-und-unterricht/faecher-und-rahmenplaene/rahmenplaene-an-allgemeinbildenden-schulen/",
|
|
9828
|
+
"gymnasium|9|chemie": "https://www.bildung-mv.de/schueler/schule-und-unterricht/faecher-und-rahmenplaene/rahmenplaene-an-allgemeinbildenden-schulen/",
|
|
9829
|
+
"gymnasium|9|biologie": "https://www.bildung-mv.de/schueler/schule-und-unterricht/faecher-und-rahmenplaene/rahmenplaene-an-allgemeinbildenden-schulen/"
|
|
9830
|
+
}
|
|
9831
|
+
};
|
|
9832
|
+
|
|
9833
|
+
// src/cli/curriculum/providers/rahmenplan-mv/index.ts
|
|
9834
|
+
var rahmenplanMvProvider = {
|
|
9835
|
+
id: "rahmenplan-mv",
|
|
9836
|
+
country: "DE",
|
|
9837
|
+
countryLabel: "Deutschland",
|
|
9838
|
+
region: "MV",
|
|
9839
|
+
regionLabel: "Mecklenburg-Vorpommern",
|
|
9840
|
+
label: "Rahmenplan (Mecklenburg-Vorpommern)",
|
|
9841
|
+
listSchoolTypes() {
|
|
9842
|
+
return RAHMENPLAN_MV_MANIFEST.schoolTypes;
|
|
9843
|
+
},
|
|
9844
|
+
listGrades(schoolType) {
|
|
9845
|
+
return (RAHMENPLAN_MV_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
9846
|
+
id,
|
|
9847
|
+
label: `Klasse ${id}`
|
|
9848
|
+
}));
|
|
9849
|
+
},
|
|
9850
|
+
listSubjects(schoolType, _grade) {
|
|
9851
|
+
return RAHMENPLAN_MV_MANIFEST.subjects[schoolType] || [];
|
|
9852
|
+
},
|
|
9853
|
+
listTracks(schoolType, grade, subject) {
|
|
9854
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
9855
|
+
return RAHMENPLAN_MV_MANIFEST.tracks[key] || [];
|
|
9856
|
+
},
|
|
9857
|
+
listTopics(selection) {
|
|
9858
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
9859
|
+
const list = RAHMENPLAN_MV_MANIFEST.topics[key] || [];
|
|
9860
|
+
return list.map((t2) => ({
|
|
9861
|
+
...t2,
|
|
9862
|
+
sourceRef: key
|
|
9863
|
+
}));
|
|
9864
|
+
},
|
|
9865
|
+
resolveTopic(topic) {
|
|
9866
|
+
const uri = RAHMENPLAN_MV_MANIFEST.contentUrls[topic.sourceRef];
|
|
9867
|
+
if (!uri) {
|
|
9868
|
+
throw new Error(
|
|
9869
|
+
`Rahmenplan MV: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
9870
|
+
);
|
|
9871
|
+
}
|
|
9872
|
+
return {
|
|
9873
|
+
provider: "rahmenplan-mv",
|
|
9874
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
9875
|
+
uri
|
|
9876
|
+
};
|
|
9877
|
+
},
|
|
9878
|
+
extractTopics(html, topicIds) {
|
|
9879
|
+
const results = {};
|
|
9880
|
+
const headingMatches = Array.from(
|
|
9881
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
9882
|
+
);
|
|
9883
|
+
const sections = [];
|
|
9884
|
+
for (const m of headingMatches) {
|
|
9885
|
+
const headerText = cleanHtmlText14(m[1]).trim();
|
|
9886
|
+
const start = m.index + m[0].length;
|
|
9887
|
+
const nextHeading = html.indexOf("<h", start);
|
|
9888
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 1500);
|
|
9889
|
+
const content = cleanHtmlText14(rawContent).slice(0, 1500);
|
|
9890
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
9891
|
+
}
|
|
9892
|
+
for (const topicId of topicIds) {
|
|
9893
|
+
const hashIdx = topicId.indexOf("#");
|
|
9894
|
+
if (hashIdx === -1) continue;
|
|
9895
|
+
const key = topicId.substring(0, hashIdx);
|
|
9896
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
9897
|
+
const list = RAHMENPLAN_MV_MANIFEST.topics[key] ?? [];
|
|
9898
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
9899
|
+
if (!match) continue;
|
|
9900
|
+
const label = match.label;
|
|
9901
|
+
const normalizedLabel = normalizeForComparison14(label);
|
|
9902
|
+
let section = sections.find(
|
|
9903
|
+
(s) => normalizeForComparison14(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
9904
|
+
);
|
|
9905
|
+
if (!section && sections.length > 0) {
|
|
9906
|
+
section = sections.find((s) => s.content.length > 80) || sections[0];
|
|
7818
9907
|
}
|
|
7819
|
-
|
|
7820
|
-
|
|
7821
|
-
|
|
7822
|
-
|
|
7823
|
-
|
|
7824
|
-
|
|
9908
|
+
if (section) {
|
|
9909
|
+
results[topicId] = `${label}
|
|
9910
|
+
|
|
9911
|
+
${section.content}`.trim();
|
|
9912
|
+
} else {
|
|
9913
|
+
results[topicId] = label;
|
|
7825
9914
|
}
|
|
7826
|
-
const fullHtml = collectedSections.map((s) => s.contentHtml).join("\n");
|
|
7827
|
-
results[topicId] = cleanHtmlText2(fullHtml);
|
|
7828
9915
|
}
|
|
7829
9916
|
return results;
|
|
7830
9917
|
}
|
|
7831
9918
|
};
|
|
7832
|
-
function
|
|
9919
|
+
function cleanHtmlText14(html) {
|
|
7833
9920
|
let text = html.replace(
|
|
7834
9921
|
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
7835
9922
|
" "
|
|
@@ -7839,17 +9926,185 @@ function cleanHtmlText2(html) {
|
|
|
7839
9926
|
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
7840
9927
|
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
7841
9928
|
text = text.replace(/<[^>]+>/g, " ");
|
|
7842
|
-
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'")
|
|
9929
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
7843
9930
|
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
7844
9931
|
}
|
|
7845
|
-
function
|
|
9932
|
+
function normalizeForComparison14(str) {
|
|
9933
|
+
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
9934
|
+
}
|
|
9935
|
+
|
|
9936
|
+
// src/cli/curriculum/providers/rahmenrichtlinien-st/manifest.ts
|
|
9937
|
+
var RAHMENRICHTLINIEN_ST_MANIFEST = {
|
|
9938
|
+
schoolYear: "2025/2026",
|
|
9939
|
+
capturedOn: "2026-07-02",
|
|
9940
|
+
sourceRevision: "Rahmenrichtlinien Sachsen-Anhalt",
|
|
9941
|
+
schoolTypes: [
|
|
9942
|
+
{ id: "sekundarschule", label: "Sekundarschule" },
|
|
9943
|
+
{ id: "gymnasium", label: "Gymnasium" }
|
|
9944
|
+
],
|
|
9945
|
+
grades: {
|
|
9946
|
+
sekundarschule: ["7", "8", "9", "10"],
|
|
9947
|
+
gymnasium: ["7", "8", "9", "10"]
|
|
9948
|
+
},
|
|
9949
|
+
subjects: {
|
|
9950
|
+
sekundarschule: [
|
|
9951
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
9952
|
+
{ id: "informatik", label: "Informatik" },
|
|
9953
|
+
{ id: "physik", label: "Physik" },
|
|
9954
|
+
{ id: "chemie", label: "Chemie" },
|
|
9955
|
+
{ id: "biologie", label: "Biologie" }
|
|
9956
|
+
],
|
|
9957
|
+
gymnasium: [
|
|
9958
|
+
{ id: "mathematik", label: "Mathematik" },
|
|
9959
|
+
{ id: "informatik", label: "Informatik" },
|
|
9960
|
+
{ id: "physik", label: "Physik" },
|
|
9961
|
+
{ id: "chemie", label: "Chemie" },
|
|
9962
|
+
{ id: "biologie", label: "Biologie" }
|
|
9963
|
+
]
|
|
9964
|
+
},
|
|
9965
|
+
tracks: {},
|
|
9966
|
+
topics: {
|
|
9967
|
+
"sekundarschule|10|mathematik": [{ id: "funktionen", label: "Funktionen" }],
|
|
9968
|
+
"gymnasium|10|mathematik": [{ id: "analysis", label: "Analysis" }],
|
|
9969
|
+
"sekundarschule|9|physik": [{ id: "mechanik", label: "Mechanik" }],
|
|
9970
|
+
"sekundarschule|9|chemie": [{ id: "reaktionen", label: "Reaktionen" }],
|
|
9971
|
+
"sekundarschule|9|biologie": [{ id: "zellen", label: "Zellen" }],
|
|
9972
|
+
"gymnasium|9|physik": [{ id: "elektrizitaet", label: "Elektrizit\xE4t" }],
|
|
9973
|
+
"gymnasium|9|chemie": [{ id: "bindungen", label: "Bindungen" }],
|
|
9974
|
+
"gymnasium|9|biologie": [{ id: "genetik", label: "Genetik" }]
|
|
9975
|
+
},
|
|
9976
|
+
contentUrls: {
|
|
9977
|
+
"sekundarschule|10|mathematik": "https://lisa.sachsen-anhalt.de/schulqualitaet/lehrplaene-rahmenrichtlinien",
|
|
9978
|
+
"gymnasium|10|mathematik": "https://lisa.sachsen-anhalt.de/schulqualitaet/lehrplaene-rahmenrichtlinien",
|
|
9979
|
+
"sekundarschule|9|physik": "https://lisa.sachsen-anhalt.de/schulqualitaet/lehrplaene-rahmenrichtlinien",
|
|
9980
|
+
"sekundarschule|9|chemie": "https://lisa.sachsen-anhalt.de/schulqualitaet/lehrplaene-rahmenrichtlinien",
|
|
9981
|
+
"sekundarschule|9|biologie": "https://lisa.sachsen-anhalt.de/schulqualitaet/lehrplaene-rahmenrichtlinien",
|
|
9982
|
+
"gymnasium|9|physik": "https://lisa.sachsen-anhalt.de/schulqualitaet/lehrplaene-rahmenrichtlinien",
|
|
9983
|
+
"gymnasium|9|chemie": "https://lisa.sachsen-anhalt.de/schulqualitaet/lehrplaene-rahmenrichtlinien",
|
|
9984
|
+
"gymnasium|9|biologie": "https://lisa.sachsen-anhalt.de/schulqualitaet/lehrplaene-rahmenrichtlinien"
|
|
9985
|
+
}
|
|
9986
|
+
};
|
|
9987
|
+
|
|
9988
|
+
// src/cli/curriculum/providers/rahmenrichtlinien-st/index.ts
|
|
9989
|
+
var rahmenrichtlinienStProvider = {
|
|
9990
|
+
id: "rahmenrichtlinien-st",
|
|
9991
|
+
country: "DE",
|
|
9992
|
+
countryLabel: "Deutschland",
|
|
9993
|
+
region: "ST",
|
|
9994
|
+
regionLabel: "Sachsen-Anhalt",
|
|
9995
|
+
label: "Rahmenrichtlinien (Sachsen-Anhalt)",
|
|
9996
|
+
listSchoolTypes() {
|
|
9997
|
+
return RAHMENRICHTLINIEN_ST_MANIFEST.schoolTypes;
|
|
9998
|
+
},
|
|
9999
|
+
listGrades(schoolType) {
|
|
10000
|
+
return (RAHMENRICHTLINIEN_ST_MANIFEST.grades[schoolType] || []).map((id) => ({
|
|
10001
|
+
id,
|
|
10002
|
+
label: `Klasse ${id}`
|
|
10003
|
+
}));
|
|
10004
|
+
},
|
|
10005
|
+
listSubjects(schoolType, _grade) {
|
|
10006
|
+
return RAHMENRICHTLINIEN_ST_MANIFEST.subjects[schoolType] || [];
|
|
10007
|
+
},
|
|
10008
|
+
listTracks(schoolType, grade, subject) {
|
|
10009
|
+
const key = `${schoolType}|${grade}|${subject}`;
|
|
10010
|
+
return RAHMENRICHTLINIEN_ST_MANIFEST.tracks[key] || [];
|
|
10011
|
+
},
|
|
10012
|
+
listTopics(selection) {
|
|
10013
|
+
const key = selection.track ? `${selection.schoolType}|${selection.grade}|${selection.subject}|${selection.track}` : `${selection.schoolType}|${selection.grade}|${selection.subject}`;
|
|
10014
|
+
const list = RAHMENRICHTLINIEN_ST_MANIFEST.topics[key] || [];
|
|
10015
|
+
return list.map((t2) => ({
|
|
10016
|
+
...t2,
|
|
10017
|
+
sourceRef: key
|
|
10018
|
+
}));
|
|
10019
|
+
},
|
|
10020
|
+
resolveTopic(topic) {
|
|
10021
|
+
const uri = RAHMENRICHTLINIEN_ST_MANIFEST.contentUrls[topic.sourceRef];
|
|
10022
|
+
if (!uri) {
|
|
10023
|
+
throw new Error(
|
|
10024
|
+
`Rahmenrichtlinien ST: no resolvable source URL for topic "${topic.id}" (${topic.sourceRef}).`
|
|
10025
|
+
);
|
|
10026
|
+
}
|
|
10027
|
+
return {
|
|
10028
|
+
provider: "rahmenrichtlinien-st",
|
|
10029
|
+
topicId: `${topic.sourceRef}#${topic.id}`,
|
|
10030
|
+
uri
|
|
10031
|
+
};
|
|
10032
|
+
},
|
|
10033
|
+
extractTopics(html, topicIds) {
|
|
10034
|
+
const results = {};
|
|
10035
|
+
const headingMatches = Array.from(
|
|
10036
|
+
html.matchAll(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi)
|
|
10037
|
+
);
|
|
10038
|
+
const sections = [];
|
|
10039
|
+
for (const m of headingMatches) {
|
|
10040
|
+
const headerText = cleanHtmlText15(m[1]).trim();
|
|
10041
|
+
const start = m.index + m[0].length;
|
|
10042
|
+
const nextHeading = html.indexOf("<h", start);
|
|
10043
|
+
const rawContent = nextHeading > -1 ? html.slice(start, nextHeading) : html.slice(start, start + 1500);
|
|
10044
|
+
const content = cleanHtmlText15(rawContent).slice(0, 1500);
|
|
10045
|
+
if (headerText) sections.push({ header: headerText, content });
|
|
10046
|
+
}
|
|
10047
|
+
for (const topicId of topicIds) {
|
|
10048
|
+
const hashIdx = topicId.indexOf("#");
|
|
10049
|
+
if (hashIdx === -1) continue;
|
|
10050
|
+
const key = topicId.substring(0, hashIdx);
|
|
10051
|
+
const shortId = topicId.substring(hashIdx + 1);
|
|
10052
|
+
const list = RAHMENRICHTLINIEN_ST_MANIFEST.topics[key] ?? [];
|
|
10053
|
+
const match = list.find((t2) => t2.id === shortId);
|
|
10054
|
+
if (!match) continue;
|
|
10055
|
+
const label = match.label;
|
|
10056
|
+
const normalizedLabel = normalizeForComparison15(label);
|
|
10057
|
+
let section = sections.find(
|
|
10058
|
+
(s) => normalizeForComparison15(s.header).includes(normalizedLabel) || s.header.toLowerCase().includes(shortId.toLowerCase())
|
|
10059
|
+
);
|
|
10060
|
+
if (!section && sections.length > 0) {
|
|
10061
|
+
section = sections.find((s) => s.content.length > 80) || sections[0];
|
|
10062
|
+
}
|
|
10063
|
+
if (section) {
|
|
10064
|
+
results[topicId] = `${label}
|
|
10065
|
+
|
|
10066
|
+
${section.content}`.trim();
|
|
10067
|
+
} else {
|
|
10068
|
+
results[topicId] = label;
|
|
10069
|
+
}
|
|
10070
|
+
}
|
|
10071
|
+
return results;
|
|
10072
|
+
}
|
|
10073
|
+
};
|
|
10074
|
+
function cleanHtmlText15(html) {
|
|
10075
|
+
let text = html.replace(
|
|
10076
|
+
/<(head|script|style|svg)[^>]*>[\s\S]*?<\/\1>/gi,
|
|
10077
|
+
" "
|
|
10078
|
+
);
|
|
10079
|
+
text = text.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, "\n\n$1\n\n");
|
|
10080
|
+
text = text.replace(/<li[^>]*>/gi, "\n- ");
|
|
10081
|
+
text = text.replace(/<p[^>]*>/gi, "\n");
|
|
10082
|
+
text = text.replace(/<br\s*\/?>/gi, "\n");
|
|
10083
|
+
text = text.replace(/<[^>]+>/g, " ");
|
|
10084
|
+
text = text.replace(/ /g, " ").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&").replace(/"/g, '"').replace(/'/g, "'");
|
|
10085
|
+
return text.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
10086
|
+
}
|
|
10087
|
+
function normalizeForComparison15(str) {
|
|
7846
10088
|
return str.toLowerCase().replace(/&[a-z0-9#]+;/gi, "").replace(/[^a-z0-9]/gi, "");
|
|
7847
10089
|
}
|
|
7848
10090
|
|
|
7849
10091
|
// src/cli/curriculum/registry.ts
|
|
7850
10092
|
var CURRICULUM_PROVIDERS = [
|
|
7851
10093
|
lehrplanplusBayernProvider,
|
|
7852
|
-
bildungsplanBwProvider
|
|
10094
|
+
bildungsplanBwProvider,
|
|
10095
|
+
kernlehrplanNrwProvider,
|
|
10096
|
+
kerncurriculumHessenProvider,
|
|
10097
|
+
kerncurriculumNiedersachsenProvider,
|
|
10098
|
+
lehrplanSachsenProvider,
|
|
10099
|
+
rahmenlehrplanBerlinBrandenburgProvider,
|
|
10100
|
+
bildungsplanHamburgProvider,
|
|
10101
|
+
bildungsplanBremenProvider,
|
|
10102
|
+
rahmenplanMvProvider,
|
|
10103
|
+
lehrplaeneRpProvider,
|
|
10104
|
+
lehrplanSaarlandProvider,
|
|
10105
|
+
rahmenrichtlinienStProvider,
|
|
10106
|
+
fachanforderungenShProvider,
|
|
10107
|
+
lehrplanThueringenProvider
|
|
7853
10108
|
];
|
|
7854
10109
|
function getCurriculumProvider(id) {
|
|
7855
10110
|
return CURRICULUM_PROVIDERS.find((provider) => provider.id === id);
|
|
@@ -11302,7 +13557,7 @@ async function fetchRawHtml(url) {
|
|
|
11302
13557
|
const res = await fetch(url, {
|
|
11303
13558
|
signal: controller.signal,
|
|
11304
13559
|
headers: {
|
|
11305
|
-
"User-Agent": "ZAM-Content-Studio/0.6.
|
|
13560
|
+
"User-Agent": "ZAM-Content-Studio/0.6.3"
|
|
11306
13561
|
}
|
|
11307
13562
|
});
|
|
11308
13563
|
if (!res.ok) {
|