Files
hub/app/Jobs/CalculateBrandAnalysisMetrics.php
kelly aed1e62c65 style: fix Pint issues in brand analysis files
- single_quote fixes
- unary_operator_spaces fixes
- concat_space fixes
2025-12-10 18:36:36 -07:00

160 lines
5.0 KiB
PHP

<?php
namespace App\Jobs;
use App\Models\Brand;
use App\Models\Business;
use App\Services\Cannaiq\BrandAnalysisService;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;
/**
* Background job to pre-calculate Brand Analysis metrics.
*
* This job runs in the background to compute expensive engagement and sentiment
* metrics for brands, caching the results for 2 hours. This prevents N+1 queries
* and expensive aggregations from running on page load.
*
* Schedule: Every 2 hours via Horizon
* Queue: default (or 'analytics' if available)
*
* Key benefits:
* - Aggregates CRM message counts, response rates, and quote/order metrics in batch
* - Pre-computes buyer engagement scores
* - For CannaiQ-enabled businesses, also pre-computes sentiment scores
* - Uses existing BrandAnalysisService caching mechanism (2-hour TTL)
*/
class CalculateBrandAnalysisMetrics implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
/**
* The business to calculate metrics for (null = all seller businesses)
*/
public ?int $businessId;
/**
* The brand to calculate metrics for (null = all brands in business)
*/
public ?int $brandId;
/**
* Create a new job instance.
*/
public function __construct(?int $businessId = null, ?int $brandId = null)
{
$this->businessId = $businessId;
$this->brandId = $brandId;
}
/**
* Execute the job.
*/
public function handle(BrandAnalysisService $service): void
{
$startTime = microtime(true);
$processedCount = 0;
try {
if ($this->businessId && $this->brandId) {
// Single brand calculation
$this->calculateForBrand($service, $this->businessId, $this->brandId);
$processedCount = 1;
} elseif ($this->businessId) {
// All brands for a single business
$processedCount = $this->calculateForBusiness($service, $this->businessId);
} else {
// All seller businesses with active brands
$processedCount = $this->calculateForAllBusinesses($service);
}
$duration = round(microtime(true) - $startTime, 2);
Log::info('CalculateBrandAnalysisMetrics completed', [
'business_id' => $this->businessId ?? 'all',
'brand_id' => $this->brandId ?? 'all',
'brands_processed' => $processedCount,
'duration_seconds' => $duration,
]);
} catch (\Exception $e) {
Log::error('CalculateBrandAnalysisMetrics failed', [
'business_id' => $this->businessId,
'brand_id' => $this->brandId,
'error' => $e->getMessage(),
]);
throw $e;
}
}
/**
* Calculate metrics for all seller businesses
*/
private function calculateForAllBusinesses(BrandAnalysisService $service): int
{
$processedCount = 0;
Business::where('type', 'seller')
->where('status', 'approved')
->chunk(10, function ($businesses) use ($service, &$processedCount) {
foreach ($businesses as $business) {
$processedCount += $this->calculateForBusiness($service, $business->id);
}
});
return $processedCount;
}
/**
* Calculate metrics for all active brands in a business
*/
private function calculateForBusiness(BrandAnalysisService $service, int $businessId): int
{
$business = Business::find($businessId);
if (! $business) {
return 0;
}
$brands = Brand::where('business_id', $businessId)
->where('is_active', true)
->get();
foreach ($brands as $brand) {
$this->calculateForBrand($service, $businessId, $brand->id);
}
return $brands->count();
}
/**
* Calculate metrics for a single brand
*/
private function calculateForBrand(BrandAnalysisService $service, int $businessId, int $brandId): void
{
$business = Business::find($businessId);
$brand = Brand::find($brandId);
if (! $business || ! $brand) {
return;
}
// This triggers the full analysis calculation and caches it
// The BrandAnalysisService handles caching internally with 2-hour TTL
$service->refreshAnalysis($brand, $business);
}
/**
* The job failed to process.
*/
public function failed(\Throwable $exception): void
{
Log::error('CalculateBrandAnalysisMetrics job failed', [
'business_id' => $this->businessId,
'brand_id' => $this->brandId,
'exception' => $exception->getMessage(),
]);
}
}