Files
vergabe-teilnahme/workplans/WP-0011-marktbegleiter.md
tegwick bde10f3a69 feat(WP-0011): Marktbegleiter-Analyse — Katalog, Passagen, Auswertung
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>
2026-05-11 16:20:55 +02:00

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