Notebook-Uebersichtsseite - das fehlende Gegenstueck zu programme.html
Der 403 auf /OR/Notebooks_04/ hatte eine andere Ursache als vermutet: Beim
Eintragen der Website-Adresse ins README hatte ich in der Lesen-Tabelle das
VERZEICHNIS verlinkt. Keine Seite der Website tut das - die Kapitelseiten
zeigen direkt auf Notebooks_04/<name>.ipynb, und das funktioniert. Der
Webserver verweigert Directory-Listing, zu Recht.
Die Frage "gibt es so etwas nicht schon?" war aber berechtigt und hat die
eigentliche Luecke sichtbar gemacht: baue_programme_seite() erzeugt seit
jeher programme.html, eine gestaltete Uebersicht der 76 Programme. Fuer die
25 Notebooks gab es kein Gegenstueck - sie wurden ausgeliefert, aber keine
Seite listete sie.
Neu ist baue_notebooks_seite(), nach demselben Muster und mit denselben
Bausteinen: die Zuordnung Seite -> Notebook kommt fertig aus
baue_notebooks(), Rahmen und Navigation aus baue_seiten_schablone() und
baue_sidebar_html(). Gruppiert nach Vorspann, Kapiteln und Anhaengen; der
einleitende Absatz nennt die drei Wege, ein Notebook auszufuehren.
Dazu ein ZIP mit allen 25 Notebooks (208 KB), byteidentisch ueber zwei
Laeufe. Ein ZIP speichert je Eintrag die Aenderungszeit; ohne festen Wert
entstuende bei jedem Bau eine andere Datei, und da OR_HTML_04/ versioniert
ist, wuechse das Repository bei jedem Lauf. Die Eintraege werden deshalb
sortiert und mit ZipInfo(date_time=(1980,1,1,0,0,0)) geschrieben - dieselbe
Sorgfalt wie svg.hashsalt bei den Diagrammen.
Gegen kuenftige 403 schreiben spiegle_notebooks() und baue_programme_seite()
je eine dreizeilige index.html mit meta refresh in ihr Downloadverzeichnis.
Das behebt zugleich denselben latenten Fall bei programme/.
Ein eigener Fehler, zum zweiten Mal derselbe: Das .replace(",", ".") fuer
deutsche Tausenderpunkte hing am Ende eines mehrzeiligen f-Strings - und in
Python bindet die Methode an die GESAMTE zusammengesetzte Zeichenkette. Aus
"Drei Wege, sie auszufuehren" wurde "Drei Wege. sie auszufuehren". Exakt der
Fehler, vor dem ich in erzeuge_wirkung.py selbst einen Kommentar hinterlassen
hatte. Die Zahl wird jetzt getrennt formatiert.
Geprueft: 25 gelistete Notebooks, 0 tote Links auf der Seite, ZIP mit 25
Eintraegen und identischen Zeitstempeln, Seitenleisteneintrag auf allen 40
Seiten mit Seitenleiste, --check fehlerfrei, PDF unveraendert 759 Seiten.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
a1562d99e5
commit
5c49725d5c
49 changed files with 311 additions and 41 deletions
|
|
@ -161,6 +161,12 @@ Im PDF steht stattdessen ein Hinweis auf die Website.
|
||||||
Das Buch steht online auf <https://jamulix.de/OR/>, das Repository oeffentlich auf
|
Das Buch steht online auf <https://jamulix.de/OR/>, das Repository oeffentlich auf
|
||||||
<https://kitux.de/forgejo/dschlueter/operations_research>.
|
<https://kitux.de/forgejo/dschlueter/operations_research>.
|
||||||
|
|
||||||
|
Die Website hat vier Übersichtsseiten: `index.html` (Landing), `programme.html`,
|
||||||
|
`notebooks.html` und `stichwortverzeichnis.html`. Die beiden Downloadverzeichnisse
|
||||||
|
`programme/` und `Notebooks_04/` bekommen je eine `index.html`, die auf ihre Übersicht
|
||||||
|
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
|
**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
|
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
|
`curl -s https://jamulix.de/OR/lp.html | grep -c colab.research` muss **0** ergeben, und die
|
||||||
|
|
|
||||||
BIN
OR_HTML_04/Notebooks_04.zip
generated
Normal file
BIN
OR_HTML_04/Notebooks_04.zip
generated
Normal file
Binary file not shown.
4
OR_HTML_04/Notebooks_04/index.html
generated
Normal file
4
OR_HTML_04/Notebooks_04/index.html
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<!doctype html><meta charset="utf-8">
|
||||||
|
<meta http-equiv="refresh" content="0; url=../notebooks.html">
|
||||||
|
<title>Weiterleitung</title>
|
||||||
|
<p>Weiter zur <a href="../notebooks.html">Übersicht</a>.</p>
|
||||||
BIN
OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf
generated
BIN
OR_HTML_04/Operations_Research_mit_Python_Version_04.pdf
generated
Binary file not shown.
2
OR_HTML_04/anhang-fehlerdiagnose.html
generated
2
OR_HTML_04/anhang-fehlerdiagnose.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/anhang-glossar-literatur.html
generated
2
OR_HTML_04/anhang-glossar-literatur.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/anhang-loesungen.html
generated
2
OR_HTML_04/anhang-loesungen.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/anhang-modellierungsmuster.html
generated
2
OR_HTML_04/anhang-modellierungsmuster.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/anhang-spickzettel.html
generated
2
OR_HTML_04/anhang-spickzettel.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/bruecke.html
generated
2
OR_HTML_04/bruecke.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/cpsat.html
generated
2
OR_HTML_04/cpsat.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/cvar.html
generated
2
OR_HTML_04/cvar.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/dekomposition.html
generated
2
OR_HTML_04/dekomposition.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/dynamische-programmierung.html
generated
2
OR_HTML_04/dynamische-programmierung.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/einfuehrung.html
generated
2
OR_HTML_04/einfuehrung.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/finanzdaten.html
generated
2
OR_HTML_04/finanzdaten.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/fundament.html
generated
2
OR_HTML_04/fundament.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/graphen.html
generated
2
OR_HTML_04/graphen.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/handelsmaschine.html
generated
2
OR_HTML_04/handelsmaschine.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/index.html
generated
2
OR_HTML_04/index.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/lp.html
generated
2
OR_HTML_04/lp.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/markowitz.html
generated
2
OR_HTML_04/markowitz.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/mehrziel.html
generated
2
OR_HTML_04/mehrziel.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/metaheuristiken.html
generated
2
OR_HTML_04/metaheuristiken.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/milp.html
generated
2
OR_HTML_04/milp.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/modellierung.html
generated
2
OR_HTML_04/modellierung.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/notation.html
generated
2
OR_HTML_04/notation.html
generated
File diff suppressed because one or more lines are too long
116
OR_HTML_04/notebooks.html
generated
Normal file
116
OR_HTML_04/notebooks.html
generated
Normal file
File diff suppressed because one or more lines are too long
2
OR_HTML_04/oekosystem.html
generated
2
OR_HTML_04/oekosystem.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/praxisfallen.html
generated
2
OR_HTML_04/praxisfallen.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/prognose.html
generated
2
OR_HTML_04/prognose.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/programme.html
generated
2
OR_HTML_04/programme.html
generated
File diff suppressed because one or more lines are too long
4
OR_HTML_04/programme/index.html
generated
Normal file
4
OR_HTML_04/programme/index.html
generated
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
<!doctype html><meta charset="utf-8">
|
||||||
|
<meta http-equiv="refresh" content="0; url=../programme.html">
|
||||||
|
<title>Weiterleitung</title>
|
||||||
|
<p>Weiter zur <a href="../programme.html">Übersicht</a>.</p>
|
||||||
2
OR_HTML_04/projektwerkstatt.html
generated
2
OR_HTML_04/projektwerkstatt.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/qp-nlp.html
generated
2
OR_HTML_04/qp-nlp.html
generated
File diff suppressed because one or more lines are too long
4
OR_HTML_04/stichwortverzeichnis.html
generated
4
OR_HTML_04/stichwortverzeichnis.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/supplychain.html
generated
2
OR_HTML_04/supplychain.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/synthese-anwendungen.html
generated
2
OR_HTML_04/synthese-anwendungen.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/synthese-grundlagen.html
generated
2
OR_HTML_04/synthese-grundlagen.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/synthese-kernverfahren.html
generated
2
OR_HTML_04/synthese-kernverfahren.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/synthese-nichtlinear.html
generated
2
OR_HTML_04/synthese-nichtlinear.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/synthese-praxis.html
generated
2
OR_HTML_04/synthese-praxis.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/testing.html
generated
2
OR_HTML_04/testing.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/unsicherheit.html
generated
2
OR_HTML_04/unsicherheit.html
generated
File diff suppressed because one or more lines are too long
2
OR_HTML_04/vorwort.html
generated
2
OR_HTML_04/vorwort.html
generated
File diff suppressed because one or more lines are too long
Binary file not shown.
|
|
@ -34,6 +34,7 @@ import os
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import zipfile
|
||||||
|
|
||||||
HIER = os.path.dirname(os.path.abspath(__file__))
|
HIER = os.path.dirname(os.path.abspath(__file__))
|
||||||
BASIS = os.path.dirname(HIER)
|
BASIS = os.path.dirname(HIER)
|
||||||
|
|
@ -1541,6 +1542,7 @@ def baue_sidebar_html(struktur: list[dict], aktuelle_seite: str, pdf_verfuegbar:
|
||||||
+ "".join(teile_html)
|
+ "".join(teile_html)
|
||||||
+ '<ul class="sidebar-extra">'
|
+ '<ul class="sidebar-extra">'
|
||||||
+ '<li><a href="programme.html">' + icon("book") + ' Beispielprogramme</a></li>'
|
+ '<li><a href="programme.html">' + icon("book") + ' Beispielprogramme</a></li>'
|
||||||
|
+ '<li><a href="notebooks.html">' + icon("book") + ' Notebooks</a></li>'
|
||||||
+ '<li><a href="stichwortverzeichnis.html">' + icon("search")
|
+ '<li><a href="stichwortverzeichnis.html">' + icon("search")
|
||||||
+ ' Stichwortverzeichnis</a></li>'
|
+ ' Stichwortverzeichnis</a></li>'
|
||||||
+ '<li><a href="gesamtdokument.html">' + icon("external-link")
|
+ '<li><a href="gesamtdokument.html">' + icon("external-link")
|
||||||
|
|
@ -1855,6 +1857,21 @@ def baue_notebooks(struktur: list[dict]) -> dict[str, str]:
|
||||||
return notizbuecher
|
return notizbuecher
|
||||||
|
|
||||||
|
|
||||||
|
def schreibe_verzeichnis_weiterleitung(verzeichnis: str, ziel: str) -> None:
|
||||||
|
"""Legt in einem Downloadverzeichnis eine index.html an, die auf die
|
||||||
|
zugehoerige Uebersichtsseite weiterleitet.
|
||||||
|
|
||||||
|
Keine Seite der Website verlinkt diese Verzeichnisse - verlinkt werden
|
||||||
|
immer die Einzeldateien. Wer die URL aber raet oder aus einer aelteren
|
||||||
|
Fassung uebernimmt, bekommt vom Webserver ein 403 (Directory-Listing ist
|
||||||
|
zu Recht abgeschaltet). Drei Zeilen machen daraus etwas Brauchbares."""
|
||||||
|
with open(os.path.join(verzeichnis, "index.html"), "w", encoding="utf-8") as f:
|
||||||
|
f.write('<!doctype html><meta charset="utf-8">\n'
|
||||||
|
f'<meta http-equiv="refresh" content="0; url={ziel}">\n'
|
||||||
|
f'<title>Weiterleitung</title>\n'
|
||||||
|
f'<p>Weiter zur <a href="{ziel}">Übersicht</a>.</p>\n')
|
||||||
|
|
||||||
|
|
||||||
def spiegle_notebooks(html_verz: str, notizbuecher: dict[str, str]) -> None:
|
def spiegle_notebooks(html_verz: str, notizbuecher: dict[str, str]) -> None:
|
||||||
"""Legt die Notebooks neben die Website, damit der Download-Link in der
|
"""Legt die Notebooks neben die Website, damit der Download-Link in der
|
||||||
Kapitelkarte auch dann funktioniert, wenn nur OR_HTML_04/ auf einen
|
Kapitelkarte auch dann funktioniert, wenn nur OR_HTML_04/ auf einen
|
||||||
|
|
@ -1866,6 +1883,7 @@ def spiegle_notebooks(html_verz: str, notizbuecher: dict[str, str]) -> None:
|
||||||
inhalt = f.read()
|
inhalt = f.read()
|
||||||
with open(os.path.join(ziel_verz, name), "w", encoding="utf-8") as f:
|
with open(os.path.join(ziel_verz, name), "w", encoding="utf-8") as f:
|
||||||
f.write(inhalt)
|
f.write(inhalt)
|
||||||
|
schreibe_verzeichnis_weiterleitung(ziel_verz, "../notebooks.html")
|
||||||
|
|
||||||
|
|
||||||
# Letzte Zeile der Karte 'Kapitel auf einen Blick' - davor wird der
|
# Letzte Zeile der Karte 'Kapitel auf einen Blick' - davor wird der
|
||||||
|
|
@ -2131,9 +2149,89 @@ def baue_programme_seite(html_verz: str, struktur: list[dict], pdf_verfuegbar: b
|
||||||
inhalt_html=inhalt)
|
inhalt_html=inhalt)
|
||||||
with open(os.path.join(html_verz, "programme.html"), "w", encoding="utf-8") as f:
|
with open(os.path.join(html_verz, "programme.html"), "w", encoding="utf-8") as f:
|
||||||
f.write(html)
|
f.write(html)
|
||||||
|
schreibe_verzeichnis_weiterleitung(ziel_verz, "../programme.html")
|
||||||
print(f"Programme-Seite erzeugt: {kopiert} Dateien nach {ziel_verz} kopiert.")
|
print(f"Programme-Seite erzeugt: {kopiert} Dateien nach {ziel_verz} kopiert.")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Der ZIP-Nullpunkt. Ein ZIP speichert je Eintrag eine Aenderungszeit - ohne
|
||||||
|
# festen Wert entstuende bei jedem Bau eine andere Datei. Da OR_HTML_04/
|
||||||
|
# versioniert ist, wuerde das Repository sonst bei jedem Lauf um die Groesse
|
||||||
|
# des Archivs wachsen. Dieselbe Ueberlegung wie svg.hashsalt und
|
||||||
|
# metadata={"Date": None} bei den Diagrammgeneratoren.
|
||||||
|
ZIP_ZEITSTEMPEL = (1980, 1, 1, 0, 0, 0)
|
||||||
|
|
||||||
|
|
||||||
|
def baue_notebooks_seite(html_verz: str, struktur: list[dict],
|
||||||
|
notizbuecher: dict[str, str], pdf_verfuegbar: bool) -> None:
|
||||||
|
"""Uebersicht ueber alle Notebooks - das Gegenstueck zu
|
||||||
|
baue_programme_seite() fuer die andere Sorte Download.
|
||||||
|
|
||||||
|
Die Zuordnung Seite -> Notebookname kommt fertig aus baue_notebooks();
|
||||||
|
hier wird nichts neu hergeleitet. Gruppiert wird nach Typ, damit Vorwort
|
||||||
|
und Anhang nicht zwischen den Kapiteln stehen.
|
||||||
|
"""
|
||||||
|
gruppen = {"sonder": ("Vorspann", []), "kapitel": ("Kapitel", []),
|
||||||
|
"anhang": ("Anhänge", [])}
|
||||||
|
for e in struktur:
|
||||||
|
name = notizbuecher.get(e["seite"])
|
||||||
|
if not name:
|
||||||
|
continue
|
||||||
|
beschriftung = (f'{e["nummer"]}: {e["titel"]}' if e.get("nummer")
|
||||||
|
else e["titel"])
|
||||||
|
gruppen.get(e["typ"], gruppen["kapitel"])[1].append(
|
||||||
|
f'<li><a href="{e["seite"]}">{beschriftung}</a> '
|
||||||
|
f'<a class="download-link" href="{NOTEBOOK_NAME}/{name}" download>'
|
||||||
|
f'{icon("download", "icon")} {name}</a></li>')
|
||||||
|
|
||||||
|
anzahl = sum(len(eintraege) for _, eintraege in gruppen.values())
|
||||||
|
|
||||||
|
# --- Alle Notebooks in einem Archiv ---------------------------------
|
||||||
|
archiv_name = f"{NOTEBOOK_NAME}.zip"
|
||||||
|
archiv_pfad = os.path.join(html_verz, archiv_name)
|
||||||
|
with zipfile.ZipFile(archiv_pfad, "w", zipfile.ZIP_DEFLATED) as archiv:
|
||||||
|
for datei in sorted(set(notizbuecher.values())):
|
||||||
|
with open(os.path.join(NOTEBOOK_VERZ, datei), encoding="utf-8") as f:
|
||||||
|
inhalt = f.read()
|
||||||
|
eintrag = zipfile.ZipInfo(f"{NOTEBOOK_NAME}/{datei}", ZIP_ZEITSTEMPEL)
|
||||||
|
eintrag.compress_type = zipfile.ZIP_DEFLATED
|
||||||
|
eintrag.external_attr = 0o644 << 16
|
||||||
|
archiv.writestr(eintrag, inhalt)
|
||||||
|
archiv_kb = os.path.getsize(archiv_pfad) / 1024
|
||||||
|
# Deutsche Tausenderpunkte NUR auf der Zahl. Ein .replace(",", ".") am Ende
|
||||||
|
# eines mehrzeiligen f-Strings trifft die gesamte zusammengesetzte
|
||||||
|
# Zeichenkette - also auch jedes Satzkomma davor.
|
||||||
|
archiv_groesse = f"{archiv_kb:,.0f}".replace(",", ".")
|
||||||
|
|
||||||
|
gruppen_html = "".join(
|
||||||
|
f'<section class="programmgruppe"><h2>{titel}</h2>'
|
||||||
|
f'<ul class="programmliste">{"".join(eintraege)}</ul></section>'
|
||||||
|
for titel, eintraege in gruppen.values() if eintraege)
|
||||||
|
|
||||||
|
inhalt = (
|
||||||
|
f"<h1>Notebooks</h1>"
|
||||||
|
f"<p>Zu jedem Kapitel gehört ein Jupyter-Notebook mit allen Codeblöcken "
|
||||||
|
f"in Lesereihenfolge — insgesamt {anzahl}. Drei Wege, sie auszuführen: "
|
||||||
|
f"in einer lokalen <strong>Jupyter</strong>-Installation öffnen, in "
|
||||||
|
f"<strong>Google Colab</strong> hochladen, oder mit dem "
|
||||||
|
f"<strong>Kurs-Image</strong> starten, das alle Solver mitbringt:</p>"
|
||||||
|
f'<pre><code>docker run --rm -p 127.0.0.1:8888:8888 or-mit-python</code></pre>'
|
||||||
|
f'<p class="notebook-archiv">'
|
||||||
|
f'<a class="download-link" href="{archiv_name}" download>'
|
||||||
|
f'{icon("download", "icon")} Alle {anzahl} Notebooks als ZIP '
|
||||||
|
f'({archiv_groesse} KB)</a></p>'
|
||||||
|
+ gruppen_html)
|
||||||
|
|
||||||
|
html = baue_seiten_schablone(
|
||||||
|
titel="Notebooks", aktuelle_seite="notebooks.html",
|
||||||
|
sidebar_html=baue_sidebar_html(struktur, "notebooks.html", pdf_verfuegbar),
|
||||||
|
inhalt_html=inhalt)
|
||||||
|
with open(os.path.join(html_verz, "notebooks.html"), "w", encoding="utf-8") as f:
|
||||||
|
f.write(html)
|
||||||
|
print(f"Notebook-Seite erzeugt: {anzahl} Notebooks, "
|
||||||
|
f"Archiv {archiv_name} ({archiv_kb:.0f} KB).")
|
||||||
|
|
||||||
|
|
||||||
_UMLAUT_TABELLE = str.maketrans(
|
_UMLAUT_TABELLE = str.maketrans(
|
||||||
{"ä": "a", "ö": "o", "ü": "u", "ß": "s", "Ä": "a", "Ö": "o", "Ü": "u"})
|
{"ä": "a", "ö": "o", "ü": "u", "ß": "s", "Ä": "a", "Ö": "o", "Ü": "u"})
|
||||||
|
|
||||||
|
|
@ -2249,6 +2347,7 @@ def baue_html_website() -> None:
|
||||||
suchindex, pdf_verfuegbar, notizbuecher)
|
suchindex, pdf_verfuegbar, notizbuecher)
|
||||||
baue_landingpage(html_verz, struktur, labels, label_seite, datei_seite, pdf_verfuegbar)
|
baue_landingpage(html_verz, struktur, labels, label_seite, datei_seite, pdf_verfuegbar)
|
||||||
baue_programme_seite(html_verz, struktur, pdf_verfuegbar)
|
baue_programme_seite(html_verz, struktur, pdf_verfuegbar)
|
||||||
|
baue_notebooks_seite(html_verz, struktur, notizbuecher, pdf_verfuegbar)
|
||||||
baue_stichwortverzeichnis(html_verz, struktur, stichwortregister, pdf_verfuegbar)
|
baue_stichwortverzeichnis(html_verz, struktur, stichwortregister, pdf_verfuegbar)
|
||||||
erzeuge_suchindex(html_verz, suchindex)
|
erzeuge_suchindex(html_verz, suchindex)
|
||||||
baue_gesamtdokument(html_verz)
|
baue_gesamtdokument(html_verz)
|
||||||
|
|
|
||||||
41
PROGRESS.md
41
PROGRESS.md
|
|
@ -1952,6 +1952,47 @@ aus der Wurzel legt dort **null** Dateien ab.
|
||||||
**Und `git gc`:** 653 lose Objekte gepackt, `.git` von 71 MB auf **28 MB** — reines
|
**Und `git gc`:** 653 lose Objekte gepackt, `.git` von 71 MB auf **28 MB** — reines
|
||||||
Repacken, kein Inhalt berührt.
|
Repacken, kein Inhalt berührt.
|
||||||
|
|
||||||
|
### ✅ Notebook-Übersichtsseite — und ein 403, dessen Ursache woanders lag
|
||||||
|
|
||||||
|
`https://jamulix.de/OR/Notebooks_04/` lieferte 403. **Die Ursache war ein Link, den ich
|
||||||
|
selbst gesetzt hatte:** Beim Eintragen der Website-Adresse ins README hatte ich in der
|
||||||
|
Lesen-Tabelle das *Verzeichnis* verlinkt. Keine Seite der Website tut das — die
|
||||||
|
Kapitelseiten zeigen direkt auf `Notebooks_04/<name>.ipynb`, und das funktioniert. Der
|
||||||
|
Webserver verweigert Directory-Listing, zu Recht.
|
||||||
|
|
||||||
|
**Die Frage „gibt es so etwas nicht schon?" war aber berechtigt** — und hat die eigentliche
|
||||||
|
Lücke sichtbar gemacht: `baue_programme_seite()` erzeugt seit jeher `programme.html`, eine
|
||||||
|
gestaltete Übersicht der 76 Programme. Für die 25 Notebooks gab es kein Gegenstück; sie
|
||||||
|
wurden ausgeliefert, aber keine Seite listete sie.
|
||||||
|
|
||||||
|
Neu ist deshalb `baue_notebooks_seite()`, gebaut nach demselben Muster und mit denselben
|
||||||
|
Bausteinen: die Zuordnung Seite → Notebook kommt fertig aus `baue_notebooks()`, Rahmen und
|
||||||
|
Navigation aus `baue_seiten_schablone()` und `baue_sidebar_html()`. Gruppiert nach Vorspann,
|
||||||
|
Kapiteln und Anhängen; der einleitende Absatz nennt die drei Wege, ein Notebook auszuführen
|
||||||
|
(Jupyter, Colab-Upload, Kurs-Image).
|
||||||
|
|
||||||
|
**Dazu ein ZIP mit allen 25 Notebooks** — 208 KB, und **byteidentisch über zwei Läufe**. Ein
|
||||||
|
ZIP speichert je Eintrag die Änderungszeit; ohne festen Wert entstünde bei jedem Bau eine
|
||||||
|
andere Datei, und da `OR_HTML_04/` versioniert ist, wüchse das Repository bei jedem Lauf.
|
||||||
|
Die Einträge werden deshalb sortiert und mit `ZipInfo(date_time=(1980,1,1,0,0,0))`
|
||||||
|
geschrieben. Dieselbe Sorgfalt wie `svg.hashsalt` bei den Diagrammen.
|
||||||
|
|
||||||
|
**Gegen künftige 403** schreiben `spiegle_notebooks()` und `baue_programme_seite()` je eine
|
||||||
|
dreizeilige `index.html` mit `meta refresh` in ihr Downloadverzeichnis. Wer die URL rät oder
|
||||||
|
einen alten Link hat, landet auf der Übersicht statt auf einem Fehler — das behebt zugleich
|
||||||
|
denselben latenten Fall bei `programme/`.
|
||||||
|
|
||||||
|
**Ein eigener Fehler, zum zweiten Mal derselbe:** Das `.replace(",", ".")` für deutsche
|
||||||
|
Tausenderpunkte hing am Ende eines mehrzeiligen f-Strings — und in Python bindet die
|
||||||
|
Methode an die **gesamte** zusammengesetzte Zeichenkette. Aus „Drei Wege, sie auszuführen"
|
||||||
|
wurde „Drei Wege. sie auszuführen". Exakt der Fehler, vor dem ich in `erzeuge_wirkung.py`
|
||||||
|
selbst einen Kommentar hinterlassen hatte. Die Zahl wird jetzt getrennt formatiert, mit
|
||||||
|
demselben Warnhinweis daneben.
|
||||||
|
|
||||||
|
Geprüft: 25 gelistete Notebooks, 0 tote Links auf der Seite, ZIP mit 25 Einträgen und
|
||||||
|
identischen Zeitstempeln, Seitenleisteneintrag auf allen 40 Seiten mit Seitenleiste
|
||||||
|
(`gesamtdokument.html` hat konstruktionsbedingt keine).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 8. Commit-Historie des V04-Strangs
|
## 8. Commit-Historie des V04-Strangs
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ Autor: **Dieter Schlüter** · Lizenz: MIT (Code) und CC BY-SA 4.0 (Text)
|
||||||
| **Website** — eine Seite je Kapitel, mit Volltextsuche, Stichwortverzeichnis und vier interaktiven Plotly-Figuren | **<https://jamulix.de/OR/>** — oder lokal `OR_HTML_04/index.html` |
|
| **Website** — eine Seite je Kapitel, mit Volltextsuche, Stichwortverzeichnis und vier interaktiven Plotly-Figuren | **<https://jamulix.de/OR/>** — oder lokal `OR_HTML_04/index.html` |
|
||||||
| **PDF**, 759 Seiten | <https://jamulix.de/OR/Operations_Research_mit_Python_Version_04.pdf> — oder lokal [`…_Version_04.pdf`](Operations_Research_mit_Python_Version_04.pdf) |
|
| **PDF**, 759 Seiten | <https://jamulix.de/OR/Operations_Research_mit_Python_Version_04.pdf> — oder lokal [`…_Version_04.pdf`](Operations_Research_mit_Python_Version_04.pdf) |
|
||||||
| **Ein einziges HTML-Dokument** | <https://jamulix.de/OR/gesamtdokument.html> |
|
| **Ein einziges HTML-Dokument** | <https://jamulix.de/OR/gesamtdokument.html> |
|
||||||
| **Notebooks**, eines je Kapitel | <https://jamulix.de/OR/Notebooks_04/> — oder lokal `Notebooks_04/` |
|
| **Notebooks**, eines je Kapitel, einzeln oder als ZIP | <https://jamulix.de/OR/notebooks.html> — oder lokal `Notebooks_04/` |
|
||||||
|
|
||||||
Die Website ist selbstgenügsam — alle Verweise zeigen auf Unterverzeichnisse (`assets/`,
|
Die Website ist selbstgenügsam — alle Verweise zeigen auf Unterverzeichnisse (`assets/`,
|
||||||
`bilder_04/`, `katex/`, `Notebooks_04/`). Der Ordner lässt sich unverändert auf einen
|
`bilder_04/`, `katex/`, `Notebooks_04/`). Der Ordner lässt sich unverändert auf einen
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue