Files
skyview.astronomiemuseum.de/scripts/import/import_meteorshowers.php
T
2026-03-31 20:36:15 +02:00

307 lines
12 KiB
PHP

<php
declare(strict_types=1);
session_start();
$loggedIn = isset($_SESSION['user_id']);
$isAdmin = $loggedIn && (($_SESSION['role'] ?? '') === 'admin');
$remoteAddr = (string)($_SERVER['REMOTE_ADDR'] ?? '');
$serverName = strtolower((string)($_SERVER['SERVER_NAME'] ?? ''));
$isLocalRequest = in_array($remoteAddr, ['127.0.0.1', '::1'], true)
|| in_array($serverName, ['localhost', '127.0.0.1', '::1'], true);
$canUseImportPage = $isAdmin || $isLocalRequest;
$pageTitle = 'Meteor-Import';
function h(string $value): string
{
return htmlspecialchars($value, ENT_QUOTES, 'UTF-8');
}
function toNullableFloat(mixed $value): ?float
{
if ($value === null || $value === '') {
return null;
}
if (is_string($value)) {
$value = str_replace(',', '.', trim($value));
}
return is_numeric($value) ? (float)$value : null;
}
function toNullableInt(mixed $value): ?int
{
if ($value === null || $value === '') {
return null;
}
if (is_string($value)) {
$value = trim($value);
}
return preg_match('/^\d+$/', (string)$value) ? (int)$value : null;
}
function ensureMeteorShowersTable(PDO $pdo): void
{
$pdo->exec(
"CREATE TABLE IF NOT EXISTS `meteoshowers` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`shower_code` varchar(16) NOT NULL,
`designation` varchar(255) NOT NULL,
`iau_no` int(10) unsigned DEFAULT NULL,
`speed` double DEFAULT NULL,
`radiant_alpha` double DEFAULT NULL,
`radiant_delta` double DEFAULT NULL,
`drift_alpha` double DEFAULT NULL,
`drift_delta` double DEFAULT NULL,
`parent_obj` varchar(255) DEFAULT NULL,
`pidx` double DEFAULT NULL,
`activity_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`activity_json`)),
`source_entry_json` longtext CHARACTER SET utf8mb4 COLLATE utf8mb4_bin DEFAULT NULL CHECK (json_valid(`source_entry_json`)),
`source_dataset_name` varchar(128) DEFAULT NULL,
`source_dataset_version` int(10) unsigned DEFAULT NULL,
`created_at` datetime NOT NULL DEFAULT current_timestamp(),
`updated_at` datetime NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),
PRIMARY KEY (`id`),
UNIQUE KEY `uq_meteoshowers_shower_code` (`shower_code`),
UNIQUE KEY `uq_meteoshowers_iau_no` (`iau_no`),
KEY `idx_meteoshowers_designation` (`designation`),
KEY `idx_meteoshowers_parent_obj` (`parent_obj`),
KEY `idx_meteoshowers_speed` (`speed`),
KEY `idx_meteoshowers_pidx` (`pidx`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci"
);
}
$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 = isset($data['shortName']) ? trim((string)$data['shortName']) : null;
$datasetVersion = toNullableInt($data['version'] ?? null);
$selectStmt = $pdo->prepare('SELECT `id` FROM `meteoshowers` WHERE `shower_code` = :shower_code LIMIT 1');
$upsertStmt = $pdo->prepare(
"INSERT INTO `meteoshowers` (
`shower_code`,
`designation`,
`iau_no`,
`speed`,
`radiant_alpha`,
`radiant_delta`,
`drift_alpha`,
`drift_delta`,
`parent_obj`,
`pidx`,
`activity_json`,
`source_entry_json`,
`source_dataset_name`,
`source_dataset_version`
) VALUES (
:shower_code,
:designation,
:iau_no,
:speed,
:radiant_alpha,
:radiant_delta,
:drift_alpha,
:drift_delta,
:parent_obj,
:pidx,
:activity_json,
:source_entry_json,
:source_dataset_name,
:source_dataset_version
)
ON DUPLICATE KEY UPDATE
`designation` = VALUES(`designation`),
`iau_no` = VALUES(`iau_no`),
`speed` = VALUES(`speed`),
`radiant_alpha` = VALUES(`radiant_alpha`),
`radiant_delta` = VALUES(`radiant_delta`),
`drift_alpha` = VALUES(`drift_alpha`),
`drift_delta` = VALUES(`drift_delta`),
`parent_obj` = VALUES(`parent_obj`),
`pidx` = VALUES(`pidx`),
`activity_json` = VALUES(`activity_json`),
`source_entry_json` = VALUES(`source_entry_json`),
`source_dataset_name` = VALUES(`source_dataset_name`),
`source_dataset_version` = VALUES(`source_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'] : [];
$upsertStmt->execute([
':shower_code' => $showerCode,
':designation' => $designation,
':iau_no' => toNullableInt($entry['IAUNo'] ?? null),
':speed' => toNullableFloat($entry['speed'] ?? null),
':radiant_alpha' => toNullableFloat($entry['radiantAlpha'] ?? null),
':radiant_delta' => toNullableFloat($entry['radiantDelta'] ?? null),
':drift_alpha' => toNullableFloat($entry['driftAlpha'] ?? null),
':drift_delta' => toNullableFloat($entry['driftDelta'] ?? null),
':parent_obj' => isset($entry['parentObj']) && trim((string)$entry['parentObj']) !== '' ? trim((string)$entry['parentObj']) : null,
':pidx' => toNullableFloat($entry['pidx'] ?? null),
':activity_json' => json_encode($activity, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
':source_entry_json' => json_encode($entry, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
':source_dataset_name' => $datasetName !== null && $datasetName !== '' ? $datasetName : null,
':source_dataset_version' => $datasetVersion,
]);
$processed++;
if ($exists) {
$updated++;
} else {
$inserted++;
}
}
$pdo->commit();
$tableCount = (int)$pdo->query('SELECT COUNT(*) FROM `meteoshowers`')->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();
}
}
?>
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><?= h($pageTitle) ?></title>
</head>
<body style="margin:0;padding:24px;font-family:Arial,sans-serif;background:#f5f5f5;color:#111;">
<div style="max-width:900px;margin:0 auto;background:#fff;border:1px solid #ccc;padding:24px;">
<h1 style="margin:0 0 12px;">Meteor-Import</h1>
<p style="margin:0 0 18px;">
Importiert <code>MeteorShowers.json</code> in die Tabelle <code>meteoshowers</code>.
Die Tabelle wird bei Bedarf automatisch angelegt.
</p>
<?php if (!$canUseImportPage): ?>
<div style="border:1px solid #c33;background:#fff3f3;color:#900;padding:14px;">
Diese Seite ist nur fuer Admins oder lokale Aufrufe auf localhost freigegeben.
</div>
<?php else: ?>
<form method="post" style="display:grid;gap:12px;">
<label style="display:grid;gap:6px;">
<span>JSON-Pfad</span>
<input
type="text"
name="json_path"
value="<?= h($selectedJsonPath) ?>"
style="width:100%;padding:10px 12px;border:1px solid #999;color:#111;"
>
</label>
<div>
<button type="submit" style="padding:10px 16px;border:1px solid #333;background:#eee;color:#111;cursor:pointer;">
Import Starten
</button>
</div>
</form>
<?php if ($errorMessage !== null): ?>
<div style="margin-top:18px;border:1px solid #c33;background:#fff3f3;color:#900;padding:14px;">
<strong>Fehler:</strong> <?= h($errorMessage) ?>
</div>
<?php endif; ?>
<?php if (is_array($report)): ?>
<div style="margin-top:18px;border:1px solid #3a7a3a;background:#f1fff1;color:#145214;padding:14px;line-height:1.7;">
<strong>Import abgeschlossen.</strong><br>
Datei: <?= h((string)$report['json_path']) ?><br>
Datensatz: <?= h((string)($report['dataset_name'] ?? '-')) ?><br>
Version: <?= h((string)($report['dataset_version'] ?? '-')) ?><br>
Verarbeitet: <?= h((string)$report['processed']) ?><br>
Neu: <?= h((string)$report['inserted']) ?><br>
Aktualisiert: <?= h((string)$report['updated']) ?><br>
Zeilen in `meteoshowers`: <?= h((string)$report['table_count']) ?>
</div>
<?php endif; ?>
<?php endif; ?>
</div>
</body>
</html>