Files
SkinbaseNova/app/Services/EarlyGrowth/AdaptiveTimeWindow.php

79 lines
2.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Services\EarlyGrowth;
use App\Models\Artwork;
use Illuminate\Support\Facades\Cache;
/**
* AdaptiveTimeWindow
*
* Dynamically widens the look-back window used by trending / rising feeds
* when recent upload volume is below configured thresholds.
*
* This only affects RANKING QUERIES — it never modifies artwork timestamps,
* canonical URLs, or any stored data.
*
* Behaviour:
* uploads/day ≥ narrow_threshold → normal window (7 d)
* uploads/day ≥ wide_threshold → medium window (30 d)
* uploads/day < wide_threshold → wide window (90 d)
*
* All thresholds and window sizes are configurable in config/early_growth.php.
*/
final class AdaptiveTimeWindow
{
/**
* Return the number of look-back days to use for trending / rising queries.
*
* @param int $defaultDays Returned as-is when EGS is disabled.
*/
public function getTrendingWindowDays(int $defaultDays = 30): int
{
if (! EarlyGrowth::adaptiveWindowEnabled()) {
return $defaultDays;
}
$uploadsPerDay = $this->getUploadsPerDay();
$narrowThreshold = (int) config('early_growth.thresholds.uploads_per_day_narrow', 10);
$wideThreshold = (int) config('early_growth.thresholds.uploads_per_day_wide', 3);
$narrowDays = (int) config('early_growth.thresholds.window_narrow_days', 7);
$mediumDays = (int) config('early_growth.thresholds.window_medium_days', 30);
$wideDays = (int) config('early_growth.thresholds.window_wide_days', 90);
if ($uploadsPerDay >= $narrowThreshold) {
return $narrowDays; // Healthy activity → normal 7-day window
}
if ($uploadsPerDay >= $wideThreshold) {
return $mediumDays; // Moderate activity → expand to 30 days
}
return $wideDays; // Low activity → expand to 90 days
}
/**
* Rolling 7-day average of approved public uploads per day.
* Cached for `early_growth.cache_ttl.time_window` seconds.
*/
public function getUploadsPerDay(): float
{
$ttl = (int) config('early_growth.cache_ttl.time_window', 600);
return Cache::remember('egs.uploads_per_day', $ttl, function (): float {
$count = Artwork::query()
->where('is_public', true)
->where('is_approved', true)
->whereNull('deleted_at')
->whereNotNull('published_at')
->where('published_at', '>=', now()->subDays(7))
->count();
return round($count / 7, 2);
});
}
}