Files
vergabe-teilnahme/workplans/WP-0001-projektgeruest.md
2026-05-08 14:26:48 +02:00

12 KiB

id, title, status, phase, created
id title status phase created
WP-0001 Projektgerüst — Django-Setup, Tailwind, Dev-Stack done 1-of-12 2026-05-08

WP-0001 — Projektgerüst

Legt das vollständige Django-Projektgerüst an: uv, Projektstruktur, Settings, alle App-Hüllen, Tailwind CSS v4 via Vite, HTMX + Alpine.js, Docker Compose für PostgreSQL, pytest-django, Makefile.

Referenzdokumente: wiki/ArchitectureBlueprint.md Abschnitte 2 und 3. Arbeitsverzeichnis: /home/worsch/vergabe-teilnahme/


id: WP-0001-T01
title: pyproject.toml und uv-Projektstruktur anlegen
status: done

Erstelle `pyproject.toml` mit uv als Package-Manager.

Abhängigkeiten (production):
  django>=5.2, psycopg[binary]>=3.2, django-storages>=1.14,
  whitenoise>=6.7, python-decouple>=3.8

Abhängigkeiten (dev):
  pytest-django>=4.8, pytest-cov>=5.0, factory-boy>=3.3,
  ruff>=0.4, mypy>=1.10, django-stubs>=5.0

Python: >=3.12

Erstelle außerdem `.python-version` mit `3.12`.
Führe `uv sync` aus und bestätige, dass die virtuelle Umgebung erstellt wird.
id: WP-0001-T02
title: Django-Projekt initialisieren und Settings-Struktur anlegen
status: done

Führe `uv run django-admin startproject vergabe_teilnahme .` aus
(Punkt am Ende — kein verschachteltes Projektverzeichnis).

Erstelle `vergabe_teilnahme/settings/` mit:
- `__init__.py` (leer)
- `base.py` — gemeinsame Settings (INSTALLED_APPS, TEMPLATES, STATIC, MEDIA,
  AUTH_USER_MODEL = 'accounts.Mitarbeiter', DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField',
  LANGUAGE_CODE = 'de-de', TIME_ZONE = 'Europe/Berlin', USE_I18N = True, USE_TZ = True)
- `dev.py` — importiert base, setzt DEBUG=True, ALLOWED_HOSTS=['*'],
  DATABASE aus python-decouple .env
- `prod.py` — importiert base, DEBUG=False, ALLOWED_HOSTS aus Env,
  WhiteNoise-Middleware, SECURE_* Flags

Passe `manage.py` und `vergabe_teilnahme/wsgi.py` auf
`DJANGO_SETTINGS_MODULE = 'vergabe_teilnahme.settings.dev'` an.
id: WP-0001-T03
title: .env.example und PostgreSQL-Konfiguration
status: done

Erstelle `.env.example`:

DATABASE_URL=postgres://vergabe:vergabe@localhost:5432/vergabe_db SECRET_KEY=change-me-in-production DEBUG=True ALLOWED_HOSTS=localhost,127.0.0.1 MEDIA_ROOT=media/ MAX_UPLOAD_SIZE=52428800


In `settings/base.py` lese DATABASE_URL via `python-decouple` und parse mit
`dj-database-url` (füge `dj-database-url>=2.1` zu pyproject.toml hinzu).

Erstelle `.env` (nur lokal, in .gitignore) mit Entwicklungswerten.
Prüfe dass `.env` und `*.sqlite3` in `.gitignore` enthalten sind.
id: WP-0001-T04
title: Alle Django-Apps anlegen
status: done

Erstelle folgende Apps mit `uv run manage.py startapp <name>`:
  core, accounts, ausschreibungen, lose, aufgaben,
  dokumente, preise, partner, bibliothek, marktbegleiter,
  nachbetrachtung, feedback

Verschiebe jede App in ein eigenes Unterverzeichnis:
  `vergabe_teilnahme/apps/<app_name>/`

Passe in jeder App `apps.py` den `name` auf `vergabe_teilnahme.apps.<app_name>` an.

Füge alle Apps zu `INSTALLED_APPS` in `settings/base.py` hinzu.
Stelle sicher, dass `django.contrib.contenttypes` ebenfalls in INSTALLED_APPS ist
(wird für GenericForeignKey im core-Modell benötigt).
id: WP-0001-T05
title: Tailwind CSS v4 via Vite integrieren
status: done

