# Kurs-Image zu "Optimierte Entscheidungsfindung mit Python".
#
# Was es ist: eine reproduzierbare Umgebung, in der alle Beispielprogramme
# laufen - ohne dass jemand Solver, BLAS-Bibliotheken und Python-Versionen von
# Hand zusammensuchen muss.
#
# Was es NICHT ist: eine Bauumgebung fuer das Buch. Das PDF braucht pandoc,
# xelatex und inkscape, zusammen ueber ein Gigabyte. Wer das Buch selbst baut,
# ist mit einer lokalen Installation besser bedient (siehe README.md).
#
#   docker build -t or-mit-python .
#   docker run --rm -p 127.0.0.1:8888:8888 or-mit-python   # JupyterLab
#   docker run --rm or-mit-python Installationstest.py     # ein Programm
#   docker run --rm -v "$PWD/ausgabe:/buch/output" or-mit-python Excel_Bruecke.py
#
# Zwei Stufen: Die erste baut die Abhaengigkeiten in eine virtuelle Umgebung
# (und braucht dafuer Compiler), die zweite kopiert nur das Ergebnis. Das
# spart im fertigen Image die gesamte Bauumgebung.

# --- Stufe 1: Abhaengigkeiten uebersetzen und einsammeln ---------------------
FROM python:3.12-slim AS bau

# build-essential wird von Paketen gebraucht, die kein passendes Wheel
# mitbringen. Bleibt in dieser Stufe und landet nie im fertigen Image.
RUN apt-get update && apt-get install --no-install-recommends -y \
        build-essential \
    && rm -rf /var/lib/apt/lists/*

ENV PIP_NO_CACHE_DIR=1 \
    PIP_DISABLE_PIP_VERSION_CHECK=1
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"

# Nur die Abhaengigkeitsbeschreibung kopieren, nicht den Quelltext: So bleibt
# diese Schicht im Cache, solange sich die Abhaengigkeiten nicht aendern.
WORKDIR /buch
COPY pyproject.toml README.md ./

# Die Gruppen aus pyproject.toml. 'figures' fehlt bewusst - es dient dem
# Neuerzeugen der Diagramme, wofuer zusaetzlich Graphviz noetig waere.
#
# ACHTUNG, gewollt: Das Image enthaelt ortools UND highspy, obwohl beide eine
# eigene HiGHS-Kopie mitbringen und sich nicht gemeinsam importieren lassen.
# Der Konflikt wird nicht durch Weglassen geloest, sondern zur Laufzeit durch
# getrennte Prozesse (Kapitel Oekosystem). Ein Image, das nur eines von beiden
# enthaelt, koennte die Haelfte der Beispiele nicht ausfuehren.
RUN pip install ".[finance,large-scale,api,dev,notebook]"

# --- Stufe 2: das eigentliche Image -----------------------------------------
FROM python:3.12-slim AS laufzeit

# libgomp1 wird von OR-Tools und HiGHS zur Laufzeit gebraucht (OpenMP) und ist
# im slim-Image nicht enthalten. Ohne es scheitert der erste Solveraufruf mit
# einem 'libgomp.so.1: cannot open shared object file'.
RUN apt-get update && apt-get install --no-install-recommends -y \
        libgomp1 \
    && rm -rf /var/lib/apt/lists/*

COPY --from=bau /opt/venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH" \
    PYTHONUNBUFFERED=1 \
    MPLBACKEND=Agg

# Kein root. Die Programme brauchen keine Rechte, und ein Kurs-Image, das als
# root laeuft, ist eine schlechte Angewohnheit, die man weitergibt.
RUN useradd --create-home --uid 1000 kurs
WORKDIR /buch
COPY --chown=kurs:kurs Operations_Research_mit_Python_Version_04_Programme/ ./
COPY --chown=kurs:kurs Notebooks_04/ ./Notebooks_04/

# Verzeichnis fuer erzeugte Dateien - als Volume einhaengbar, damit
# Excel-Ausgaben und Diagramme den Container ueberleben. Angelegt wird es
# VOR dem Benutzerwechsel, denn /buch gehoert root.
RUN mkdir -p /buch/output && chown kurs:kurs /buch/output

# Ein Startbefehl, zwei Betriebsarten - damit niemand '--entrypoint' braucht:
#   ohne Argument      -> JupyterLab mit den 25 Kapitel-Notebooks
#   mit Argument       -> genau dieses Programm
RUN printf '%s\n' \
    '#!/bin/sh' \
    'if [ $# -eq 0 ]; then' \
    '  exec jupyter lab --ip=0.0.0.0 --port=8888 --no-browser \' \
    '       --ServerApp.token= --ServerApp.root_dir=/buch' \
    'fi' \
    'exec python "$@"' \
    > /usr/local/bin/starte && chmod +x /usr/local/bin/starte

USER kurs
EXPOSE 8888

# JupyterLab laeuft ohne Token. Das ist fuer ein Kurs-Image auf dem eigenen
# Rechner richtig - deshalb im README die Portfreigabe an 127.0.0.1 binden
# und den Container NICHT ins offene Netz stellen.
ENTRYPOINT ["/usr/local/bin/starte"]
CMD []
