cosmo/backend/scripts/seed_celestial_bodies.py

194 lines
6.2 KiB
Python
Executable File

#!/usr/bin/env python3
"""
Seed celestial bodies script
Adds all celestial bodies from CELESTIAL_BODIES to the database
and fetches their current positions from NASA Horizons.
Usage:
python scripts/seed_celestial_bodies.py
"""
import sys
import os
import asyncio
from datetime import datetime
# Add backend to path
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
from app.database import get_db
from app.services.horizons import horizons_service
from app.services.db_service import celestial_body_service, position_service
from app.models.celestial import CELESTIAL_BODIES
async def seed_bodies():
"""Seed celestial bodies into database"""
print("\n" + "=" * 60)
print("🌌 Seeding Celestial Bodies")
print("=" * 60)
async for session in get_db():
success_count = 0
skip_count = 0
error_count = 0
total = len(CELESTIAL_BODIES)
for idx, (body_id, info) in enumerate(CELESTIAL_BODIES.items(), 1):
body_name = info["name"]
try:
# Check if body already exists
existing_body = await celestial_body_service.get_body_by_id(body_id, session)
if existing_body:
print(f" [{idx}/{total}] ⏭️ {body_name:20s} - Already exists")
skip_count += 1
continue
print(f" [{idx}/{total}] 🔄 {body_name:20s} - Creating...", end='', flush=True)
# Create body record
body_data = {
"id": body_id,
"name": info["name"],
"name_zh": info.get("name_zh"),
"type": info["type"],
"description": info.get("description"),
"extra_data": {
"launch_date": info.get("launch_date"),
"status": info.get("status"),
}
}
await celestial_body_service.create_body(body_data, session)
print(f" ✅ Created", flush=True)
success_count += 1
except Exception as e:
print(f" ❌ Error: {str(e)}", flush=True)
error_count += 1
continue
print(f"\n{'='*60}")
print(f"📊 Summary:")
print(f" ✅ Created: {success_count}")
print(f" ⏭️ Skipped: {skip_count}")
print(f" ❌ Errors: {error_count}")
print(f"{'='*60}\n")
break
async def sync_current_positions():
"""Fetch and store current positions for all bodies"""
print("\n" + "=" * 60)
print("📍 Syncing Current Positions")
print("=" * 60)
async for session in get_db():
now = datetime.utcnow()
success_count = 0
skip_count = 0
error_count = 0
all_bodies = await celestial_body_service.get_all_bodies(session)
total = len(all_bodies)
for idx, body in enumerate(all_bodies, 1):
body_id = body.id
body_name = body.name
try:
# Check if we have recent position (within last hour)
from datetime import timedelta
recent_time = now - timedelta(hours=1)
existing_positions = await position_service.get_positions(
body_id, recent_time, now, session
)
if existing_positions and len(existing_positions) > 0:
print(f" [{idx}/{total}] ⏭️ {body_name:20s} - Recent data exists")
skip_count += 1
continue
print(f" [{idx}/{total}] 🔄 {body_name:20s} - Fetching...", end='', flush=True)
# Special handling for Sun
if body_id == "10":
positions_data = [{"time": now, "x": 0.0, "y": 0.0, "z": 0.0}]
# Special handling for Cassini
elif body_id == "-82":
cassini_date = datetime(2017, 9, 15, 11, 58, 0)
positions_data = horizons_service.get_body_positions(
body_id, cassini_date, cassini_date
)
positions_data = [
{"time": p.time, "x": p.x, "y": p.y, "z": p.z}
for p in positions_data
]
else:
# Query current position
positions_data = horizons_service.get_body_positions(
body_id, now, now
)
positions_data = [
{"time": p.time, "x": p.x, "y": p.y, "z": p.z}
for p in positions_data
]
# Store positions
for pos_data in positions_data:
await position_service.save_position(
body_id=body_id,
time=pos_data["time"],
x=pos_data["x"],
y=pos_data["y"],
z=pos_data["z"],
source="nasa_horizons",
session=session,
)
print(f" ✅ Saved {len(positions_data)} position(s)", flush=True)
success_count += 1
# Small delay to avoid overwhelming NASA API
await asyncio.sleep(0.5)
except Exception as e:
print(f" ❌ Error: {str(e)}", flush=True)
error_count += 1
continue
print(f"\n{'='*60}")
print(f"📊 Summary:")
print(f" ✅ Success: {success_count}")
print(f" ⏭️ Skipped: {skip_count}")
print(f" ❌ Errors: {error_count}")
print(f"{'='*60}\n")
break
async def main():
print("\n🚀 Celestial Bodies Database Seeding")
print("=" * 60)
print("This script will:")
print(" 1. Add all celestial bodies to the database")
print(" 2. Fetch and store their current positions")
print("=" * 60)
# Seed celestial bodies
await seed_bodies()
# Sync current positions
await sync_current_positions()
print("\n🎉 Seeding complete!")
if __name__ == "__main__":
asyncio.run(main())