Erstelle `package.json` im Projektwurzelverzeichnis:
```json
{
  "scripts": {
    "dev": "vite build --watch",
    "build": "vite build"
  },
  "devDependencies": {
    "vite": "^5.0",
    "@tailwindcss/vite": "^4.0",
    "tailwindcss": "^4.0"
  }
}

Erstelle vite.config.js:

import { defineConfig } from 'vite'
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
  plugins: [tailwindcss()],
  build: { outDir: 'static/dist', emptyOutDir: true,
           rollupOptions: { input: 'static/src/main.css' } }
})

Erstelle static/src/main.css:

@import "tailwindcss";
@layer base { /* German-app base resets */ }
@layer components {
  .card { @apply bg-white rounded-xl border border-slate-200 shadow-sm p-6; }
  .btn-primary { @apply bg-brand-500 text-white px-4 py-2 rounded-lg hover:bg-brand-600 transition-colors; }
  .btn-secondary { @apply bg-white text-slate-700 border border-slate-300 px-4 py-2 rounded-lg hover:bg-slate-50; }
  .btn-danger { @apply bg-red-600 text-white px-4 py-2 rounded-lg hover:bg-red-700; }
  .btn-ghost { @apply text-slate-600 px-3 py-2 rounded-lg hover:bg-slate-100; }
  .field-row { @apply grid grid-cols-3 gap-4 py-3 border-b border-slate-100 last:border-0; }
  .field-label { @apply text-sm font-medium text-slate-500 col-span-1; }
  .field-value { @apply text-sm text-slate-900 col-span-2; }
  .phase-badge { @apply inline-flex items-center justify-center w-7 h-7 rounded-full text-sm font-bold; }
  .phase-todo { @apply phase-badge bg-slate-200 text-slate-500; }
  .phase-active { @apply phase-badge bg-brand-500 text-white; }
  .phase-done { @apply phase-badge bg-green-500 text-white; }
  .phase-warn { @apply phase-badge bg-amber-400 text-amber-900; }
  .section-title { @apply text-base font-semibold text-slate-900 mb-4; }
  .page-title { @apply text-2xl font-bold text-slate-900; }
  .form-input { @apply w-full rounded-lg border border-slate-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-brand-500 focus:border-transparent; }
  .form-label { @apply block text-sm font-medium text-slate-700 mb-1; }
  .table-base { @apply w-full text-sm text-left; }
  .table-header { @apply bg-slate-50 text-slate-500 font-medium text-xs uppercase tracking-wide; }
  .table-row { @apply border-t border-slate-100 hover:bg-slate-50 transition-colors; }
}

Füge CSS-Theme-Token für brand in main.css hinzu:

@theme {
  --color-brand-50: #f0f4ff;
  --color-brand-100: #dce7ff;
  --color-brand-500: #3b5bdb;
  --color-brand-600: #2f4ac7;
  --color-brand-700: #2541b2;
  --color-brand-900: #152d99;
}

Konfiguriere Django STATICFILES_DIRS und STATIC_ROOT in settings/base.py. Füge STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage' in settings/prod.py ein. Führe npm install aus.


```task
id: WP-0001-T06
title: HTMX und Alpine.js einbinden
status: done

Lade HTMX und Alpine.js als lokale Vendor-Dateien (keine CDN-Abhängigkeit):
- `static/vendor/htmx/htmx.min.js` — HTMX 2.x (von unpkg herunterladen)
- `static/vendor/alpinejs/alpine.min.js` — Alpine.js 3.x

Füge in `settings/base.py` hinzu:
```python
STATICFILES_DIRS = [BASE_DIR / 'static']

Die Einbindung im base.html-Template erfolgt in WP-0003. Erstelle hier nur die Verzeichnisstruktur und die Dateien. Prüfe: ls static/vendor/htmx/ und ls static/vendor/alpinejs/ sollten die Dateien zeigen.


```task
id: WP-0001-T07
title: Docker Compose für Entwicklungs-PostgreSQL
status: done

Erstelle `docker-compose.dev.yml`:
```yaml
services:
  db:
    image: postgres:16-alpine
    environment:
      POSTGRES_DB: vergabe_db
      POSTGRES_USER: vergabe
      POSTGRES_PASSWORD: vergabe
    ports:
      - "5432:5432"
    volumes:
      - postgres_data:/var/lib/postgresql/data
volumes:
  postgres_data:

Erstelle außerdem docker-compose.test.yml für CI (gleiche Konfiguration, anderer DB-Name: vergabe_test).

Starte die DB: docker compose -f docker-compose.dev.yml up -d Prüfe Verbindung: uv run manage.py check --database default


```task
id: WP-0001-T08
title: pytest-django konfigurieren
status: done

