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>
112 lines
4.2 KiB
PHP
112 lines
4.2 KiB
PHP
<?php
|
|
|
|
namespace App\Services\PressRelease;
|
|
|
|
use App\Models\PressReleaseCrawlerHit;
|
|
use App\Support\CrawlerIdentifier;
|
|
use Illuminate\Support\Carbon;
|
|
|
|
/**
|
|
* Admin-Auswertung des Crawler-Abruf-Protokolls (Roadmap 1c.0): welche
|
|
* Such- und KI-Dienste in welchem Umfang Meldungen geholt haben. Zählt
|
|
* Abrufe, nicht Zitate — das Protokoll weiß nicht, was ein Dienst mit der
|
|
* Seite gemacht hat.
|
|
*/
|
|
class CrawlerHitReport
|
|
{
|
|
/**
|
|
* Je Dienst: Abrufe und betroffene Meldungen im Zeitraum, Abrufe seit
|
|
* Beginn des Protokolls, letzter Abruf. Sortiert nach Abrufen im Zeitraum.
|
|
*
|
|
* @return list<array{service: string, label: string, kind: string, fetches: int, releases: int, fetches_total: int, last_at: Carbon}>
|
|
*/
|
|
public function byService(int $days = 30): array
|
|
{
|
|
$since = now()->subDays($days);
|
|
|
|
$recent = PressReleaseCrawlerHit::query()
|
|
->where('created_at', '>=', $since)
|
|
->selectRaw('service, count(*) as fetches, count(distinct press_release_id) as releases')
|
|
->groupBy('service')
|
|
->toBase()
|
|
->get()
|
|
->keyBy('service');
|
|
|
|
return PressReleaseCrawlerHit::query()
|
|
->selectRaw('service, count(*) as fetches_total, max(created_at) as last_at')
|
|
->groupBy('service')
|
|
->toBase()
|
|
->get()
|
|
->map(fn (object $row): array => [
|
|
'service' => (string) $row->service,
|
|
'label' => CrawlerIdentifier::label((string) $row->service),
|
|
'kind' => CrawlerIdentifier::kind((string) $row->service),
|
|
'fetches' => (int) ($recent[$row->service]->fetches ?? 0),
|
|
'releases' => (int) ($recent[$row->service]->releases ?? 0),
|
|
'fetches_total' => (int) $row->fetches_total,
|
|
'last_at' => Carbon::parse((string) $row->last_at),
|
|
])
|
|
->sortByDesc(fn (array $row): array => [$row['fetches'], $row['fetches_total']])
|
|
->values()
|
|
->all();
|
|
}
|
|
|
|
/**
|
|
* Abrufe je Tag über die letzten N Tage, lückenlos, getrennt nach Suche
|
|
* und KI (Art aus config/crawlers.php).
|
|
*
|
|
* @return list<array{date: Carbon, search: int, ai: int}>
|
|
*/
|
|
public function daily(int $days = 14): array
|
|
{
|
|
$start = now()->startOfDay()->subDays($days - 1);
|
|
|
|
$counts = PressReleaseCrawlerHit::query()
|
|
->where('created_at', '>=', $start)
|
|
->selectRaw('date(created_at) as day, service, count(*) as fetches')
|
|
->groupBy('day', 'service')
|
|
->toBase()
|
|
->get()
|
|
->groupBy('day');
|
|
|
|
$series = [];
|
|
|
|
for ($i = 0; $i < $days; $i++) {
|
|
$day = $start->copy()->addDays($i);
|
|
$row = ['date' => $day, 'search' => 0, 'ai' => 0];
|
|
|
|
foreach ($counts->get($day->toDateString(), collect()) as $entry) {
|
|
$row[CrawlerIdentifier::kind((string) $entry->service) === 'search' ? 'search' : 'ai'] += (int) $entry->fetches;
|
|
}
|
|
|
|
$series[] = $row;
|
|
}
|
|
|
|
return $series;
|
|
}
|
|
|
|
/**
|
|
* Am häufigsten abgerufene Meldungen im Zeitraum.
|
|
*
|
|
* @return list<array{press_release_id: int, title: string, fetches: int, services: int}>
|
|
*/
|
|
public function topReleases(int $days = 30, int $limit = 15): array
|
|
{
|
|
return PressReleaseCrawlerHit::query()
|
|
->join('press_releases', 'press_releases.id', '=', 'press_release_crawler_hits.press_release_id')
|
|
->where('press_release_crawler_hits.created_at', '>=', now()->subDays($days))
|
|
->selectRaw('press_release_crawler_hits.press_release_id, press_releases.title, count(*) as fetches, count(distinct service) as services')
|
|
->groupBy('press_release_crawler_hits.press_release_id', 'press_releases.title')
|
|
->orderByDesc('fetches')
|
|
->limit($limit)
|
|
->toBase()
|
|
->get()
|
|
->map(fn (object $row): array => [
|
|
'press_release_id' => (int) $row->press_release_id,
|
|
'title' => (string) $row->title,
|
|
'fetches' => (int) $row->fetches,
|
|
'services' => (int) $row->services,
|
|
])
|
|
->all();
|
|
}
|
|
}
|