optimizations

This commit is contained in:
2026-03-28 19:15:39 +01:00
parent 0b25d9570a
commit cab4fbd83e
509 changed files with 1016804 additions and 1605 deletions

View File

@@ -0,0 +1,111 @@
<?php
declare(strict_types=1);
use App\Models\Artwork;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
uses(RefreshDatabase::class);
it('admin discovery feedback report includes negative feedback and undo metrics', function () {
$admin = User::factory()->create(['role' => 'admin']);
$user = User::factory()->create();
$artwork = Artwork::factory()->create();
$previousDate = now()->subDay()->toDateString();
$date = now()->toDateString();
$recordEvent = function (string $eventDate, string $eventType, array $meta = []) use ($user, $artwork) {
$algoVersion = (string) ($meta['algo_version'] ?? 'clip-cosine-v2-adaptive');
unset($meta['algo_version']);
DB::table('user_discovery_events')->insert([
'event_id' => (string) Str::uuid(),
'user_id' => $user->id,
'artwork_id' => $artwork->id,
'category_id' => null,
'event_type' => $eventType,
'event_version' => 'event-v1',
'algo_version' => $algoVersion,
'weight' => 1.0,
'event_date' => $eventDate,
'occurred_at' => now(),
'meta' => json_encode(array_merge([
'gallery_type' => 'for-you',
'surface' => 'for-you',
], $meta), JSON_THROW_ON_ERROR),
'created_at' => now(),
'updated_at' => now(),
]);
};
$recordEvent($previousDate, 'view');
$recordEvent($previousDate, 'click');
$recordEvent($previousDate, 'favorite');
$recordEvent($previousDate, 'hide_artwork', ['reason' => 'not_relevant']);
$recordEvent($previousDate, 'dislike_tag', ['tag_slug' => 'abstract']);
$recordEvent($previousDate, 'view', ['gallery_type' => 'homepage', 'surface' => 'homepage', 'algo_version' => 'clip-cosine-v1']);
$recordEvent($previousDate, 'click', ['gallery_type' => 'homepage', 'surface' => 'homepage', 'algo_version' => 'clip-cosine-v1']);
$recordEvent($previousDate, 'favorite', ['gallery_type' => 'homepage', 'surface' => 'homepage', 'algo_version' => 'clip-cosine-v1']);
$recordEvent($date, 'view');
$recordEvent($date, 'click');
$recordEvent($date, 'favorite');
$recordEvent($date, 'download');
$recordEvent($date, 'hide_artwork', ['reason' => 'not_relevant']);
$recordEvent($date, 'unhide_artwork', ['reason' => 'undo']);
$recordEvent($date, 'view', ['gallery_type' => 'homepage', 'surface' => 'homepage', 'algo_version' => 'clip-cosine-v1']);
$recordEvent($date, 'click', ['gallery_type' => 'homepage', 'surface' => 'homepage', 'algo_version' => 'clip-cosine-v1']);
$recordEvent($date, 'hide_artwork', ['gallery_type' => 'homepage', 'surface' => 'homepage', 'algo_version' => 'clip-cosine-v1', 'reason' => 'not_relevant']);
$recordEvent($date, 'dislike_tag', ['gallery_type' => 'homepage', 'surface' => 'homepage', 'algo_version' => 'clip-cosine-v1', 'tag_slug' => 'portrait']);
$this->artisan('analytics:aggregate-discovery-feedback', ['--date' => $previousDate])
->assertExitCode(0);
$this->artisan('analytics:aggregate-discovery-feedback', ['--date' => $date])
->assertExitCode(0);
$response = $this->actingAs($admin)
->getJson('/api/admin/reports/discovery-feedback?from=' . $previousDate . '&to=' . $date . '&limit=10');
$response->assertOk();
$response->assertJsonPath('overview.views', 4);
$response->assertJsonPath('overview.clicks', 4);
$response->assertJsonPath('overview.feedback_actions', 4);
$response->assertJsonPath('overview.hidden_artworks', 3);
$response->assertJsonPath('overview.disliked_tags', 2);
$response->assertJsonPath('overview.negative_feedback_actions', 5);
$response->assertJsonPath('overview.undo_hidden_artworks', 1);
$response->assertJsonPath('overview.undo_disliked_tags', 0);
$response->assertJsonPath('overview.undo_actions', 1);
$response->assertJsonPath('daily_feedback.0.negative_feedback_actions', 2);
$response->assertJsonPath('daily_feedback.1.negative_feedback_actions', 3);
$response->assertJsonPath('daily_feedback.1.undo_actions', 1);
$response->assertJsonPath('trend_summary.latest_day.date', $date);
$response->assertJsonPath('trend_summary.previous_day.date', $previousDate);
$response->assertJsonPath('trend_summary.rolling_7d_average.feedback_actions', 2);
$response->assertJsonPath('trend_summary.rolling_7d_average.negative_feedback_actions', 2.5);
$response->assertJsonPath('trend_summary.rolling_7d_average.undo_actions', 0.5);
$response->assertJsonPath('trend_summary.deltas.feedback_actions.label', 'Flat');
$response->assertJsonPath('trend_summary.deltas.negative_feedback_actions.label', 'Worse +1 vs prev day');
$response->assertJsonPath('trend_summary.overall_status.level', 'watch');
$response->assertJsonPath('by_surface.0.surface', 'homepage');
$response->assertJsonPath('by_surface.0.negative_feedback_actions', 2);
$response->assertJsonPath('by_surface.0.trend.overall_status.level', 'risk');
$response->assertJsonPath('by_surface.0.trend.deltas.feedback_actions.label', 'Worse -1 vs prev day');
$response->assertJsonPath('by_surface.0.trend.deltas.negative_feedback_actions.label', 'Worse +2 vs prev day');
$response->assertJsonPath('by_surface.1.surface', 'for-you');
$response->assertJsonPath('by_surface.1.trend.overall_status.level', 'healthy');
$response->assertJsonPath('by_algo_surface.0.algo_version', 'clip-cosine-v1');
$response->assertJsonPath('by_algo_surface.0.surface', 'homepage');
$response->assertJsonPath('by_algo_surface.0.negative_feedback_actions', 2);
$response->assertJsonPath('by_algo_surface.0.trend.overall_status.level', 'risk');
$response->assertJsonPath('by_algo_surface.0.trend.deltas.feedback_actions.label', 'Worse -1 vs prev day');
$response->assertJsonPath('by_algo_surface.0.trend.deltas.negative_feedback_actions.label', 'Worse +2 vs prev day');
$response->assertJsonPath('by_algo_surface.1.algo_version', 'clip-cosine-v2-adaptive');
$response->assertJsonPath('top_artworks.0.artwork_id', $artwork->id);
$response->assertJsonPath('top_artworks.0.negative_feedback_actions', 3);
$response->assertJsonPath('top_artworks.0.undo_actions', 1);
$response->assertJsonPath('latest_aggregated_date', $date);
});