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:
parent
1c421c0377
commit
c26bdff681
8 changed files with 609 additions and 87 deletions
|
|
@ -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');
|
||||
});
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use App\Models\Category;
|
|||
use App\Models\Company;
|
||||
use App\Models\PressRelease;
|
||||
use App\Models\PressReleaseCrawlerHit;
|
||||
use App\Services\PressRelease\CrawlerHitReport;
|
||||
use App\Support\CrawlerIdentifier;
|
||||
use Tests\TestCase;
|
||||
|
||||
|
|
@ -88,3 +89,38 @@ test('the purge command removes crawler hits older than the retention period', f
|
|||
|
||||
expect(PressReleaseCrawlerHit::query()->count())->toBe(1);
|
||||
});
|
||||
|
||||
test('the crawler report aggregates fetches by service, by day and by release', function () {
|
||||
/** @var TestCase $this */
|
||||
$release = makeCrawlerLogRelease();
|
||||
$other = PressRelease::factory()->published()->forPortal(Portal::Presseecho)->create(['title' => 'Zweite Meldung', 'published_at' => now()->subDay()]);
|
||||
|
||||
PressReleaseCrawlerHit::factory()->count(3)->create(['press_release_id' => $release->id]);
|
||||
PressReleaseCrawlerHit::factory()->service('google', 'Googlebot/2.1')->create(['press_release_id' => $release->id]);
|
||||
PressReleaseCrawlerHit::factory()->service('google', 'Googlebot/2.1')->create(['press_release_id' => $other->id]);
|
||||
$old = PressReleaseCrawlerHit::factory()->service('bing', 'bingbot/2.0')->create(['press_release_id' => $other->id]);
|
||||
$old->forceFill(['created_at' => now()->subDays(40)])->saveQuietly();
|
||||
|
||||
$report = app(CrawlerHitReport::class);
|
||||
$byService = collect($report->byService(30))->keyBy('service');
|
||||
|
||||
expect($byService->keys()->all())->toBe(['openai', 'google', 'bing'])
|
||||
->and($byService['openai']['fetches'])->toBe(3)
|
||||
->and($byService['openai']['releases'])->toBe(1)
|
||||
->and($byService['google']['fetches'])->toBe(2)
|
||||
->and($byService['google']['releases'])->toBe(2)
|
||||
->and($byService['google']['kind'])->toBe('search')
|
||||
->and($byService['bing']['fetches'])->toBe(0)
|
||||
->and($byService['bing']['fetches_total'])->toBe(1);
|
||||
|
||||
$daily = $report->daily(14);
|
||||
expect($daily)->toHaveCount(14)
|
||||
->and($daily[13]['ai'])->toBe(3)
|
||||
->and($daily[13]['search'])->toBe(2)
|
||||
->and($daily[0]['ai'] + $daily[0]['search'])->toBe(0);
|
||||
|
||||
$top = $report->topReleases(30);
|
||||
expect($top[0]['press_release_id'])->toBe($release->id)
|
||||
->and($top[0]['fetches'])->toBe(4)
|
||||
->and($top[0]['services'])->toBe(2);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue