generated from coulomb/repo-seed
406 lines
12 KiB
Markdown
406 lines
12 KiB
Markdown
---
|
|
id: WP-0001
|
|
title: Projektgerüst — Django-Setup, Tailwind, Dev-Stack
|
|
status: done
|
|
phase: 1-of-12
|
|
created: "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/`
|
|
|
|
---
|
|
|
|
```task
|
|
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.
|
|
```
|
|
|
|
```task
|
|
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.
|
|
```
|
|
|
|
```task
|
|
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.
|
|
```
|
|
|
|
```task
|
|
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).
|
|
```
|
|
|
|
```task
|
|
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`:
|
|
```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`:
|
|
```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:
|
|
```css
|
|
@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:
|
|
```python
|
|
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:
|
|
```python
|
|
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.
|
|
```
|
|
|
|
```task
|
|
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.
|
|
```
|