command->info('Starting background media records import...'); $total_processed = 0; $total_skipped = 0; $total_failed = 0; // Process each CSV file foreach (self::CSV_FILES as $csv_file) { $this->command->info("\nProcessing {$csv_file}..."); $csv_path = database_path("seeders/data/areas/{$csv_file}"); if (! file_exists($csv_path)) { $this->command->warn("CSV file not found: {$csv_path}"); continue; } try { $location_data = $this->readCsv($csv_path); $list_type = pathinfo($csv_file, PATHINFO_FILENAME); $this->command->info('Found '.count($location_data)." locations in {$csv_file}"); // Process each location record foreach ($location_data as $index => $location_record) { $this->command->info('Processing '.($index + 1).'/'.count($location_data).": {$location_record['location']}"); try { // Check for duplicates if ($this->recordExists($list_type, $location_record['location'], $location_record['area'])) { $this->command->warn("Skipping existing: {$location_record['location']} in {$location_record['area']}"); $total_skipped++; continue; } // Create the record $result = $this->createRecord($list_type, $location_record); if ($result) { $total_processed++; if ($total_processed % 25 === 0) { $this->command->info("Processed {$total_processed} background media records..."); } } else { $total_failed++; } } catch (\Exception $e) { $total_failed++; $this->command->error("Failed to import: {$location_record['location']} - {$e->getMessage()}"); } } } catch (\Exception $e) { $this->command->error("Failed to process {$csv_file}: {$e->getMessage()}"); $total_failed++; } } // Summary $this->command->info("\nImport Summary:"); $this->command->info("Successfully imported: {$total_processed} background media records"); if ($total_skipped > 0) { $this->command->info("Skipped (existing): {$total_skipped} records"); } if ($total_failed > 0) { $this->command->error("Failed: {$total_failed} records"); } $this->command->info("All records created with status 'pending_media' - ready for media generation/upload"); } /** * Check if record already exists */ private function recordExists(string $list_type, string $location_name, string $area): bool { try { return BackgroundMedia::where('list_type', $list_type) ->where('location_name', $location_name) ->where('area', $area) ->exists(); } catch (\Exception $e) { $this->command->warn("Could not check duplicate for {$location_name}: {$e->getMessage()}"); return false; } } /** * Read CSV file and return data */ private function readCsv(string $csv_path): array { if (! file_exists($csv_path)) { throw new \RuntimeException("CSV file not found: {$csv_path}"); } $csv_content = file_get_contents($csv_path); $lines = str_getcsv($csv_content, "\n"); // Parse header row $headers = str_getcsv(array_shift($lines)); $location_data = []; foreach ($lines as $line) { if (empty(trim($line))) { continue; } $row = str_getcsv($line); if (count($row) === count($headers)) { $location_data[] = array_combine($headers, $row); } } return $location_data; } /** * Create a background media record */ private function createRecord(string $list_type, array $location_record): bool { try { // Validate area enum value $area = strtolower($location_record['area']); if (! in_array($area, ['interior', 'exterior'])) { $this->command->warn("Invalid area value '{$area}' for {$location_record['location']}, skipping..."); return false; } // Generate embedding based on location data try { $embedding_text = $this->buildEmbeddingText($list_type, $location_record); $embedding = CloudflareAI::getVectorEmbeddingBgeSmall($embedding_text); } catch (\Exception $e) { $this->command->warn("Failed to generate embedding for {$location_record['location']}: {$e->getMessage()}"); $embedding = null; // Continue without embedding } // Check if record exists one more time $existing_media = BackgroundMedia::where('list_type', $list_type) ->where('location_name', $location_record['location']) ->where('area', $area) ->first(); if ($existing_media) { $this->command->warn("Record already exists for {$location_record['location']}, skipping..."); return false; } // Create BackgroundMedia record with pending_media status BackgroundMedia::create([ 'list_type' => $list_type, 'area' => $area, 'location_name' => $location_record['location'], 'status' => 'pending_media', // Default status - ready for media generation 'media_uuid' => null, // Will be set when media is generated/uploaded 'media_url' => null, // Will be set when media is generated/uploaded 'embedding' => $embedding, ]); $this->command->info("Created: {$location_record['location']} ({$list_type}, {$area})"); return true; } catch (\Exception $e) { $this->command->error("Error importing {$location_record['location']}: {$e->getMessage()}"); throw $e; } } /** * Build text for embedding */ private function buildEmbeddingText(string $list_type, array $location_record): string { $text_parts = [ $list_type, $location_record['location'], $location_record['area'], $location_record['type'] ?? '', ]; // Add geographic information if available if (isset($location_record['city'])) { $text_parts[] = $location_record['city']; } if (isset($location_record['state'])) { $text_parts[] = $location_record['state']; } if (isset($location_record['country'])) { $text_parts[] = $location_record['country']; } if (isset($location_record['continent'])) { $text_parts[] = $location_record['continent']; } // Clean and join $text_parts = array_filter($text_parts, function ($part) { return ! empty(trim($part)); }); return implode(' ', $text_parts); } }