Dokumentation nachgezogen - und dabei den neunten Fund gemacht

Der Leser-Wegweiser Operations_Research_mit_Python_Version_04/README.md, die
Datei, die Leser als erstes oeffnen, beschrieb noch die Struktur der
Version 03:

* 12 der 15 Kapitelnummern falsch. "20_Lineare_Programmierung.md | 4" - es ist
  Kapitel 5. Bei den Finanzkapiteln lag die Abweichung bei sieben: README 11,
  tatsaechlich 18.
* 8 der 23 Kapitel fehlten ganz (Metaheuristiken, Spaltengenerierung,
  Mehrziel, Predict-then-Optimize, Testing und drei weitere).
* 93_Anhang_Spickzettel.md fehlte in der Anhangtabelle; der Bauabschnitt nannte
  dreimal build_version_03.py statt _04 und beschrieb --html als
  "Single-Page-HTML", obwohl es laengst eine mehrseitige Website erzeugt.

Dieselbe Regel wie acht Mal zuvor, dieselbe Behandlung: Die Tabelle wird
erzeugt, nicht korrigiert. schreibe_kapiteltabelle() ersetzt den Block
zwischen den KAPITELTABELLE-Marken aus der Struktur, die
baue_seiten_registry() ohnehin ermittelt - derselben Quelle, aus der die
Nummern im Buch stammen. --check meldet einen veralteten Block und bricht ab.
Gegenprobe: alle 37 Dateien aus DATEIEN stehen in der Uebersicht.

Weiter nachgezogen:

* CLAUDE.md kannte veroeffentliche_04.py nicht und nannte .env.beispiel nicht.
  Der Abschnitt "Veroeffentlichung" beschrieb noch das Kopieren von Hand samt
  curl-Kontrolle - das erledigt jetzt das Skript.
* Fundzahl der Nummern-Regel: CLAUDE.md sagte sieben, README acht, PROGRESS
  dokumentiert einen "Achter Fund". Jetzt ueberall neun.
* Diagrammzahl: CLAUDE.md zaehlt 32 Kapiteldiagramme, README 33 - der
  Unterschied ist titelseite.svg. Das steht jetzt dabei.

Nebenbei am Upload-Skript: rsync bekommt --checksum. Der Bau schreibt alle 42
Seiten bei jedem Lauf neu, auch ohne inhaltliche Aenderung; nach Zeitstempel
und Groesse waeren das jedes Mal rund 19 MB sinnlose Uebertragung. Gemessen
nach einem Neubau ohne Aenderung: 0 statt 151 Dateien.

Website unveraendert - kein Upload noetig, der Probelauf meldet 0 zu
uebertragen und 0 zu loeschen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
dschlueter 2026-09-08 19:13:34 +02:00
commit 3d85488d7c
6 changed files with 212 additions and 42 deletions

View file

