admin: Bericht „Indexierung & Abrufe" — Rolle Erstquelle/Kopie, Quellenbefund, Crawler-Abrufe; Schlüsselpfad ab Projektwurzel

Der Bericht meldete auf Live „nicht konfiguriert", während der Lauf
längst Daten holte: der relative Pfad der Schlüsseldatei war im Cron
(Projektwurzel) lesbar, im Web-Request (public/) nicht. Relative Pfade
werden jetzt ab base_path() aufgelöst.

Die Verteilung trennt zusätzlich nach Rolle in der kanonischen Zuordnung
(1c.8): bei einer Kopie mit Verweis ist „ausgeschlossen" der erwartete
Befund. Neu: Quellenbefund über Erstquellen (welche Adresse Google führt,
fremde Hosts), Abrufe durch Such- und KI-Dienste je Dienst, Tag und
Meldung mit CSV. Herkunftsmarker als Join statt korreliertem EXISTS,
Aggregate 15 Minuten gecacht.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-18 12:53:22 +00:00
parent 1c421c0377
commit c26bdff681
8 changed files with 609 additions and 87 deletions

View file

@ -161,18 +161,32 @@ test('the admin report splits the distribution by origin and lists the latest ch
// Ältere Momentaufnahme zählt nicht — nur die jüngste je Meldung.
PressReleaseIndexCheck::factory()->withState(IndexState::NotCrawled)->create(['press_release_id' => $direct->id, 'portal' => Portal::Presseecho, 'checked_at' => now()->subDays(5)]);
PressReleaseIndexCheck::factory()->withState(IndexState::Indexed)->create(['press_release_id' => $direct->id, 'portal' => Portal::Presseecho, 'checked_at' => now()->subDay()]);
PressReleaseIndexCheck::factory()->withState(IndexState::Indexed)->create(['press_release_id' => $direct->id, 'portal' => Portal::Presseecho, 'checked_at' => now()->subDay(), 'url' => 'https://presseecho.test/de/press-release/'.$direct->slug, 'google_canonical' => 'https://presseecho.test/de/press-release/'.$direct->slug]);
PressReleaseIndexCheck::factory()->withState(IndexState::CrawledNotIndexed)->create(['press_release_id' => $api->id, 'portal' => Portal::Presseecho, 'checked_at' => now()->subDay()]);
// Kopie mit Verweis (1c.8): Google folgt dem Canonical → „ausgeschlossen" ist hier erwartet.
$copy = publishedOn(Portal::Presseecho, 11, ['title' => 'Kopie der Meldung', 'canonical_press_release_id' => $direct->id]);
PressReleaseIndexCheck::factory()->withState(IndexState::Excluded)->create(['press_release_id' => $copy->id, 'portal' => Portal::Presseecho, 'checked_at' => now()->subDay(), 'google_canonical' => 'https://businessportal24.test/de/press-release/'.$direct->slug]);
$distribution = app(IndexStatusReport::class)->distribution();
expect($distribution['presseecho']['direct']['total'])->toBe(2)
->and($distribution['presseecho']['direct']['indexed'])->toBe(1)
->and($distribution['presseecho']['direct']['not_crawled'])->toBe(0)
->and($distribution['presseecho']['direct']['unchecked'])->toBe(1)
->and($distribution['presseecho']['api']['total'])->toBe(1)
->and($distribution['presseecho']['api']['crawled_not_indexed'])->toBe(1)
->and($distribution['businessportal24']['direct']['total'])->toBe(0);
expect($distribution['presseecho']['direct']['source']['total'])->toBe(2)
->and($distribution['presseecho']['direct']['source']['indexed'])->toBe(1)
->and($distribution['presseecho']['direct']['source']['not_crawled'])->toBe(0)
->and($distribution['presseecho']['direct']['source']['unchecked'])->toBe(1)
->and($distribution['presseecho']['direct']['copy']['total'])->toBe(1)
->and($distribution['presseecho']['direct']['copy']['excluded'])->toBe(1)
->and($distribution['presseecho']['api']['source']['total'])->toBe(1)
->and($distribution['presseecho']['api']['source']['crawled_not_indexed'])->toBe(1)
->and($distribution['businessportal24']['direct']['source']['total'])->toBe(0);
// Quellenbefund nur über Erstquellen: die Kopie zählt nicht mit.
$sources = app(IndexStatusReport::class)->sourceSummary(app(IndexStatusChecker::class));
expect($sources['presseecho']['total'])->toBe(2)
->and($sources['presseecho']['self'])->toBe(1)
->and($sources['presseecho']['unknown'])->toBe(1)
->and($sources['presseecho']['own_other'])->toBe(0);
$admin = User::factory()->create(['is_active' => true]);
$admin->assignRole('admin');
@ -186,5 +200,8 @@ test('the admin report splits the distribution by origin and lists the latest ch
->assertSeeText('Direkt eingereichte Meldung')
->assertSeeText('Syndizierte Meldung')
->assertSeeText('gecrawlt, nicht indexiert')
->assertSeeText('Kopie mit Verweis')
->assertSeeText('Quellenbefund')
->assertSeeText('Abrufe durch Such- und KI-Dienste')
->assertSeeText('Kundenansicht');
});