vergabe-teilnahme/workplans/VT-WP-0011-marktbegleiter.md
codex 8838725e70
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
fix(workplans): bring vergabe-teilnahme onto the work-record convention
This repository predates the convention wholesale: no file declared a type at
all, identifiers were bare WP-NNNN with no repository prefix, and no file
carried a hub identifier. Seventeen real workplans were therefore invisible to
every projection while the hub separately held sixteen records for them — so a
forge-derived reset read all sixteen as no longer deriving.

Adds type: workplan, moves identifiers to VT-WP-NNNN, qualifies 135 task
identifiers with their workplan, and derives projection UUIDs. Frontmatter
status done maps to finished; task-block statuses are a different vocabulary and
are untouched.

Known remaining divergence: task fences contain prose after the YAML, so only 15
of 135 tasks parse. Recorded rather than rewritten — reflowing someone's
workplan content is not a mechanical change.

Refs STATE-WP-0083-T05

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
2026-08-26 20:22:29 +02:00

4.6 KiB

id type title status phase created depends_on domain repo state_hub_workstream_id
VT-WP-0011 workplan Marktbegleiter-Analyse finished 11-of-12 2026-05-08 WP-0010 communication vergabe-teilnahme fe7cd651-b5c1-58e7-9bd2-8641b589bd2e

WP-0011 — Marktbegleiter-Analyse

Marktbegleiter-Katalog, Passagen-Erfassung mit Verlässlichkeitsscore, Musterauswertung. Referenz: UC-MB-01 bis UC-MB-03.


id: VT-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: VT-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: VT-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: VT-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