Füge in `pyproject.toml` hinzu:
```toml
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "vergabe_teilnahme.settings.dev"
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = "--tb=short -q"

[tool.ruff]
line-length = 100
target-version = "py312"
select = ["E", "F", "I", "N", "UP"]

Erstelle conftest.py im Projektwurzel:

import pytest
@pytest.fixture
def mitarbeiter(db):
    from vergabe_teilnahme.apps.accounts.models import Mitarbeiter
    return Mitarbeiter.objects.create_user(
        username='testuser', password='testpass', first_name='Test', last_name='User'
    )

Prüfe: uv run pytest --co -q (keine Tests vorhanden, aber Konfiguration valide).


```task
id: WP-0001-T09
title: Makefile für häufige Dev-Commands
status: done

Erstelle `Makefile` im Projektwurzel:
```makefile
.PHONY: dev db migrate shell test lint css

db:
	docker compose -f docker-compose.dev.yml up -d

dev: db
	uv run manage.py runserver 0.0.0.0:8000

css:
	npm run dev

migrate:
	uv run manage.py makemigrations
	uv run manage.py migrate

shell:
	uv run manage.py shell_plus 2>/dev/null || uv run manage.py shell

test:
	uv run pytest

lint:
	uv run ruff check .
	uv run mypy vergabe_teilnahme/

createsuperuser:
	uv run manage.py createsuperuser

collectstatic:
	uv run manage.py collectstatic --noinput

Prüfe: make db startet PostgreSQL, make migrate läuft fehlerfrei durch (zu diesem Zeitpunkt noch ohne Fachmodelle — nur Django-Default-Migrationen).


```task
id: WP-0001-T10
title: Django URL-Grundkonfiguration und Health-Check
status: done

Editiere `vergabe_teilnahme/urls.py`:
```python
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
from django.http import JsonResponse

def health(request):
    return JsonResponse({'status': 'ok'})

urlpatterns = [
    path('admin/', admin.site.urls),
    path('health/', health),
    # Module-URLs werden in späteren Workplans ergänzt
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Füge in settings/base.py hinzu:

MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'

Prüfe: make dev startet ohne Fehler, curl http://localhost:8000/health/ gibt {"status": "ok"} zurück.


```task
id: WP-0001-T11
title: CLAUDE.md mit Build-Commands aktualisieren
status: done

Aktualisiere `/home/worsch/vergabe-teilnahme/CLAUDE.md` um einen Abschnitt
"## Entwicklungs-Commands":

```markdown
## Entwicklungs-Commands

```bash
make db           # PostgreSQL via Docker starten
make dev          # Django-Dev-Server (Port 8000)
make css          # Tailwind CSS im Watch-Modus
make migrate      # Migrations generieren und ausführen
make test         # pytest ausführen
make lint         # ruff + mypy
uv run manage.py test <app>   # Einzelne App testen
uv run pytest tests/<pfad>.py # Einzelne Testdatei

Projektstruktur

vergabe_teilnahme/
├── apps/           # Alle Django-Apps
│   ├── core/       # FlexibleModel, CustomAttribute, EntityFieldConfig, Freigabe
│   ├── accounts/   # Mitarbeiter (AbstractUser)
│   └── ...         # je eine App pro Fachdomäne
├── settings/       # base.py, dev.py, prod.py
└── urls.py

static/
├── src/main.css    # Tailwind-Quelldatei
├── vendor/         # HTMX, Alpine.js
└── dist/           # Build-Output (gitignored)

workplans/          # Ralph-Loop-Workplans
wiki/               # PRD, Blueprint, Use-Case-Katalog

Füge außerdem `static/dist/` zu `.gitignore` hinzu.
id: WP-0001-T12
title: Erstes `uv run manage.py migrate` und Smoke-Test
status: done

Führe die gesamte initiale Setup-Sequenz durch und verifiziere:

1. `make db` → PostgreSQL läuft
2. `uv run manage.py migrate` → alle Django-Default-Migrationen laufen sauber durch
3. `uv run manage.py check` → keine Fehler
4. `uv run pytest` → 0 Tests gesammelt, kein Fehler
5. `npm run build` → `static/dist/` enthält die kompilierte CSS-Datei
6. `make dev` → Server startet, `/health/` antwortet mit 200

Notiere etwaige Fehler und behebe sie. Erst wenn alle 6 Checks bestanden sind
gilt dieser Task als erledigt.