From 8e11892f57acb3ea2ccdedd873ea77e9e55a8a9a Mon Sep 17 00:00:00 2001 From: Refringe Date: Tue, 21 May 2024 21:02:49 -0400 Subject: [PATCH] Early Mod Import --- app/Console/Commands/ImportWoltlabData.php | 141 +++++++++++++++++- app/View/Components/ModListSection.php | 12 +- database/factories/ModFactory.php | 5 +- .../0001_01_01_000000_create_users_table.php | 1 + ...024_05_15_022315_create_licenses_table.php | 1 + .../2024_05_15_022710_create_mods_table.php | 8 +- ...05_15_023430_create_spt_versions_table.php | 1 + ...05_15_023705_create_mod_versions_table.php | 1 + 8 files changed, 157 insertions(+), 13 deletions(-) diff --git a/app/Console/Commands/ImportWoltlabData.php b/app/Console/Commands/ImportWoltlabData.php index a94fabf..d5a8530 100644 --- a/app/Console/Commands/ImportWoltlabData.php +++ b/app/Console/Commands/ImportWoltlabData.php @@ -2,11 +2,15 @@ namespace App\Console\Commands; +use App\Models\License; +use App\Models\Mod; use App\Models\User; use Carbon\Carbon; use Illuminate\Console\Command; use Illuminate\Support\Collection; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Storage; +use Illuminate\Support\Str; class ImportWoltlabData extends Command { @@ -30,6 +34,8 @@ class ImportWoltlabData extends Command public function handle(): void { $this->importUsers(); + $this->importLicenses(); + $this->importMods(); $this->info('Data imported successfully.'); } @@ -47,6 +53,7 @@ class ImportWoltlabData extends Command $registrationDate->setTimezone('UTC'); $insertData[] = [ + 'hub_id' => $wolt->userID, 'name' => $wolt->username, 'email' => mb_convert_case($wolt->email, MB_CASE_LOWER, 'UTF-8'), 'password' => $wolt->password, @@ -56,15 +63,143 @@ class ImportWoltlabData extends Command } if (!empty($insertData)) { - User::insert($insertData); + User::upsert($insertData, ['hub_id'], ['name', 'email', 'password', 'created_at', 'updated_at']); $totalInserted += count($insertData); - $this->info('Inserted ' . count($insertData) . ' users. Total inserted so far: ' . $totalInserted); + $this->line('Processed ' . count($insertData) . ' users. Total processed so far: ' . $totalInserted); } unset($insertData); unset($users); }, 'userID'); - $this->info('Total users inserted: ' . $totalInserted); + $this->info('Total users processed: ' . $totalInserted); + $this->newLine(); + } + + protected function importLicenses(): void + { + $totalInserted = 0; + + DB::connection('mysql_woltlab')->table('filebase1_license')->chunkById(100, function (Collection $licenses) use (&$totalInserted) { + $insertData = []; + foreach ($licenses as $license) { + $insertData[] = [ + 'hub_id' => $license->licenseID, + 'name' => $license->licenseName, + 'link' => $license->licenseURL, + ]; + } + + if (!empty($insertData)) { + DB::table('licenses')->upsert($insertData, ['hub_id'], ['name', 'link']); + $totalInserted += count($insertData); + $this->line('Processed ' . count($insertData) . ' licenses. Total processed so far: ' . $totalInserted); + } + + unset($insertData); + unset($licenses); + }, 'licenseID'); + + $this->info('Total licenses processed: ' . $totalInserted); + $this->newLine(); + } + + protected function importMods(): void + { + $command = $this; + $totalInserted = 0; + + DB::connection('mysql_woltlab')->table('filebase1_file')->chunkById(5, function (Collection $mods) use (&$command, &$totalInserted) { + + foreach ($mods as $mod) + { + $modContent = DB::connection('mysql_woltlab')->table('filebase1_file_content')->where('fileID', $mod->fileID)->first(); + + $insertData[] = [ + 'hub_id' => $mod->fileID, + 'user_id' => User::whereName($mod->username)->value('id'), + 'name' => $modContent->subject, + 'slug' => Str::slug($modContent->subject), + 'teaser' => $modContent->teaser, + 'description' => $modContent->message, + 'thumbnail' => $this->fetchModThumbnail($command, $mod->fileID, $mod->iconHash, $mod->iconExtension), + 'license_id' => License::whereHubId($mod->licenseID)->value('id'), + 'source_code_link' => $this->fetchSourceLinkValue($mod->fileID), + 'featured' => $mod->isFeatured, + 'contains_ai_content' => $this->fetchContainsAiContentValue($mod->fileID), + 'disabled' => $mod->isDisabled, + 'created_at' => Carbon::parse($mod->time, 'UTC'), + 'updated_at' => Carbon::parse($mod->lastChangeTime, 'UTC'), + ]; + } + + if (!empty($insertData)) { + Mod::upsert($insertData, ['hub_id'], ['user_id', 'name', 'slug', 'teaser', 'description', 'thumbnail', 'license_id', 'source_code_link', 'featured', 'contains_ai_content', 'disabled', 'created_at', 'updated_at']); + $totalInserted += count($insertData); + $command->line('Processed ' . count($insertData) . ' mods. Total processed so far: ' . $totalInserted); + } + + unset($insertData); + unset($mods); + }, 'fileID'); + + $this->info('Total mods processed: ' . $totalInserted); + $this->newLine(); + } + + protected function fetchSourceLinkValue(string $fileID): string + { + $options = DB::connection('mysql_woltlab')->table('filebase1_file_option_value')->where('fileID', $fileID)->get(); + + // Iterate over the options and find the 'optionID' of 5 or 1. That record will contain the source code link in + // the 'optionValue' column. The 'optionID' of 5 should take precedence over 1. If neither are found, return an + // empty string. + foreach ($options as $option) { + if ($option->optionID == 5 && !empty($option->optionValue)) { + return $option->optionValue; + } + if ($option->optionID == 1 && !empty($option->optionValue)) { + return $option->optionValue; + } + } + return ''; + } + + protected function fetchContainsAiContentValue(string $fileID): bool + { + $options = DB::connection('mysql_woltlab')->table('filebase1_file_option_value')->where('fileID', $fileID)->get(); + + // Iterate over the options and find the 'optionID' of 7. That record will contain the AI flag. + foreach ($options as $option) { + if ($option->optionID == 7) { + return (bool) $option->optionValue; + } + } + return ''; + } + + protected function fetchModThumbnail($command, string $fileID, string $thumbnailHash, string $thumbnailExtension): string + { + if (empty($fileID) || empty($thumbnailHash) || empty($thumbnailExtension)) { + return ''; + } + + // Only the first two characters of the icon hash. + $hashShort = substr($thumbnailHash, 0, 2); + + $hubUrl = "https://hub.sp-tarkov.com/files/images/file/$hashShort/$fileID.$thumbnailExtension"; + $localPath = "mods/$thumbnailHash.$thumbnailExtension"; + + // Check to make sure the image doesn't already exist. + if (Storage::disk('public')->exists($localPath)) { + return "/storage/$localPath"; + } + + $command->output->write("Downloading mod thumbnail: $hubUrl... "); + Storage::disk('public')->put($localPath, file_get_contents($hubUrl)); + $command->info('Done.'); + + // Return the path to the saved thumbnail. + return "/storage/$localPath"; } } diff --git a/app/View/Components/ModListSection.php b/app/View/Components/ModListSection.php index fb614d7..5f22b67 100644 --- a/app/View/Components/ModListSection.php +++ b/app/View/Components/ModListSection.php @@ -9,20 +9,20 @@ use Illuminate\View\Component; class ModListSection extends Component { - public Collection $modsSuggested; + public Collection $modsFeatured; public Collection $modsLatest; public Collection $modsUpdated; public function __construct() { - $this->modsSuggested = $this->fetchSuggestedMods(); + $this->modsFeatured = $this->fetchFeaturedMods(); $this->modsLatest = $this->fetchLatestMods(); $this->modsUpdated = $this->fetchUpdatedMods(); } - private function fetchSuggestedMods(): Collection + private function fetchFeaturedMods(): Collection { - return Mod::with('versionLatestSptVersion.sptVersion')->whereSuggested(true)->take(6)->get(); + return Mod::with('versionLatestSptVersion.sptVersion')->whereFeatured(true)->take(6)->get(); } private function fetchLatestMods(): Collection @@ -39,8 +39,8 @@ class ModListSection extends Component { return [ [ - 'title' => 'Suggested Mods', - 'mods' => $this->modsSuggested, + 'title' => 'Featured Mods', + 'mods' => $this->modsFeatured, 'versionScope' => 'versionLatestSptVersion' ], [ diff --git a/database/factories/ModFactory.php b/database/factories/ModFactory.php index 3322487..44d3a56 100644 --- a/database/factories/ModFactory.php +++ b/database/factories/ModFactory.php @@ -20,10 +20,11 @@ class ModFactory extends Factory 'user_id' => User::factory(), 'name' => $name, 'slug' => Str::slug($name), - 'description' => $this->faker->paragraph, + 'teaser' => $this->faker->sentence, + 'description' => $this->faker->sentences(6, true), 'license_id' => License::factory(), 'source_code_link' => $this->faker->url(), - 'suggested' => $this->faker->boolean, + 'featured' => $this->faker->boolean, 'contains_ai_content' => $this->faker->boolean, 'created_at' => now(), 'updated_at' => now(), diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 31d7807..84d5fb5 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -13,6 +13,7 @@ return new class extends Migration { Schema::create('users', function (Blueprint $table) { $table->id(); + $table->string('hub_id')->nullable()->unique(); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); diff --git a/database/migrations/2024_05_15_022315_create_licenses_table.php b/database/migrations/2024_05_15_022315_create_licenses_table.php index 4e7ff7d..fbfcdce 100644 --- a/database/migrations/2024_05_15_022315_create_licenses_table.php +++ b/database/migrations/2024_05_15_022315_create_licenses_table.php @@ -10,6 +10,7 @@ return new class extends Migration { Schema::create('licenses', function (Blueprint $table) { $table->id(); + $table->string('hub_id')->nullable()->unique(); $table->string('name'); $table->string('link'); $table->softDeletes(); diff --git a/database/migrations/2024_05_15_022710_create_mods_table.php b/database/migrations/2024_05_15_022710_create_mods_table.php index 66a21f5..a611a73 100644 --- a/database/migrations/2024_05_15_022710_create_mods_table.php +++ b/database/migrations/2024_05_15_022710_create_mods_table.php @@ -12,14 +12,18 @@ return new class extends Migration { Schema::create('mods', function (Blueprint $table) { $table->id(); + $table->string('hub_id')->nullable()->unique(); $table->foreignIdFor(User::class)->constrained('users'); $table->string('name'); $table->string('slug'); + $table->string('teaser'); $table->longText('description'); - $table->foreignIdFor(License::class)->constrained('licenses'); + $table->string('thumbnail')->default(''); + $table->foreignIdFor(License::class)->nullable()->default(null)->constrained('licenses'); $table->string('source_code_link'); - $table->boolean('suggested')->default(false); + $table->boolean('featured')->default(false); $table->boolean('contains_ai_content')->default(false); + $table->boolean('disabled')->default(false); $table->softDeletes(); $table->timestamps(); }); diff --git a/database/migrations/2024_05_15_023430_create_spt_versions_table.php b/database/migrations/2024_05_15_023430_create_spt_versions_table.php index a47d369..c9d3f17 100644 --- a/database/migrations/2024_05_15_023430_create_spt_versions_table.php +++ b/database/migrations/2024_05_15_023430_create_spt_versions_table.php @@ -10,6 +10,7 @@ return new class extends Migration { Schema::create('spt_versions', function (Blueprint $table) { $table->id(); + $table->string('hub_id')->nullable()->unique(); $table->string('version'); $table->string('color_class'); $table->softDeletes(); diff --git a/database/migrations/2024_05_15_023705_create_mod_versions_table.php b/database/migrations/2024_05_15_023705_create_mod_versions_table.php index a5de806..cfb840c 100644 --- a/database/migrations/2024_05_15_023705_create_mod_versions_table.php +++ b/database/migrations/2024_05_15_023705_create_mod_versions_table.php @@ -12,6 +12,7 @@ return new class extends Migration { Schema::create('mod_versions', function (Blueprint $table) { $table->id(); + $table->string('hub_id')->nullable()->unique(); $table->foreignIdFor(Mod::class)->constrained('mods'); $table->string('version'); $table->longText('description');