generated from coulomb/repo-seed
Implementiert UC-MB-01 bis UC-MB-03: Marktbegleiter-Katalog (Liste, Detail, Anlegen/Bearbeiten), Ausschreibungspassagen mit Verlässlichkeitsscore (1–10, Validator), Musterauswertung mit Aggregationen (Ausschreiber-Häufigkeit, Ø-Score, Passagen-Anzahl). 4 Tests grün. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
4.5 KiB
4.5 KiB
id, title, status, phase, created, depends_on
| id | title | status | phase | created | depends_on |
|---|---|---|---|---|---|
| WP-0011 | Marktbegleiter-Analyse | done | 11-of-12 | 2026-05-08 | WP-0010 |
WP-0011 — Marktbegleiter-Analyse
Marktbegleiter-Katalog, Passagen-Erfassung mit Verlässlichkeitsscore, Musterauswertung. Referenz: UC-MB-01 bis UC-MB-03.
id: WP-0011-T01
title: Marktbegleiter-Katalog: Liste und Anlegen (UC-MB-01)
status: done
`marktbegleiter/views.py` — marktbegleiter_liste, marktbegleiter_neu/_bearbeiten:
Liste: Tabellenansicht mit Name, Branchen (gekürzt), Aktualisierungsdatum.
Filter: Branche (Freitext-Match auf `relevante_branchen`).
`MarktbegleiterForm(ModelForm)`:
`typische_formulierungen` als Textarea mit Placeholder "Eine Formulierung pro Zeile".
`vertraulichkeit` als Select (intern/streng_vertraulich).
marktbegleiter_detail:
- Profil-Abschnitte: Portfolio, Stärken/Schwächen, typische Formulierungen (als Tags/Pilllen)
- Abschnitt "Verknüpfte Passagen": Liste aller Ausschreibungspassagen, sortiert nach Verlässlichkeit desc
- Aggregation: "Erscheint in X Ausschreibungen, Ø Verlässlichkeit: Y"
- CustomAttribute-Panel
id: WP-0011-T02
title: Ausschreibungspassage erfassen (UC-MB-02, UC-MB-03)
status: done
`marktbegleiter/passagen_views.py` — passagen_liste und passage_neu:
passagen_liste (pro Ausschreibung):
`Ausschreibungspassage.objects.filter(ausschreibung_id=ausschreibung_id).select_related('marktbegleiter', 'dokument')`
Template `marktbegleiter/passagen_liste.html`: Tabelle mit Textauszug (150 Zeichen), Marktbegleiter,
Score, Kategorie, Datum.
`AusschreibungspassageForm(ModelForm)`:
- `passage` als großes Textarea
- `verlaesslichkeitsscore` als Range-Input 1-10 (mit Alpine.js-Beschriftung: "1=sehr unsicher, 10=sehr sicher")
- `marktbegleiter` als Select + Link "Neuen Marktbegleiter anlegen" (öffnet Modal)
- `dokument` als Select (nur Dokumente dieser Ausschreibung)
- `kategorie` als Select: formulierung/leistungsmerkmal/zertifizierung/referenz/sonstiges
Nach Speichern: Passage erscheint auf Ausschreibungsdetail (Tab "Marktbegleiter")
und im Marktbegleiter-Profil.
id: WP-0011-T03
title: Marktbegleiter-Musterauswertung (UC-MB-03)
status: done
`marktbegleiter/views.py` — marktbegleiter_auswertung:
```python
def marktbegleiter_auswertung(request, pk):
mb = get_object_or_404(Marktbegleiter, pk=pk)
passagen = Ausschreibungspassage.objects.filter(marktbegleiter=mb).select_related(
'ausschreibung', 'dokument'
)
# Aggregationen
ausschreiber_haeufigkeit = passagen.values(
'ausschreibung__ausschreiber'
).annotate(count=Count('id')).order_by('-count')[:10]
score_durchschnitt = passagen.aggregate(Avg('verlaesslichkeitsscore'))
ctx = {
'marktbegleiter': mb,
'passagen': passagen,
'ausschreiber_haeufigkeit': ausschreiber_haeufigkeit,
'score_durchschnitt': score_durchschnitt['verlaesslichkeitsscore__avg'],
'anzahl_ausschreibungen': passagen.values('ausschreibung').distinct().count(),
}
return render(request, 'marktbegleiter/auswertung.html', ctx)
Template marktbegleiter/auswertung.html:
- Statistikkacheln: Anzahl Passagen, Anzahl Ausschreibungen, Ø Score
- Tabelle: Häufigste Ausschreiber
- Alle Passagen sortierbar nach Datum / Score / Ausschreibung
```task
id: WP-0011-T04
title: URL-Verkabelung und Tests
status: done
`marktbegleiter/passagen_urls.py`:
```python
urlpatterns = [
path('', passagen_views.passagen_liste, name='liste'),
path('neu/', passagen_views.passage_neu, name='neu'),
path('<int:pk>/', passagen_views.passage_detail, name='detail'),
path('<int:pk>/bearbeiten/', passagen_views.passage_bearbeiten, name='bearbeiten'),
]
marktbegleiter/urls.py:
app_name = 'marktbegleiter'
urlpatterns = [
path('', views.marktbegleiter_liste, name='liste'),
path('neu/', views.marktbegleiter_neu, name='neu'),
path('<int:pk>/', views.marktbegleiter_detail, name='detail'),
path('<int:pk>/auswertung/', views.marktbegleiter_auswertung, name='auswertung'),
path('<int:pk>/bearbeiten/', views.marktbegleiter_bearbeiten, name='bearbeiten'),
]
Global in Haupt-URLs: path('marktbegleiter/', include('vergabe_teilnahme.apps.marktbegleiter.urls'))
Tests:
- Test: Passage anlegen mit verlaesslichkeitsscore=10 → gespeichert
- Test: verlaesslichkeitsscore=11 → ValidationError (MinValueValidator/MaxValueValidator)
- Test: Auswertungs-View mit Passagen → score_durchschnitt korrekt berechnet
- Test: Marktbegleiter-Detail zeigt Passagen-Liste