--- id: WP-0011 title: Marktbegleiter-Analyse status: done phase: 11-of-12 created: "2026-05-08" depends_on: WP-0010 --- # WP-0011 — Marktbegleiter-Analyse Marktbegleiter-Katalog, Passagen-Erfassung mit Verlässlichkeitsscore, Musterauswertung. Referenz: UC-MB-01 bis UC-MB-03. --- ```task 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 ``` ```task 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. ``` ```task 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('/', passagen_views.passage_detail, name='detail'), path('/bearbeiten/', passagen_views.passage_bearbeiten, name='bearbeiten'), ] ``` `marktbegleiter/urls.py`: ```python app_name = 'marktbegleiter' urlpatterns = [ path('', views.marktbegleiter_liste, name='liste'), path('neu/', views.marktbegleiter_neu, name='neu'), path('/', views.marktbegleiter_detail, name='detail'), path('/auswertung/', views.marktbegleiter_auswertung, name='auswertung'), path('/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 ```