add docker file

This commit is contained in:
John Gatward
2026-03-20 10:36:22 +00:00
parent 80a66bc44c
commit bdcbe6efbd
7 changed files with 598 additions and 1 deletions

35
.dockerignore Normal file
View File

@@ -0,0 +1,35 @@
# Python
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
# Virtual environments
.venv/
venv/
# Development tools
.idea/
.vscode/
*.egg-info/
# uv cache
.uv/
# Local output
dist/
# Don't bake data into the image — mount it at runtime
# data.csv is excluded here so it must be provided via docker-compose volume
data.csv
# Git
.git/
.gitignore
# Docker itself
Dockerfile
docker-compose.yml
.dockerignore

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
.idea
.venv
__pycache__/

40
Dockerfile Normal file
View File

@@ -0,0 +1,40 @@
# syntax=docker/dockerfile:1
FROM python:3.13-slim
# Pull uv binary from the official image (no pip overhead)
COPY --from=ghcr.io/astral-sh/uv:latest /uv /uvx /usr/local/bin/
# Non-root user for security
RUN adduser --disabled-password --no-create-home appuser
WORKDIR /app
# Install dependencies first (layer is cached unless lock file changes)
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project
# Copy application source
COPY src/ ./src/
# data.csv is NOT baked into the image — it is mounted at runtime via docker-compose
# so updates to the CSV don't require a rebuild.
RUN chown -R appuser:appuser /app
USER appuser
# WORKDIR=/app → gunicorn CWD is /app → data.csv resolves to /app/data.csv (mounted volume)
# PYTHONPATH=/app/src → 'app' module resolves to src/app.py
ENV PYTHONPATH=/app/src
EXPOSE 8000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000')"
CMD ["uv", "run", "--no-dev", "gunicorn", \
"--workers", "2", \
"--bind", "0.0.0.0:8000", \
"--access-logfile", "-", \
"--error-logfile", "-", \
"app:app"]

506
dist/index.html vendored Normal file

File diff suppressed because one or more lines are too long

View File

@@ -6,6 +6,7 @@ readme = "README.md"
requires-python = ">=3.13"
dependencies = [
"flask>=3.1.1",
"gunicorn>=23.0.0",
"pandas>=2.3.0",
"plotly>=6.1.2",
"scikit-learn>=1.7.0",

View File

@@ -15,7 +15,7 @@
<header class="bg-blue-900 text-white shadow-lg">
<div class="max-w-6xl mx-auto px-4 sm:px-8 py-8 sm:py-10">
<p class="text-blue-300 text-xs font-semibold tracking-widest uppercase mb-2">Performance Dashboard</p>
<h1 class="text-3xl sm:text-4xl font-bold tracking-tight">🍺 The Hope Pub Quiz</h1>
<h1 class="text-3xl sm:text-4xl font-bold tracking-tight">The Hope Pub Quiz</h1>
<p class="text-blue-300 mt-2 text-sm">Trying to convince ourselves we're not stupid</p>
</div>
</header>

14
uv.lock generated
View File

@@ -49,6 +49,18 @@ wheels = [
{ url = "https://files.pythonhosted.org/packages/ec/f9/7f9263c5695f4bd0023734af91bedb2ff8209e8de6ead162f35d8dc762fd/flask-3.1.2-py3-none-any.whl", hash = "sha256:ca1d8112ec8a6158cc29ea4858963350011b5c846a414cdb7a954aa9e967d03c", size = 103308, upload-time = "2025-08-19T21:03:19.499Z" },
]
[[package]]
name = "gunicorn"
version = "25.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
]
sdist = { url = "https://files.pythonhosted.org/packages/66/13/ef67f59f6a7896fdc2c1d62b5665c5219d6b0a9a1784938eb9a28e55e128/gunicorn-25.1.0.tar.gz", hash = "sha256:1426611d959fa77e7de89f8c0f32eed6aa03ee735f98c01efba3e281b1c47616", size = 594377, upload-time = "2026-02-13T11:09:58.989Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/da/73/4ad5b1f6a2e21cf1e85afdaad2b7b1a933985e2f5d679147a1953aaa192c/gunicorn-25.1.0-py3-none-any.whl", hash = "sha256:d0b1236ccf27f72cfe14bce7caadf467186f19e865094ca84221424e839b8b8b", size = 197067, upload-time = "2026-02-13T11:09:57.146Z" },
]
[[package]]
name = "itsdangerous"
version = "2.2.0"
@@ -272,6 +284,7 @@ version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "flask" },
{ name = "gunicorn" },
{ name = "pandas" },
{ name = "plotly" },
{ name = "scikit-learn" },
@@ -281,6 +294,7 @@ dependencies = [
[package.metadata]
requires-dist = [
{ name = "flask", specifier = ">=3.1.1" },
{ name = "gunicorn", specifier = ">=23.0.0" },
{ name = "pandas", specifier = ">=2.3.0" },
{ name = "plotly", specifier = ">=6.1.2" },
{ name = "scikit-learn", specifier = ">=1.7.0" },