@ -39,6 +39,8 @@ pyproject.toml Quelle: Abhängigkeiten in Gruppe
Dockerfile, .dockerignore Quelle: Kurs-Image (Programme + JupyterLab)
LICENSE, LICENSE-TEXT.md MIT fuer Code, CC BY-SA 4.0 fuer den Text
.gitattributes LF im Repository, egal auf welchem System
.env.beispiel Vorlage; die echte .env (Ziel des Uploads)
ist bewusst NICHT im Repository
pandoc-defaults-basis.yaml, pandoc/, pandoc-defaults-buch.yaml PDF-Konfiguration
```
@ -60,8 +62,17 @@ cd Version_04
python3 Operations_Research_mit_Python_Version_04/build_version_04.py --check # nur prüfen
python3 Operations_Research_mit_Python_Version_04/build_version_04.py --pdf --html
python3 Operations_Research_mit_Python_Version_04/extract_programme_04.py
python3 Operations_Research_mit_Python_Version_04/veroeffentliche_04.py # hochladen
```
`veroeffentliche_04.py` lädt `OR_HTML_04/` per `rsync --delete` nach <https://jamulix.de/OR/>
**nicht** per `scp`, denn das überschreibt nur und löscht nie; eine aus dem Buch entfernte
Seite bliebe sonst dauerhaft online. Vorher prüft es, ob eine Quelldatei neuer ist als die
gebaute Website (die Website hing einmal wochenlang einen Bau zurück, ohne dass es auffiel),
hinterher ruft es vierzehn Adressen per HTTP ab. Bei mehr als 30 Löschungen bricht es ab —
die Liste ist dann anzusehen, bevor `--loeschgrenze N` sie freigibt. Ziel in `.env`
(nicht im Repository, Vorlage `.env.beispiel`); Anmeldung per SSH-Schlüssel, kein Passwort.
`--check` prüft mehr als die Struktur: fehlende Codezäune, Links auf nicht existierende
Dateien und **harte Kapitel-/Abschnitts-/Aufgabennummern im Quelltext**. Es meldet Datei und
Zeile. Der Suchausdruck erlaubt beliebigen Zwischenraum — der letzte gefundene Fall war ein
@ -76,9 +87,10 @@ sonst fälschlich als sichtbaren Umbruch dar.
## Die wichtigsten Konventionen
**Keine abgeleiteten Zahlen im Quelltext.** Das ist die Regel, an der dieses Projekt am
häufigsten gescheitert ist — **sieben** Mal an verschiedenen Stellen verletzt gefunden
häufigsten gescheitert ist — **neun** Mal an verschiedenen Stellen verletzt gefunden
(Anhang A, 66 Programm-Docstrings, die Übersichtstabellen der Anhänge, `CLAUDE.md` selbst,
eine Tabellenzelle, die 98 Lösungsmarken des Anhangs, neun Denkfehler-Verweise).
eine Tabellenzelle, die 98 Lösungsmarken des Anhangs, neun Denkfehler-Verweise, sieben
„Übung N.N"-Verweise in alter Zählung, zuletzt die Dateiübersicht des Leser-Wegweisers).
Seit dem letzten Fund trägt **keine** Marke mehr eine handgeschriebene Nummer, und `--check`
bewacht jede Familie. Konkret:
@ -95,6 +107,11 @@ bewacht jede Familie. Konkret:
einer bereits nummerierten Überschrift. Verwiesen wird auf
`{ref:sec:<kapitel>-denkfehler}`.
* Im Fließtext `{ref:<label>}`, nie eine Literalzahl.
* **Die Dateiübersicht in `Operations_Research_mit_Python_Version_04/README.md`** steht
zwischen `<!-- KAPITELTABELLE:ANFANG -->` und `<!-- KAPITELTABELLE:ENDE -->` und wird von
`schreibe_kapiteltabelle()` aus derselben Struktur erzeugt wie die Nummern im Buch. Nicht
von Hand bearbeiten — `--check` meldet einen veralteten Block, ein Lauf ohne `--check`
zieht ihn nach.
* In Code-Kommentaren und Docstrings der Kapitel**name** („Kapitel Metaheuristiken:"), nie
die Nummer. Auch nicht im Dateinamen.
@ -137,8 +154,10 @@ gehört wie die Arbeit, die sie beschreibt.
## Diagramme
`bilder_04/erzeuge_*.py` erzeugen 18 der 32 SVGs, jeweils **aus derselben Instanz wie das
zugehörige Buchprogramm**. Das ist kein Selbstzweck: Von zehn nachgebauten Bildern förderten
`bilder_04/erzeuge_*.py` erzeugen 18 der 32 Kapiteldiagramme, jeweils **aus derselben
Instanz wie das zugehörige Buchprogramm**. (Das 33. SVG im Verzeichnis ist `titelseite.svg`,
die der Build bei jedem Lauf selbst schreibt — deshalb nennt `README.md` 33 und diese Datei
32.) Das ist kein Selbstzweck: Von zehn nachgebauten Bildern förderten
sieben einen Fehler zutage — dreimal ein Modell, das im Buch gar nicht vorkommt, einmal
widersprüchliche Zahlen zwischen Bild und Text, einmal ein gekipptes Vorzeichen. Wer ein
Diagramm anfasst, vergleicht es zuerst mit dem Modell des Kapitels.
@ -174,10 +193,14 @@ Die Website hat vier Übersichtsseiten: `index.html` (Landing), `programme.html`
weiterleitet — sonst antwortet der Webserver mit 403, weil Directory-Listing abgeschaltet
ist.
**Nach jedem `--pdf --html` muss `OR_HTML_04/` neu auf den Webserver kopiert werden** — sonst
laeuft die veroeffentlichte Fassung dem Repository hinterher. Woran man das merkt: Ein
`curl -s https://jamulix.de/OR/lp.html | grep -c colab.research` muss **0** ergeben, und die
Groesse des dort liegenden PDF muss zur lokalen Datei passen.
**Nach jedem `--pdf --html` gehoert `veroeffentliche_04.py` hinterher** — sonst laeuft die
veroeffentlichte Fassung dem Repository hinterher, und genau das ist einmal wochenlang
unbemerkt geblieben. Von Hand muss dabei nichts mehr geprueft werden: Das Skript weigert
sich, einen veralteten Bau hochzuladen, und ruft anschliessend vierzehn Adressen ab.
Der erste Lauf am 08.09.2026 uebertrug 213 Dateien und loeschte 35 — 33 verwaiste PNGs, ein
leeres `__pycache__/` und die geteilte Anhangseite. Alle 35 haette ein `scp`-Upload stehen
gelassen.
**`COLAB_BASIS_URL` ist bewusst leer.** Colab oeffnet Notebooks nur aus GitHub, Google Drive
oder einem Upload; die URL-Form `colab.research.google.com/github/...` ist fest auf GitHub

View file

@ -29,63 +29,81 @@ arbeiten, üben und eigene Anwendungen bauen können.**
Der Text ist auf mehrere Dateien verteilt, damit er handhabbar bleibt. Die Nummern-Präfixe
legen die Lesereihenfolge fest.
<!-- KAPITELTABELLE:ANFANG - erzeugt von build_version_04.py -->
### Vorspann
| Datei | Inhalt |
| --- | --- |
| [`00_Vorwort_und_Lesehilfe.md`](00_Vorwort_und_Lesehilfe.md) | Vorwort, Lernpfade, Verzeichnis der Beispielprogramme, Setup, wie die Lernelemente funktionieren |
| [`01_Notation_und_Abkuerzungen.md`](01_Notation_und_Abkuerzungen.md) | Alle Symbole und Abkürzungen, ausgeschrieben und erklärt |
| [`00_Vorwort_und_Lesehilfe.md`](00_Vorwort_und_Lesehilfe.md) | Vorwort & Lesehilfe |
| [`01_Notation_und_Abkuerzungen.md`](01_Notation_und_Abkuerzungen.md) | Notation & Abkürzungen |
### Teil I — Grundlagen
### Teil I: Grundlagen des Operations Research
| Datei | Kapitel |
| --- | --- |
| [`10_Einfuehrung_OR.md`](10_Einfuehrung_OR.md) | 1 — Einführung in Operations Research |
| [`11_Mathematisches_Fundament.md`](11_Mathematisches_Fundament.md) | 2 — Vektoren, Matrizen, Konvexität |
| [`12_Python_Oekosystem.md`](12_Python_Oekosystem.md) | 3 — Das Python-Ökosystem für OR |
| [`10_Einfuehrung_OR.md`](10_Einfuehrung_OR.md) | Kapitel 1 — Einführung in Operations Research — Vom Ursprung zur mathematischen Entscheidungsfindung |
| [`11_Mathematisches_Fundament.md`](11_Mathematisches_Fundament.md) | Kapitel 2 — Das mathematische Fundament — Vektoren, Matrizen, Konvexität |
| [`12_Python_Oekosystem.md`](12_Python_Oekosystem.md) | Kapitel 3 — Das Python-Ökosystem für OR — Solver, Bindings und Modellierungsschichten |
| [`13_Vom_Wunsch_zum_Modell.md`](13_Vom_Wunsch_zum_Modell.md) | Kapitel 4 — Vom Management-Wunsch zum Modell |
| [`19_Synthese_Grundlagen.md`](19_Synthese_Grundlagen.md) | Synthese Teil I |
### Teil II — Deterministische Kernverfahren
### Teil II: Die Kernverfahren der deterministischen Optimierung
| Datei | Kapitel |
| --- | --- |
| [`20_Lineare_Programmierung.md`](20_Lineare_Programmierung.md) | 4 — Lineare Programmierung, Simplex, Dualität |
| [`21_MILP.md`](21_MILP.md) | 5 — Gemischt-ganzzahlige Optimierung |
| [`22_CP_SAT.md`](22_CP_SAT.md) | 6 — Constraint Programming mit CP-SAT |
| [`23_Graphen_Fluesse_Touren.md`](23_Graphen_Fluesse_Touren.md) | 7 — Graphen, Flüsse, Tourenplanung |
| [`20_Lineare_Programmierung.md`](20_Lineare_Programmierung.md) | Kapitel 5 — Lineare Programmierung — Simplex, Dualität und Schattenpreise |
| [`21_MILP.md`](21_MILP.md) | Kapitel 6 — Gemischt-ganzzahlige Optimierung — Diskrete Entscheidungen und Branch-and-Bound |
| [`22_CP_SAT.md`](22_CP_SAT.md) | Kapitel 7 — Constraint Programming mit CP-SAT — Logik, Scheduling und Zuweisung |
| [`23_Graphen_Fluesse_Touren.md`](23_Graphen_Fluesse_Touren.md) | Kapitel 8 — Graphen, Flüsse und Touren — Min-Cost-Flow, Matching und VRP |
| [`24_Metaheuristiken.md`](24_Metaheuristiken.md) | Kapitel 9 — Metaheuristiken — wenn der exakte Solver aussteigt |
| [`25_Spaltengenerierung.md`](25_Spaltengenerierung.md) | Kapitel 10 — Spaltengenerierung — das Modell umbauen statt die Lösung raten |
| [`29_Synthese_Kernverfahren.md`](29_Synthese_Kernverfahren.md) | Synthese Teil II |
### Teil III — Nichtlinearität, Unsicherheit, Dynamik
### Teil III: Nichtlinearität, Unsicherheit und mehrperiodige Dynamik
| Datei | Kapitel |
| --- | --- |
| [`30_QP_und_NLP.md`](30_QP_und_NLP.md) | 8 — Quadratische und nichtlineare Optimierung, KKT |
| [`31_Unsicherheit.md`](31_Unsicherheit.md) | 9 — Monte-Carlo, stochastische und robuste Optimierung |
| [`32_Dynamische_Programmierung.md`](32_Dynamische_Programmierung.md) | 10 — Dynamische Programmierung, Bellman |
| [`30_QP_und_NLP.md`](30_QP_und_NLP.md) | Kapitel 11 — Quadratische und nichtlineare Optimierung — KKT, Lagrange, Konvexität |
| [`31_Unsicherheit.md`](31_Unsicherheit.md) | Kapitel 12 — Optimierung unter Unsicherheit — Monte-Carlo, Stochastik, Robustheit |
| [`32_Dynamische_Programmierung.md`](32_Dynamische_Programmierung.md) | Kapitel 13 — Dynamische Programmierung — Die Bellman-Gleichung und Order-Execution |
| [`33_Mehrziel_und_Pareto.md`](33_Mehrziel_und_Pareto.md) | Kapitel 14 — Mehrere Ziele — Pareto-Fronten statt Gewichte |
| [`34_Predict_then_Optimize.md`](34_Predict_then_Optimize.md) | Kapitel 15 — Predict-then-Optimize — die bessere Prognose, die schlechtere Entscheidung |
| [`39_Synthese_Nichtlinear.md`](39_Synthese_Nichtlinear.md) | Synthese Teil III |
### Teil IV — Operations Research an den Finanzmärkten
### Teil IV: Anwendungen — Energiewirtschaft und Finanzmärkte
| Datei | Kapitel |
| --- | --- |
| [`40_Finanzdaten.md`](40_Finanzdaten.md) | 11 — Finanzdaten, Kovarianz, Shrinkage |
| [`41_Markowitz.md`](41_Markowitz.md) | 12 — Markowitz-Portfoliotheorie |
| [`42_CVaR.md`](42_CVaR.md) | 13 — Tail-Risiko, CVaR, Transaktionskosten |
| [`43_Handelsmaschine.md`](43_Handelsmaschine.md) | 14 — Die vollständige Handelsmaschine |
| [`35_Strukturbruecke.md`](35_Strukturbruecke.md) | Kapitel 16 — Die Strukturbrücke — dieselbe Mathematik, zwei Welten |
| [`36_Supply_Chain_Energie.md`](36_Supply_Chain_Energie.md) | Kapitel 17 — Supply-Chain und Energieeinsatz unter Unsicherheit |
| [`40_Finanzdaten.md`](40_Finanzdaten.md) | Kapitel 18 — Finanzdaten-Modellierung — Renditen, Kovarianz und Shrinkage |
| [`41_Markowitz.md`](41_Markowitz.md) | Kapitel 19 — Die moderne Portfoliotheorie nach Markowitz |
| [`42_CVaR.md`](42_CVaR.md) | Kapitel 20 — Tail-Risiko, CVaR und Transaktionskosten |
| [`43_Handelsmaschine.md`](43_Handelsmaschine.md) | Kapitel 21 — Die vollständige quantitative Handelsmaschine |
| [`49_Synthese_Anwendungen.md`](49_Synthese_Anwendungen.md) | Synthese Teil IV |
### Teil V — Praxis
### Teil V: Praxis
| Datei | Kapitel |
| --- | --- |
| [`50_Praxis.md`](50_Praxis.md) | 15 — Praxisfallen und produktiver Einsatz |
| [`59_Projektwerkstatt.md`](59_Projektwerkstatt.md) | Projektwerkstatt: 8 eigene Anwendungen |
| [`50_Praxis.md`](50_Praxis.md) | Kapitel 22 — Praxisfallen und der Weg zum produktiven Einsatz |
| [`51_Testing_und_Deployment.md`](51_Testing_und_Deployment.md) | Kapitel 23 — Testen, Messen, Ausliefern |
| [`52_Synthese_Praxis.md`](52_Synthese_Praxis.md) | Synthese Teil V |
| [`59_Projektwerkstatt.md`](59_Projektwerkstatt.md) | Projektwerkstatt |
### Anhang
| Datei | Inhalt |
| --- | --- |
| [`90_Anhang_Loesungen.md`](90_Anhang_Loesungen.md) | Lösungen zu **allen** Übungsaufgaben und Selbsttests |
| [`91_Anhang_Modellierungsmuster.md`](91_Anhang_Modellierungsmuster.md) | Musterkatalog: 24 wiederkehrende Modellierungstricks |
| [`92_Anhang_Fehlerdiagnose.md`](92_Anhang_Fehlerdiagnose.md) | Diagnose-Handbuch für Solver-Probleme |
| [`94_Anhang_Glossar.md`](94_Anhang_Glossar.md) | Glossar der Fachbegriffe |
| [`95_Anhang_Literatur.md`](95_Anhang_Literatur.md) | Literaturverzeichnis |
| [`90_Anhang_Loesungen.md`](90_Anhang_Loesungen.md) | Anhang A — Lösungen zu allen Übungsaufgaben |
| [`91_Anhang_Modellierungsmuster.md`](91_Anhang_Modellierungsmuster.md) | Anhang B — Katalog der Modellierungsmuster |
| [`92_Anhang_Fehlerdiagnose.md`](92_Anhang_Fehlerdiagnose.md) | Anhang C — Fehlerdiagnose-Handbuch |
| [`93_Anhang_Spickzettel.md`](93_Anhang_Spickzettel.md) | Anhang D — Spickzettel der Solver |
| [`94_Anhang_Glossar.md`](94_Anhang_Glossar.md) | Anhang E — Glossar |
| [`95_Anhang_Literatur.md`](95_Anhang_Literatur.md) | Anhang F — Literaturverzeichnis |
<!-- KAPITELTABELLE:ENDE -->
---
@ -95,7 +113,7 @@ Wer lieber ein einziges Dokument möchte (z. B. zum Drucken oder für die PDF-Er
baut es aus den Einzeldateien:
```bash
cd OR_mit_Python/Version_04 # Wurzel der Version 04
cd Version_04 # das Wurzelverzeichnis des Repositorys
python3 Operations_Research_mit_Python_Version_04/build_version_04.py
```
@ -105,11 +123,15 @@ Inhaltsverzeichnis-Platzhalter und entfernten unnötigen Zeilenumbrüchen innerh
Absätzen. Optionen:
```bash
python3 .../build_version_03.py --pdf # zusätzlich PDF über pandoc + xelatex
python3 .../build_version_03.py --html # zusätzlich Single-Page-HTML
python3 .../build_version_03.py --check # nur Konsistenzprüfung, schreibt nichts
python3 .../build_version_04.py --pdf # zusätzlich PDF über pandoc + xelatex
python3 .../build_version_04.py --html # zusätzlich die Website OR_HTML_04/
python3 .../build_version_04.py --check # nur Konsistenzprüfung, schreibt nichts
```
`--html` erzeugt die **mehrseitige** Website mit Suche, Stichwortverzeichnis und
Notebook-Downloads; das Gesamtdokument auf einer Seite liegt darin als
`gesamtdokument.html`.
---
## Empfohlene Reihenfolge für Kursteilnehmer

View file

@ -2443,6 +2443,86 @@ def baue_stichwortverzeichnis(html_verz: str, struktur: list[dict],
print(f"Stichwortverzeichnis erzeugt: {len(namen)} Hauptbegriffe.")
# Der Leser-Wegweiser Operations_Research_mit_Python_Version_04/README.md fuehrt
# alle Quelldateien mit ihrer Kapitelnummer auf. Diese Tabelle stand von Hand
# darin - und war der NEUNTE Fall der Regel "keine abgeleiteten Zahlen im
# Quelltext": 12 der 15 Nummern zeigten noch die Zaehlung der Version 03, und
# 8 der 23 Kapitel fehlten ganz. Jetzt erzeugt der Bau den Block zwischen den
# beiden Marken; --check meldet, wenn er veraltet ist.
README_MARKE_ANFANG = "<!-- KAPITELTABELLE:ANFANG - erzeugt von build_version_04.py -->"
README_MARKE_ENDE = "<!-- KAPITELTABELLE:ENDE -->"
LESER_README = os.path.join(HIER, "README.md")
def baue_kapiteltabelle(struktur: list[dict]) -> str:
"""Baut die Dateiuebersicht des Leser-Wegweisers aus der Struktur, die
baue_seiten_registry() ohnehin ermittelt - dieselbe Quelle, aus der auch
die Nummern im Buch stammen. Damit koennen die beiden nicht auseinander-
laufen."""
zeilen = [README_MARKE_ANFANG, ""]
def tabelle(eintraege: list[dict], spalte: str) -> None:
zeilen.append(f"| Datei | {spalte} |")
zeilen.append("| --- | --- |")
for e in eintraege:
was = f'{e["nummer"]}{e["titel"]}' if e["nummer"] else e["titel"]
zeilen.append(f'| [`{e["datei"]}`]({e["datei"]}) | {was} |')
zeilen.append("")
vorspann = [e for e in struktur if e["teil"] is None and e["typ"] == "sonder"]
if vorspann:
zeilen.append("### Vorspann")
zeilen.append("")
tabelle(vorspann, "Inhalt")
gesehen: list[str] = []
for e in struktur:
if e["teil"] and e["teil"] not in gesehen:
gesehen.append(e["teil"])
for teil in gesehen:
zeilen.append(f"### {teil}")
zeilen.append("")
tabelle([e for e in struktur if e["teil"] == teil], "Kapitel")
anhaenge = [e for e in struktur if e["typ"] == "anhang"]
if anhaenge:
zeilen.append("### Anhang")
zeilen.append("")
tabelle(anhaenge, "Inhalt")
zeilen.append(README_MARKE_ENDE)
return "\n".join(zeilen)
def schreibe_kapiteltabelle(struktur: list[dict], nur_pruefen: bool) -> list[str]:
"""Ersetzt den markierten Block im Leser-Wegweiser. Liefert bei --check
eine nicht leere Liste, wenn er veraltet ist."""
if not os.path.exists(LESER_README):
return []
with open(LESER_README, encoding="utf-8") as f:
inhalt = f.read()
if README_MARKE_ANFANG not in inhalt or README_MARKE_ENDE not in inhalt:
print(f"FEHLER: {os.path.basename(LESER_README)} hat keine "
f"KAPITELTABELLE-Marken - die Dateiuebersicht kann nicht "
f"erzeugt werden.")
return ["README-Marken"]
vorher, rest = inhalt.split(README_MARKE_ANFANG, 1)
_, nachher = rest.split(README_MARKE_ENDE, 1)
neu = vorher + baue_kapiteltabelle(struktur) + nachher
if neu == inhalt:
return []
if nur_pruefen:
print(f"FEHLER: Die Dateiuebersicht in "
f"{os.path.basename(LESER_README)} ist veraltet - ein Lauf ohne "
f"--check zieht sie nach.")
return ["README-Kapiteltabelle"]
with open(LESER_README, "w", encoding="utf-8") as f:
f.write(neu)
print(f"Dateiuebersicht im Leser-Wegweiser nachgezogen.")
return []
def erzeuge_suchindex(html_verz: str, suchindex: list[dict]) -> None:
# .js mit globaler Variable statt .json + fetch() - siehe Kommentar in
# baue_programme_seite() (fetch() auf lokale Dateien scheitert unter
@ -2511,6 +2591,13 @@ def main() -> int:
print(f"Geprueft: {len(DATEIEN)} Dateien, {zeilen:,} Zeilen, "
f"{zeichen/1024:.0f} KB, {kapitel} Hauptueberschriften.")
# Die Dateiuebersicht des Leser-Wegweisers stammt aus derselben Struktur
# wie die Nummern im Buch. Bei --check wird nur gemeldet, dass sie veraltet
# ist; geschrieben wird sie im normalen Lauf.
_, _, _, struktur_fuer_readme, _ = baue_seiten_registry()
if schreibe_kapiteltabelle(struktur_fuer_readme, nur_pruefen=argumente.check):
return 1
if argumente.check:
print("Nur Pruefung - es wurde nichts geschrieben.")
return 0

View file

@ -192,7 +192,13 @@ def rsync_befehl(env: dict[str, str], probelauf: bool) -> list[str]:
# ohnehin nicht setzen, -a erzeugte dafuer nur Warnungen. --chmod sorgt
# dafuer, dass der Webserver alles lesen kann, unabhaengig von der lokalen
# umask.
befehl = ["rsync", "-rltz", "--delete", "--chmod=D755,F644",
# --checksum: vergleiche INHALTE, nicht Groesse und Zeitstempel. Der Bau
# schreibt alle 42 Seiten bei jedem Lauf neu, auch wenn sich nichts
# geaendert hat - nach der Vorgabe waeren das jedes Mal rund 19 MB
# sinnlose Uebertragung. Mit --checksum meldet der Probelauf genau die
# Dateien, deren Inhalt sich wirklich unterscheidet. Das Lesen der 23 MB
# auf beiden Seiten faellt dagegen nicht ins Gewicht.
befehl = ["rsync", "-rltz", "--checksum", "--delete", "--chmod=D755,F644",
"--skip-compress=pdf/zip/woff2/png/jpg/svg",
"--itemize-changes",
"-e", ssh_optionen(env)]

View file

@ -2158,6 +2158,38 @@ Damit ist der seit dem Colab-Umbau offene Punkt erledigt: Online steht kein tote
Colab-Link mehr, `Notebooks_04/` liefert statt 403 eine Übersichtsseite, und das PDF
entspricht byte-genau dem gebauten.
### 🐛 Neunter Fund: die Dateiübersicht des Leser-Wegweisers
Beim Durchgehen der Dokumentation zeigte sich, dass
`Operations_Research_mit_Python_Version_04/README.md` — die Datei, die **Leser** als erstes
öffnen — noch die Struktur der Version 03 beschrieb:
* **12 der 15 Kapitelnummern waren falsch.** „20_Lineare_Programmierung.md | 4" — es ist
Kapitel 5. Bei den Finanzkapiteln lag die Abweichung bei sieben: README 11, tatsächlich 18.
* **8 der 23 Kapitel fehlten ganz** (unter anderem Metaheuristiken, Spaltengenerierung,
Mehrziel, Predict-then-Optimize, Testing).
* Auch `93_Anhang_Spickzettel.md` fehlte in der Anhangtabelle, und der Bauabschnitt nannte
dreimal `build_version_03.py` statt `_04`, dazu „Single-Page-HTML" für einen Schalter, der
längst eine mehrseitige Website erzeugt.
Dieselbe Regel, dieselbe Ursache: eine abgeleitete Zahl von Hand geschrieben. Deshalb dieselbe
Behandlung — **die Tabelle wird jetzt erzeugt**, nicht korrigiert. `schreibe_kapiteltabelle()`
ersetzt den Block zwischen `<!-- KAPITELTABELLE:ANFANG -->` und `<!-- KAPITELTABELLE:ENDE -->`
aus der Struktur, die `baue_seiten_registry()` ohnehin ermittelt — derselben Quelle, aus der
die Nummern im Buch stammen. Auseinanderlaufen können die beiden damit nicht mehr.
`--check` meldet einen veralteten Block und bricht ab; ein Lauf ohne `--check` zieht ihn nach.
Gegenprobe: Alle **37** Dateien aus `DATEIEN` stehen in der erzeugten Übersicht, mit 23
Kapiteln, 5 Teil-Synthesen und 6 Anhängen.
**Das Muster ist inzwischen belastbar:** Neun Funde, neun Prüfungen. Keiner davon wurde durch
Nachdenken gefunden — jeder durch einen Vergleich der Behauptung mit der Messung.
**Nebenbei am Upload-Skript:** `rsync` bekommt jetzt `--checksum`. Der Bau schreibt alle 42
Seiten bei jedem Lauf neu, auch wenn sich nichts geändert hat; nach Zeitstempel und Größe
hätte das jedes Mal rund 19 MB sinnlose Übertragung bedeutet. Gemessen nach einem Neubau
ohne inhaltliche Änderung: **0 statt 151 Dateien**.
---
## 8. Commit-Historie des V04-Strangs

View file

@ -155,7 +155,7 @@ python3 bilder_04/erzeuge_kondition.py
`--check` prüft mehr als die Struktur: fehlende Codezäune, tote Dateilinks, eine gebrochene
Lesekette, Lösungsabschnitte mit falscher Aufgabenzahl — und **jede von Hand geschriebene
Nummer**, die der Build selbst vergeben müsste. Diese Regel ist die in diesem Projekt am
häufigsten verletzte; acht Fälle wurden nachträglich gefunden, und jeder hat eine eigene
häufigsten verletzte; neun Fälle wurden nachträglich gefunden, und jeder hat eine eigene
Prüfung hinterlassen.
### Externe Werkzeuge