exec( "CREATE TABLE IF NOT EXISTS `meteorshowers` ( `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'Eindeutige interne ID des Meteorstroms', `shower_code` varchar(16) NOT NULL COMMENT 'Kurzer IAU- oder Projektcode des Meteorstroms, z. B. PER oder GEM', `iau_no` smallint(5) unsigned DEFAULT NULL COMMENT 'Offizielle IAU-Nummer des Meteorstroms, falls vorhanden', `designation` varchar(255) NOT NULL COMMENT 'Vollstaendige Bezeichnung des Meteorstroms', `speed_kms` decimal(6,2) DEFAULT NULL COMMENT 'Geozentrische Eintrittsgeschwindigkeit der Meteore in km/s', `radiant_alpha_deg` decimal(7,3) DEFAULT NULL COMMENT 'Rektaszension des Radianten in Grad', `radiant_delta_deg` decimal(7,3) DEFAULT NULL COMMENT 'Deklination des Radianten in Grad', `drift_alpha_deg` decimal(7,3) DEFAULT NULL COMMENT 'Taegliche Aenderung der Radianten-Rektaszension in Grad', `drift_delta_deg` decimal(7,3) DEFAULT NULL COMMENT 'Taegliche Aenderung der Radianten-Deklination in Grad', `parent_object` varchar(255) DEFAULT NULL COMMENT 'Vermuteter oder bekannter Mutterkoerper, z. B. ein Komet oder Asteroid', `population_index` decimal(4,2) DEFAULT NULL COMMENT 'Populationsindex r als Mass fuer die Helligkeitsverteilung der Meteore', `active_start_sol` decimal(6,2) DEFAULT NULL COMMENT 'Beginn der Aktivitaet in Sonnenlaenge (Grad, 0-360)', `active_finish_sol` decimal(6,2) DEFAULT NULL COMMENT 'Ende der Aktivitaet in Sonnenlaenge (Grad, 0-360)', `peak_sol` decimal(6,2) DEFAULT NULL COMMENT 'Maximum des Meteorstroms in Sonnenlaenge (Grad, 0-360)', `zhr` int(11) DEFAULT NULL COMMENT 'Zenithal Hourly Rate des generischen Aktivitaetseintrags', `zhr_variable` varchar(32) DEFAULT NULL COMMENT 'Textuelle variable ZHR-Angabe, z. B. 5-20 oder 20-700', `disttype` tinyint(3) unsigned DEFAULT NULL COMMENT 'Typ der Aktivitaetsverteilung aus der Quelldatei', `b1` decimal(8,4) DEFAULT NULL COMMENT 'Parameter b1 fuer das Aktivitaetsprofil aus der Quelldatei', `b2` decimal(8,4) DEFAULT NULL COMMENT 'Parameter b2 fuer das Aktivitaetsprofil aus der Quelldatei', `activity_json` json DEFAULT NULL COMMENT 'Komplettes activity-Array aus der JSON-Datei mit generischen und jahresspezifischen Aktivitaetsdaten', `colors_json` json DEFAULT NULL COMMENT 'Optionale Farbverteilung der Meteore aus der JSON-Datei', `source_entry_json` json DEFAULT NULL COMMENT 'Originaler kompletter JSON-Eintrag des Meteorstroms zur Nachvollziehbarkeit', `has_yearly_variants` tinyint(1) NOT NULL DEFAULT 0 COMMENT '1, wenn neben generic auch jahresspezifische Aktivitaetseintraege vorhanden sind', `dataset_version` int(10) unsigned DEFAULT NULL COMMENT 'Versionsnummer des importierten Quelldatensatzes', `updated_at` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp() COMMENT 'Zeitpunkt der letzten Aktualisierung des Datensatzes', `created_at` timestamp NOT NULL DEFAULT current_timestamp() COMMENT 'Zeitpunkt der Erstellung des Datensatzes', PRIMARY KEY (`id`), UNIQUE KEY `uq_meteorshowers_code` (`shower_code`), UNIQUE KEY `uq_meteorshowers_iau_no` (`iau_no`), KEY `idx_meteorshowers_designation` (`designation`), KEY `idx_meteorshowers_active_start` (`active_start_sol`), KEY `idx_meteorshowers_active_finish` (`active_finish_sol`), KEY `idx_meteorshowers_peak` (`peak_sol`), KEY `idx_meteorshowers_zhr` (`zhr`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Meteorstroeme mit Stammdaten sowie generischen und historischen Aktivitaetsinformationen'" ); $requiredColumns = [ 'shower_code', 'iau_no', 'designation', 'speed_kms', 'radiant_alpha_deg', 'radiant_delta_deg', 'drift_alpha_deg', 'drift_delta_deg', 'parent_object', 'population_index', 'active_start_sol', 'active_finish_sol', 'peak_sol', 'zhr', 'zhr_variable', 'disttype', 'b1', 'b2', 'activity_json', 'colors_json', 'source_entry_json', 'has_yearly_variants', 'dataset_version', ]; $stmt = $pdo->query('SHOW COLUMNS FROM `meteorshowers`'); $existingColumns = $stmt->fetchAll(PDO::FETCH_COLUMN); $missingColumns = array_values(array_diff($requiredColumns, $existingColumns)); if ($missingColumns !== []) { throw new RuntimeException( 'Tabelle `meteorshowers` hat nicht das erwartete Schema. Fehlende Spalten: ' . implode(', ', $missingColumns) ); } } function findGenericActivity(array $activity): ?array { foreach ($activity as $entry) { if (!is_array($entry)) { continue; } if (strtolower(trim((string)($entry['year'] ?? ''))) === 'generic') { return $entry; } } return null; } function hasYearlyVariants(array $activity): bool { foreach ($activity as $entry) { if (!is_array($entry)) { continue; } $year = strtolower(trim((string)($entry['year'] ?? ''))); if ($year !== '' && $year !== 'generic') { return true; } } return false; } function toNullableString(mixed $value): ?string { if ($value === null) { return null; } $value = trim((string)$value); return $value === '' ? null : $value; } $defaultJsonPath = realpath(__DIR__ . '/MeteorShowers.json') ?: (__DIR__ . '/MeteorShowers.json'); $selectedJsonPath = (string)$defaultJsonPath; $report = null; $errorMessage = null; if ($canUseImportPage && $_SERVER['REQUEST_METHOD'] === 'POST') { $jsonPath = trim((string)($_POST['json_path'] ?? '')); if ($jsonPath === '') { $jsonPath = (string)$defaultJsonPath; } $selectedJsonPath = $jsonPath; try { if (!is_file($jsonPath)) { throw new RuntimeException('JSON-Datei nicht gefunden: ' . $jsonPath); } $config = require __DIR__ . '/../../config/database.php'; $dsn = sprintf( 'mysql:host=%s;dbname=%s;charset=%s', $config['host'], $config['dbname'], $config['charset'] ?? 'utf8mb4' ); $pdo = new PDO( $dsn, $config['user'], $config['pass'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ] ); ensureMeteorShowersTable($pdo); $json = file_get_contents($jsonPath); if ($json === false) { throw new RuntimeException('JSON-Datei konnte nicht gelesen werden.'); } $data = json_decode($json, true, 512, JSON_THROW_ON_ERROR); $showers = $data['showers'] ?? null; if (!is_array($showers)) { throw new RuntimeException('Ungueltige JSON-Struktur: "showers" fehlt oder ist kein Objekt.'); } $datasetName = toNullableString($data['shortName'] ?? null); $datasetVersion = toNullableInt($data['version'] ?? null); $selectStmt = $pdo->prepare('SELECT `id` FROM `meteorshowers` WHERE `shower_code` = :shower_code LIMIT 1'); $upsertStmt = $pdo->prepare( "INSERT INTO `meteorshowers` ( `shower_code`, `designation`, `iau_no`, `speed_kms`, `radiant_alpha_deg`, `radiant_delta_deg`, `drift_alpha_deg`, `drift_delta_deg`, `parent_object`, `population_index`, `active_start_sol`, `active_finish_sol`, `peak_sol`, `zhr`, `zhr_variable`, `disttype`, `b1`, `b2`, `activity_json`, `colors_json`, `source_entry_json`, `has_yearly_variants`, `dataset_version` ) VALUES ( :shower_code, :designation, :iau_no, :speed_kms, :radiant_alpha_deg, :radiant_delta_deg, :drift_alpha_deg, :drift_delta_deg, :parent_object, :population_index, :active_start_sol, :active_finish_sol, :peak_sol, :zhr, :zhr_variable, :disttype, :b1, :b2, :activity_json, :colors_json, :source_entry_json, :has_yearly_variants, :dataset_version ) ON DUPLICATE KEY UPDATE `designation` = VALUES(`designation`), `iau_no` = VALUES(`iau_no`), `speed_kms` = VALUES(`speed_kms`), `radiant_alpha_deg` = VALUES(`radiant_alpha_deg`), `radiant_delta_deg` = VALUES(`radiant_delta_deg`), `drift_alpha_deg` = VALUES(`drift_alpha_deg`), `drift_delta_deg` = VALUES(`drift_delta_deg`), `parent_object` = VALUES(`parent_object`), `population_index` = VALUES(`population_index`), `active_start_sol` = VALUES(`active_start_sol`), `active_finish_sol` = VALUES(`active_finish_sol`), `peak_sol` = VALUES(`peak_sol`), `zhr` = VALUES(`zhr`), `zhr_variable` = VALUES(`zhr_variable`), `disttype` = VALUES(`disttype`), `b1` = VALUES(`b1`), `b2` = VALUES(`b2`), `activity_json` = VALUES(`activity_json`), `colors_json` = VALUES(`colors_json`), `source_entry_json` = VALUES(`source_entry_json`), `has_yearly_variants` = VALUES(`has_yearly_variants`), `dataset_version` = VALUES(`dataset_version`), `updated_at` = CURRENT_TIMESTAMP()" ); $processed = 0; $inserted = 0; $updated = 0; $pdo->beginTransaction(); foreach ($showers as $code => $entry) { if (!is_array($entry)) { continue; } $showerCode = strtoupper(trim((string)$code)); if ($showerCode === '') { continue; } $selectStmt->execute([':shower_code' => $showerCode]); $exists = $selectStmt->fetchColumn() !== false; $designation = trim((string)($entry['designation'] ?? $showerCode)); if ($designation === '') { $designation = $showerCode; } $activity = isset($entry['activity']) && is_array($entry['activity']) ? $entry['activity'] : []; $genericActivity = findGenericActivity($activity); $colors = isset($entry['colors']) && is_array($entry['colors']) ? $entry['colors'] : []; $upsertStmt->execute([ ':shower_code' => $showerCode, ':designation' => $designation, ':iau_no' => toNullableInt($entry['IAUNo'] ?? null), ':speed_kms' => toNullableFloat($entry['speed'] ?? null), ':radiant_alpha_deg' => toNullableFloat($entry['radiantAlpha'] ?? null), ':radiant_delta_deg' => toNullableFloat($entry['radiantDelta'] ?? null), ':drift_alpha_deg' => toNullableFloat($entry['driftAlpha'] ?? null), ':drift_delta_deg' => toNullableFloat($entry['driftDelta'] ?? null), ':parent_object' => toNullableString($entry['parentObj'] ?? null), ':population_index' => toNullableFloat($entry['pidx'] ?? null), ':active_start_sol' => toNullableFloat($genericActivity['start'] ?? null), ':active_finish_sol' => toNullableFloat($genericActivity['finish'] ?? null), ':peak_sol' => toNullableFloat($genericActivity['peak'] ?? null), ':zhr' => toNullableInt($genericActivity['zhr'] ?? null), ':zhr_variable' => toNullableString($genericActivity['variable'] ?? null), ':disttype' => toNullableInt($genericActivity['disttype'] ?? null), ':b1' => toNullableFloat($genericActivity['b1'] ?? null), ':b2' => toNullableFloat($genericActivity['b2'] ?? null), ':activity_json' => json_encode($activity, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ':colors_json' => $colors !== [] ? json_encode($colors, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) : null, ':source_entry_json' => json_encode($entry, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), ':has_yearly_variants' => hasYearlyVariants($activity) ? 1 : 0, ':dataset_version' => $datasetVersion, ]); $processed++; if ($exists) { $updated++; } else { $inserted++; } } $pdo->commit(); $tableCount = (int)$pdo->query('SELECT COUNT(*) FROM `meteorshowers`')->fetchColumn(); $report = [ 'json_path' => $jsonPath, 'dataset_name' => $datasetName, 'dataset_version' => $datasetVersion, 'processed' => $processed, 'inserted' => $inserted, 'updated' => $updated, 'table_count' => $tableCount, ]; } catch (Throwable $e) { if (isset($pdo) && $pdo instanceof PDO && $pdo->inTransaction()) { $pdo->rollBack(); } $errorMessage = $e->getMessage(); } } ?>
Importiert MeteorShowers.json in die Tabelle meteorshowers.
Die Tabelle wird bei Bedarf automatisch angelegt.