重新定义天体大小的展现逻辑

main
mula.liu 2026-01-12 14:44:47 +08:00
parent 2e31f89464
commit 14fb922cd2
21 changed files with 0 additions and 1259 deletions

BIN
.DS_Store vendored

Binary file not shown.

View File

@ -1,148 +0,0 @@
# Scheduled Jobs System - Code Review Summary
## Overview
This document summarizes the code review and cleanup performed on the scheduled jobs system.
## Changes Made
### 1. Backend - Removed Debug Logs with Emojis
#### `app/jobs/predefined.py`
- Removed emoji icons from log messages (🌍, 📋, 🔄, ✅, ❌, 🎉, ⚠️)
- Changed `logger.info` to `logger.debug` for detailed operation logs
- Kept `logger.info` only for high-level operation summaries
- Kept `logger.error` and `logger.warning` for error conditions
**Before:**
```python
logger.info(f"🌍 Starting solar system position sync: days={days}")
logger.info(f"🔄 Fetching positions for {body.name}")
logger.info(f"✅ Saved {count} positions for {body.name}")
```
**After:**
```python
logger.info(f"Starting solar system position sync: days={days}")
logger.debug(f"Fetching positions for {body.name}")
logger.debug(f"Saved {count} positions for {body.name}")
```
#### `app/jobs/registry.py`
- Changed task registration log from `logger.info` to `logger.debug`
- Changed task execution logs from `logger.info` to `logger.debug`
- Removed emoji icons (📋, 🚀, ✅)
#### `app/services/scheduler_service.py`
- Removed emoji icons from all log messages (⏰, ❌, ✅)
- Kept important lifecycle logs as `logger.info` (start, stop, job scheduling)
- Changed detailed execution logs to `logger.debug`
### 2. Backend - Removed Unused Imports
#### `app/api/scheduled_job.py`
- Removed unused imports: `update`, `delete` from sqlalchemy
**Before:**
```python
from sqlalchemy import select, update, delete
```
**After:**
```python
from sqlalchemy import select
```
### 3. Frontend - Removed Debug Console Logs
#### `pages/admin/ScheduledJobs.tsx`
- Removed `console.log` statements from `loadAvailableTasks()`
- Removed `console.error` statements from `loadAvailableTasks()`
- Removed `console.log` statements from `handleEdit()`
- Removed `console.error` from error handling (kept only toast messages)
**Removed:**
```typescript
console.log('Loaded available tasks:', result);
console.error('Failed to load available tasks:', error);
console.log('Editing record:', record);
console.log('Available tasks:', availableTasks);
console.error(error);
```
## Code Quality Improvements
### 1. Consistent Logging Levels
- **ERROR**: For failures that prevent operations
- **WARNING**: For non-critical issues (e.g., "No bodies found")
- **INFO**: For high-level operation summaries
- **DEBUG**: For detailed operation traces
### 2. Clean User-Facing Messages
- All user-facing error messages use toast notifications
- No console output in production frontend code
- Backend logs are professional and parseable
### 3. Transaction Safety
- Using SQLAlchemy savepoints (`begin_nested()`) for isolated error handling
- Proper rollback and commit patterns
- Error messages include full traceback for debugging
## Testing Results
### Import Test
✓ All backend imports successful
✓ Task registry properly initialized
✓ 2 tasks registered:
- sync_solar_system_positions
- sync_celestial_events
### Task Schema Test
✓ Task parameters properly defined:
- body_ids (array, optional, default=None)
- days (integer, optional, default=7)
- source (string, optional, default=nasa_horizons_cron)
### Integration Test
✓ Position constraint fixed (nasa_horizons_cron added to CHECK constraint)
✓ Manual job execution successful
✓ 26 celestial bodies synced with 52 positions
✓ Task record properly created and updated
✓ No failures during execution
## Remaining Console Logs (Other Admin Pages)
The following console logs exist in other admin pages but were left unchanged as they're outside the scope of this scheduled jobs feature:
- `SystemSettings.tsx`: 1 console.error
- `Users.tsx`: 2 console.error
- `Dashboard.tsx`: 1 console.error
- `StaticData.tsx`: 1 console.error
- `CelestialBodies.tsx`: 2 (1 error, 1 for JSON parsing)
- `NASADownload.tsx`: 3 (2 debug logs, 1 error)
## Files Modified
### Backend
1. `/backend/app/jobs/predefined.py` - Removed emoji logs, adjusted log levels
2. `/backend/app/jobs/registry.py` - Changed to debug logging
3. `/backend/app/services/scheduler_service.py` - Removed emojis, adjusted log levels
4. `/backend/app/api/scheduled_job.py` - Removed unused imports
### Frontend
1. `/frontend/src/pages/admin/ScheduledJobs.tsx` - Removed all console logs
### Database
1. `/backend/scripts/fix_position_source_constraint.py` - Fixed CHECK constraint
## Summary
All scheduled jobs related code has been reviewed and cleaned:
- ✅ No emoji icons in production logs
- ✅ Appropriate logging levels (ERROR/WARNING/INFO/DEBUG)
- ✅ No console.log/console.error in frontend
- ✅ No unused imports
- ✅ All imports and registrations working
- ✅ Database constraints fixed
- ✅ Integration tests passing
The code is now production-ready with clean, professional logging suitable for monitoring and debugging.

View File

@ -1,182 +0,0 @@
# 恒星系统架构改造 - 进度报告
## ✅ 已完成工作
### 1. 数据库架构改造
- ✅ 创建 `star_systems`
- ✅ 添加太阳系初始记录id=1
- ✅ 扩展 `celestial_bodies` 表(添加 `system_id` 字段)
- ✅ 更新所有太阳系天体 `system_id = 1`30个天体
### 2. ORM 模型
- ✅ 创建 `StarSystem` ORM 模型
- ✅ 更新 `CelestialBody` ORM 模型(添加 system_id 关系)
- ✅ 在 `__init__.py` 中注册 StarSystem
### 3. 数据迁移
- ✅ 编写完整的数据迁移脚本(`scripts/migrate_interstellar_data.py`
- ✅ 实现自动中文名翻译功能
- ✅ 实现行星数据去重逻辑
- ✅ 成功迁移 578 个系外恒星系统
- ✅ 成功迁移 898 颗系外行星(去重后)
### 4. 后端服务层
- ✅ 创建 `StarSystemService``app/services/star_system_service.py`
- 支持 CRUD 操作
- 支持搜索和分页
- 支持获取恒星系及其所有天体
- 支持统计功能
- ✅ 创建 Pydantic 模型(`app/models/star_system.py`
- StarSystemBase
- StarSystemCreate
- StarSystemUpdate
- StarSystemResponse
- StarSystemWithBodies
- StarSystemStatistics
### 5. 迁移数据统计
```
恒星系统总数: 579
- 太阳系: 1
- 系外恒星系: 578
天体总数: 928
- 太阳系天体: 30含太阳、行星、矮行星、卫星、探测器、彗星
- 系外行星: 898已去重
数据质量:
- 去重前行星记录: ~3000+
- 去重后行星记录: 898
- 去重率: ~70%
```
---
## 🚧 剩余工作
### 1. 后端 API 开发
- [ ] 创建 StarSystem API 路由
- GET /api/star-systems获取所有恒星系统
- GET /api/star-systems/{id}(获取单个恒星系统)
- GET /api/star-systems/{id}/bodies获取恒星系及其天体
- POST /api/admin/star-systems创建恒星系统
- PUT /api/admin/star-systems/{id}(更新恒星系统)
- DELETE /api/admin/star-systems/{id}(删除恒星系统)
- GET /api/star-systems/statistics获取统计信息
- [ ] 更新 CelestialBody API
- 添加 `system_id` 查询参数
- 添加 `include_no_system` 参数(用于包含探测器等)
### 2. 后台管理界面Admin Frontend
- [ ] 创建恒星系统管理页面(`/admin/star-systems`
- 列表展示(支持搜索、分页)
- 新增恒星系统
- 编辑恒星系统
- 删除恒星系统(不可删除太阳系)
- 查看恒星系详情(含所有行星)
- [ ] 改造天体管理页面(`/admin/celestial-bodies`
- **关键改动**:先选择恒星系,再列出该恒星系的天体
- 添加恒星系选择器(下拉框)
- 根据选中的恒星系过滤天体列表
- 新增天体时自动设置 `system_id`
- 支持在恒星系之间移动天体
### 3. 前端界面更新
- [ ] 更新 GalaxyScene 组件
- 使用新的 `/api/star-systems` API
- 移除前端行星去重代码
- 优化恒星点击事件(使用后端返回的完整数据)
- [ ] 更新 App.tsx 查询逻辑
- Solar 视图:查询 `system_id=1` 的天体
- Galaxy 视图:查询所有恒星系统
### 4. 菜单配置
- [ ] 在后台管理菜单中添加"恒星系统管理"入口
---
## 📊 数据模型关系图
```
star_systems (579条记录)
├── id=1: Solar System (太阳系)
│ └── celestial_bodies (30条)
│ ├── Sun (star)
│ ├── Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune (planet)
│ ├── Pluto, Ceres, Haumea, Makemake, Eris (dwarf_planet)
│ ├── Moon, Io, Europa, Ganymede, Callisto (satellite)
│ ├── Voyager 1, Voyager 2, Parker Solar Probe... (probe)
│ └── Halley, NEOWISE, C/2020 F3 (comet)
├── id=2: Proxima Cen System (比邻星系统)
│ └── celestial_bodies (2条)
│ ├── Proxima Cen b (比邻星 b)
│ └── Proxima Cen d (比邻星 d)
├── id=3: TRAPPIST-1 System
│ └── celestial_bodies (7条)
│ └── TRAPPIST-1 b/c/d/e/f/g/h
└── ... (575 more systems)
```
---
## 🎯 下一步行动
**立即可做:**
1. 完成 StarSystem API 路由
2. 测试 API 端点
3. 开发后台管理界面
**预计工作量:**
- 后端 API1-2小时
- 后台界面3-4小时
- 前端更新1-2小时
- 测试验证1小时
**总计6-9小时**
---
## 🔧 技术要点
### 中文名翻译规则
```python
# 恒星名翻译示例
Proxima Cen → 比邻星
Kepler-442 → 开普勒-442
TRAPPIST-1 → TRAPPIST-1
HD 40307 → HD 40307
# 行星名翻译示例
Proxima Cen b → 比邻星 b
Kepler-442 b → 开普勒-442 b
```
### 去重逻辑
- 按行星名称name去重
- 保留字段最完整的记录非NULL字段最多的
- 平均每个恒星系从5.2条记录减少到1.6条效率提升70%
### 查询优化
```sql
-- Solar 视图
SELECT * FROM celestial_bodies WHERE system_id = 1;
-- Galaxy 视图
SELECT * FROM star_systems WHERE id > 1;
-- 恒星系详情
SELECT * FROM celestial_bodies WHERE system_id = ?;
```
---
**文档版本**: v1.0
**更新时间**: 2025-12-05 19:10
**状态**: 数据迁移完成API开发进行中

View File

@ -1,61 +0,0 @@
#!/usr/bin/env python3
"""Add missing real_radius data for solar system bodies"""
import asyncio
import sys
import os
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from app.services.db_service import celestial_body_service
# Real radii from NASA data (in km)
MISSING_RADII = {
'10': 696000, # Sun
'999': 1188, # Pluto
'2000001': 476, # Ceres
'136199': 1163, # Eris
'136108': 816, # Haumea
'136472': 715, # Makemake
}
async def add_missing_radii():
"""Add real_radius to bodies that are missing it"""
async with AsyncSessionLocal() as session:
print("=" * 70)
print("Adding missing real_radius data")
print("=" * 70)
for body_id, radius in MISSING_RADII.items():
body = await celestial_body_service.get_body_by_id(body_id, session)
if not body:
print(f"❌ Body {body_id} not found")
continue
# Get existing extra_data or create new dict
extra_data = body.extra_data.copy() if body.extra_data else {}
# Add real_radius
extra_data['real_radius'] = radius
# Update body
updated = await celestial_body_service.update_body(
body_id,
{'extra_data': extra_data},
session
)
if updated:
print(f"{body.name:<20} (ID: {body_id:<10}): Added real_radius = {radius:>8} km")
else:
print(f"{body.name:<20} (ID: {body_id:<10}): Update failed")
await session.commit()
print("\n" + "=" * 70)
print("✅ All missing real_radius data added successfully!")
print("=" * 70)
if __name__ == "__main__":
asyncio.run(add_missing_radii())

View File

@ -1,64 +0,0 @@
#!/usr/bin/env python3
"""Analyze the size calculation problem"""
import json
print("=" * 80)
print("SIZE CALCULATION ANALYSIS")
print("=" * 80)
configs = {
"planet": {"ratio": 0.00008},
"dwarf_planet": {"ratio": 0.00015},
}
bodies = {
'Jupiter': {'type': 'planet', 'radius': 69911},
'Saturn': {'type': 'planet', 'radius': 58232},
'Earth': {'type': 'planet', 'radius': 6371},
'Ceres': {'type': 'dwarf_planet', 'radius': 476},
'Pluto': {'type': 'dwarf_planet', 'radius': 1188},
}
print("\n1. CALCULATED DISPLAY SIZES:")
print("-" * 80)
for name, body in bodies.items():
ratio = configs[body['type']]['ratio']
size = body['radius'] * ratio
print(f"{name:<15}: {body['radius']:>6} km * {ratio:.6f} = {size:.4f}")
print("\n2. SIZE RATIOS (relative to Earth):")
print("-" * 80)
earth_size = bodies['Earth']['radius'] * configs['planet']['ratio']
for name, body in bodies.items():
ratio = configs[body['type']]['ratio']
size = body['radius'] * ratio
relative = size / earth_size
print(f"{name:<15}: {size:.4f} / {earth_size:.4f} = {relative:.2f}x Earth")
print("\n" + "=" * 80)
print("⚠️ PROBLEM IDENTIFIED:")
print("=" * 80)
print("Planet ratio: 0.00008")
print("Dwarf planet ratio: 0.00015 (1.875x larger!)")
print()
print("This means dwarf planets are artificially ENLARGED by 1.875x")
print("relative to regular planets!")
print()
print("IMPACT:")
print(" • Small dwarf planets (Ceres) appear TOO LARGE")
print(" • Large planets (Jupiter, Saturn) appear correctly sized")
print(" • But the ratio between them is WRONG")
print()
print("SOLUTION:")
print(" → Use the SAME ratio for all body types")
print(" → Recommended: ratio = 0.00008 for all types")
print("=" * 80)
# Show what sizes would be with unified ratio
print("\n3. SIZES WITH UNIFIED RATIO (0.00008):")
print("-" * 80)
unified_ratio = 0.00008
for name, body in bodies.items():
size = body['radius'] * unified_ratio
relative = size / earth_size
print(f"{name:<15}: {body['radius']:>6} km * {unified_ratio:.6f} = {size:.4f} ({relative:.2f}x Earth)")

View File

@ -1,36 +0,0 @@
#!/usr/bin/env python3
"""Check all positions for Earth"""
import asyncio
import sys
import os
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from app.services.db_service import position_service
async def check_all_positions():
"""Check all position data for Earth"""
async with AsyncSessionLocal() as session:
# Get all positions (no time filter)
positions = await position_service.get_positions(
body_id="399",
start_time=None,
end_time=None,
session=session
)
print(f"Total positions for Earth: {len(positions)}")
if positions:
print(f"\nFirst position: {positions[0].time}")
print(f"Last position: {positions[-1].time}")
print("\nSample of last 5 positions:")
for pos in positions[-5:]:
print(f" {pos.time}: ({pos.x:.6f}, {pos.y:.6f}, {pos.z:.6f})")
else:
print("\n❌ No positions at all!")
print("You need to download position data first.")
if __name__ == "__main__":
asyncio.run(check_all_positions())

View File

@ -1,43 +0,0 @@
#!/usr/bin/env python3
"""Check real_radius for all planets"""
import asyncio
import sys
import os
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from sqlalchemy import select
from app.models.db.celestial_body import CelestialBody
async def check_radii():
async with AsyncSessionLocal() as session:
# Get all planets and dwarf planets
stmt = select(CelestialBody).where(
CelestialBody.type.in_(['planet', 'dwarf_planet', 'star'])
).order_by(CelestialBody.name)
result = await session.execute(stmt)
bodies = result.scalars().all()
print("Body Name | Type | Real Radius (km)")
print("-" * 60)
for body in bodies:
radius = body.extra_data.get('real_radius') if body.extra_data else None
print(f"{body.name:20s} | {body.type:13s} | {radius if radius else 'N/A'}")
# Calculate ratios relative to Earth
earth_radius = 6371
print("\n" + "=" * 60)
print("Size ratios relative to Earth (6371 km):")
print("=" * 60)
for body in bodies:
if body.extra_data and body.extra_data.get('real_radius'):
radius = body.extra_data['real_radius']
ratio = radius / earth_radius
print(f"{body.name:20s}: {radius:8.0f} km = {ratio:6.2f}x Earth")
if __name__ == "__main__":
asyncio.run(check_radii())

View File

@ -1,29 +0,0 @@
import requests
import sys
def check_api():
url = "http://localhost:8000/api/celestial/positions"
try:
response = requests.get(url, timeout=5)
response.raise_for_status()
data = response.json()
bodies = data.get("bodies", [])
print(f"API returned {len(bodies)} bodies.")
targets = [b for b in bodies if b.get("name") in ["Earth", "Jupiter"]]
for t in targets:
print(f"Body: {t.get('name')}")
print(f" Keys: {list(t.keys())}")
print(f" Extra Data: {t.get('extra_data')}")
if t.get('extra_data'):
print(f" Real Radius: {t.get('extra_data', {}).get('real_radius')}")
else:
print(" Real Radius: MISSING")
except Exception as e:
print(f"Error checking API: {e}")
if __name__ == "__main__":
check_api()

View File

@ -1,53 +0,0 @@
#!/usr/bin/env python3
"""Check celestial_type_configs system setting"""
import asyncio
import sys
import os
import json
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from app.services.system_settings_service import system_settings_service
async def check_configs():
async with AsyncSessionLocal() as session:
# Get celestial_type_configs
configs = await system_settings_service.get_setting_value('celestial_type_configs', session)
if configs:
print("=" * 70)
print("CELESTIAL TYPE CONFIGS")
print("=" * 70)
print(json.dumps(configs, indent=2))
print("\n" + "=" * 70)
print("CALCULATED SIZES (assuming Earth radius = 6371 km)")
print("=" * 70)
# Test calculation for different body types
test_bodies = {
'Sun': {'type': 'star', 'real_radius': 696000},
'Jupiter': {'type': 'planet', 'real_radius': 69911},
'Saturn': {'type': 'planet', 'real_radius': 58232},
'Earth': {'type': 'planet', 'real_radius': 6371},
'Mars': {'type': 'planet', 'real_radius': 3389},
'Ceres': {'type': 'dwarf_planet', 'real_radius': 476},
'Pluto': {'type': 'dwarf_planet', 'real_radius': 1188},
}
for name, body in test_bodies.items():
body_type = body['type']
real_radius = body['real_radius']
if body_type in configs and 'ratio' in configs[body_type]:
ratio = configs[body_type]['ratio']
calculated_size = real_radius * ratio
print(f"{name:<15} ({body_type:<13}): {real_radius:>7} km * {ratio:.6f} = {calculated_size:.4f}")
else:
print(f"{name:<15} ({body_type:<13}): No ratio config found")
else:
print("❌ celestial_type_configs not found in system settings")
if __name__ == "__main__":
asyncio.run(check_configs())

View File

@ -1,52 +0,0 @@
#!/usr/bin/env python3
"""Check which solar system bodies are missing real_radius"""
import asyncio
import sys
import os
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from sqlalchemy import select
from app.models.db.celestial_body import CelestialBody
async def check_missing_radii():
async with AsyncSessionLocal() as session:
# Solar System body IDs
solar_ids = ['10', '199', '299', '399', '499', '599', '699', '799', '899', '999',
'2000001', '136199', '136108', '136472']
stmt = select(CelestialBody).where(CelestialBody.id.in_(solar_ids))
result = await session.execute(stmt)
bodies = result.scalars().all()
print("=" * 70)
print("SOLAR SYSTEM BODIES - Missing real_radius Check")
print("=" * 70)
has_radius = []
missing_radius = []
for body in bodies:
radius = body.extra_data.get('real_radius') if body.extra_data else None
if radius:
has_radius.append((body.name, body.type, radius))
else:
missing_radius.append((body.name, body.type))
print(f"\n✅ Bodies WITH real_radius ({len(has_radius)}):")
print("-" * 70)
for name, btype, radius in sorted(has_radius, key=lambda x: x[2], reverse=True):
print(f" {name:<20} ({btype:<15}): {radius:>8.0f} km")
print(f"\n❌ Bodies MISSING real_radius ({len(missing_radius)}):")
print("-" * 70)
for name, btype in sorted(missing_radius):
print(f" {name:<20} ({btype})")
if missing_radius:
print("\n⚠️ WARNING: Bodies without real_radius will use TYPE_SIZES fallback!")
print(" This causes INCONSISTENT scaling!")
if __name__ == "__main__":
asyncio.run(check_missing_radii())

View File

@ -1,40 +0,0 @@
#!/usr/bin/env python3
"""Check if positions exist in database for Earth"""
import asyncio
import sys
import os
from datetime import datetime, timedelta
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from app.services.db_service import position_service
async def check_positions():
"""Check if we have position data for Earth"""
async with AsyncSessionLocal() as session:
now = datetime.utcnow()
recent_window = now - timedelta(hours=24)
positions = await position_service.get_positions(
body_id="399",
start_time=recent_window,
end_time=now,
session=session
)
print(f"Checking positions for Earth (ID: 399)")
print(f"Time range: {recent_window} to {now}")
print(f"Found {len(positions)} positions")
if positions:
latest = positions[-1]
print(f"\nLatest position:")
print(f" Time: {latest.time}")
print(f" X: {latest.x}, Y: {latest.y}, Z: {latest.z}")
else:
print("\n❌ No recent positions found for Earth!")
print("This explains why the API is failing.")
if __name__ == "__main__":
asyncio.run(check_positions())

View File

@ -1,46 +0,0 @@
#!/usr/bin/env python3
"""Check real_radius for solar system planets only"""
import asyncio
import sys
import os
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from sqlalchemy import select
from app.models.db.celestial_body import CelestialBody
async def check_solar_system():
async with AsyncSessionLocal() as session:
# Solar System body IDs
solar_ids = ['10', '199', '299', '399', '499', '599', '699', '799', '899', '999',
'2000001', '136199', '136108', '136472']
stmt = select(CelestialBody).where(CelestialBody.id.in_(solar_ids))
result = await session.execute(stmt)
bodies = result.scalars().all()
print("=" * 70)
print("SOLAR SYSTEM CELESTIAL BODIES - Real Radius Check")
print("=" * 70)
print(f"{'Name':<20} {'Type':<15} {'Real Radius (km)':>15}")
print("-" * 70)
for body in bodies:
radius = body.extra_data.get('real_radius') if body.extra_data else None
print(f"{body.name:<20} {body.type:<15} {radius if radius else 'N/A':>15}")
# Calculate ratios relative to Earth
earth_radius = 6371
print("\n" + "=" * 70)
print("SIZE RATIOS (relative to Earth = 1.0x)")
print("=" * 70)
for body in sorted(bodies, key=lambda b: b.extra_data.get('real_radius', 0) if b.extra_data else 0, reverse=True):
if body.extra_data and body.extra_data.get('real_radius'):
radius = body.extra_data['real_radius']
ratio = radius / earth_radius
print(f"{body.name:<20}: {radius:>8.0f} km = {ratio:>6.2f}x Earth")
if __name__ == "__main__":
asyncio.run(check_solar_system())

View File

@ -1,28 +0,0 @@
#!/usr/bin/env python3
"""Clear all caches to force fresh data"""
import asyncio
import sys
import os
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.services.redis_cache import redis_cache
from app.services.cache import cache_service
async def clear_caches():
"""Clear all caches"""
print("Clearing memory cache...")
cache_service.clear()
print("✓ Memory cache cleared")
print("\nClearing Redis cache...")
try:
await redis_cache.clear_all()
print("✓ Redis cache cleared")
except Exception as e:
print(f"✗ Redis cache clear failed: {e}")
print("\nAll caches cleared! Fresh data will be loaded on next request.")
if __name__ == "__main__":
asyncio.run(clear_caches())

View File

@ -1,15 +0,0 @@
import requests
def clear_cache():
url = "http://localhost:8000/api/system/cache/clear"
try:
response = requests.post(url, timeout=5)
print(f"Status: {response.status_code}")
print(f"Body: {response.text}")
response.raise_for_status()
print(f"Cache cleared: {response.json()}")
except Exception as e:
print(f"Error clearing cache: {e}")
if __name__ == "__main__":
clear_cache()

View File

@ -1,92 +0,0 @@
#!/usr/bin/env python3
"""Show the complete picture of the sizing problem"""
print("=" * 90)
print("COMPLETE SIZE CALCULATION ANALYSIS")
print("=" * 90)
# Real radii (km)
real_radii = {
'Sun': 696000,
'Jupiter': 69911,
'Saturn': 58232,
'Earth': 6371,
'Ceres': 476,
'Pluto': 1188,
}
# Current config
planet_ratio = 0.00008
dwarf_ratio = 0.00015
star_ratio = 0.0000015
# Current TYPE_SIZES fallback
type_sizes = {
'planet': 0.6,
'dwarf_planet': 0.18,
'star': 0.4,
}
print("\n📊 CURRENT SITUATION:")
print("-" * 90)
print(f"{'Body':<15} {'Has real_radius?':<20} {'Calculation':<40} {'Size':<10}")
print("-" * 90)
# Planets with real_radius
for name in ['Jupiter', 'Saturn', 'Earth']:
radius = real_radii[name]
size = radius * planet_ratio
calc = f"{radius} * {planet_ratio} = {size:.4f}"
print(f"{name:<15} {'YES (✓)':<20} {calc:<40} {size:<10.4f}")
# Dwarf planets WITHOUT real_radius (using fallback)
for name in ['Ceres', 'Pluto']:
radius = real_radii[name]
fallback_size = type_sizes['dwarf_planet']
correct_size = radius * planet_ratio # What it SHOULD be
calc = f"FALLBACK: {fallback_size:.2f} (should be {correct_size:.4f})"
ratio = fallback_size / correct_size
print(f"{name:<15} {'NO (✗) MISSING!':<20} {calc:<40} {fallback_size:<10.2f} ({ratio:.1f}x too large!)")
# Sun WITHOUT real_radius
name = 'Sun'
radius = real_radii[name]
fallback_size = type_sizes['star']
correct_size = radius * star_ratio
calc = f"FALLBACK: {fallback_size:.2f} (should be {correct_size:.4f})"
ratio = fallback_size / correct_size
print(f"{name:<15} {'NO (✗) MISSING!':<20} {calc:<40} {fallback_size:<10.2f}")
print("\n" + "=" * 90)
print("⚠️ PROBLEMS:")
print("=" * 90)
print("1. Dwarf planets (Ceres, Pluto, etc.) use FIXED fallback size 0.18")
print(" → Ceres (476 km) should be 0.0381 but shows as 0.18 (4.7x too large!)")
print(" → Pluto (1188 km) should be 0.0950 but shows as 0.18 (1.9x too large!)")
print()
print("2. Different ratios for different types causes MORE confusion:")
print(f" → planet ratio: {planet_ratio}")
print(f" → dwarf_planet ratio: {dwarf_ratio} (1.875x larger!)")
print()
print("3. Sun has no real_radius, uses fixed size 0.4")
print()
print("=" * 90)
print("✅ SOLUTION:")
print("=" * 90)
print("1. Add missing real_radius data for:")
print(" • Sun: 696000 km")
print(" • Ceres: 476 km")
print(" • Pluto: 1188 km")
print(" • Eris: 1163 km")
print(" • Haumea: 816 km")
print(" • Makemake: 715 km")
print()
print("2. Use UNIFIED ratio for all types:")
print(" • Recommended: 0.00008 for ALL types (star, planet, dwarf_planet, etc.)")
print(" • This ensures consistent scaling based on real physical size")
print()
print("3. If planets appear too large visually, adjust the GLOBAL ratio:")
print(" • Reduce ratio to 0.00005 or 0.00004 for ALL types")
print(" • This keeps relative sizes correct while fitting better on screen")
print("=" * 90)

View File

@ -1,53 +0,0 @@
#!/usr/bin/env python3
"""Direct update celestial_type_configs using SQL"""
import asyncio
import sys
import os
import json
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from sqlalchemy import select, update
from app.models.db.system_settings import SystemSettings
# Unified ratio for all types
UNIFIED_RATIO = 0.00008
async def direct_update():
"""Direct SQL update of celestial_type_configs"""
async with AsyncSessionLocal() as session:
# Get the setting record
stmt = select(SystemSettings).where(SystemSettings.key == 'celestial_type_configs')
result = await session.execute(stmt)
setting = result.scalar_one_or_none()
if not setting:
print("❌ Setting 'celestial_type_configs' not found!")
return
# Parse the JSON value
current_value = json.loads(setting.value) if isinstance(setting.value, str) else setting.value
print("BEFORE UPDATE:")
print(json.dumps(current_value, indent=2))
# Update all ratios
updated_config = {}
for body_type, config in current_value.items():
updated_config[body_type] = {
'ratio': UNIFIED_RATIO,
'default': config.get('default', 0.5)
}
# Update the raw_value (stored as JSON string)
setting.raw_value = json.dumps(updated_config)
await session.commit()
print("\nAFTER UPDATE:")
print(json.dumps(updated_config, indent=2))
print(f"\n✅ Successfully updated all ratios to {UNIFIED_RATIO}")
if __name__ == "__main__":
asyncio.run(direct_update())

View File

@ -1,76 +0,0 @@
#!/usr/bin/env python3
"""Verify final sizes after updates - SIMPLIFIED"""
import asyncio
import sys
import os
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from sqlalchemy import select
from app.models.db.celestial_body import CelestialBody
from app.services.system_settings_service import system_settings_service
async def verify_final():
async with AsyncSessionLocal() as session:
# Get updated config
configs = await system_settings_service.get_setting_value('celestial_type_configs', session)
# Solar System body IDs
solar_ids = ['10', '199', '299', '399', '499', '599', '699', '799', '899', '999',
'2000001', '136199', '136108', '136472']
stmt = select(CelestialBody).where(CelestialBody.id.in_(solar_ids))
result = await session.execute(stmt)
bodies = result.scalars().all()
print("=" * 95)
print("FINAL VERIFICATION - Solar System Celestial Body Display Sizes")
print("=" * 95)
print(f"{'Name':<15} {'Type':<15} {'Real Radius':>12} {'× Ratio':>12} {'= Display':>12} {'Relative':<12}")
print("-" * 95)
earth_display = None
data = []
for body in bodies:
if body.extra_data and body.extra_data.get('real_radius'):
real_radius = body.extra_data['real_radius']
ratio = configs[body.type]['ratio']
display_size = real_radius * ratio
data.append({
'name': body.name,
'type': body.type,
'real_radius': real_radius,
'ratio': ratio,
'display': display_size
})
if body.name == 'Earth':
earth_display = display_size
# Sort by display size
data.sort(key=lambda x: x['display'], reverse=True)
for d in data:
rel = f"{d['display'] / earth_display:.2f}x Earth" if earth_display else "N/A"
print(f"{d['name']:<15} {d['type']:<15} {d['real_radius']:>9,.0f} km {d['ratio']:.6f} {d['display']:>10.4f} {rel:<12}")
print("\n" + "=" * 95)
print("🎉 SUCCESS! All sizes are now physically accurate and consistent!")
print("=" * 95)
print(f"\nUnified Ratio: {configs['planet']['ratio']}")
print(f"Formula: Display Size = Real Radius (km) × {configs['planet']['ratio']}")
print("\nKey Size Relationships (all relative to Earth):")
size_dict = {d['name']: d['display'] for d in data}
print(f" • Sun = {size_dict['Sun'] / earth_display:>6.2f}x (physically correct)")
print(f" • Jupiter = {size_dict['Jupiter'] / earth_display:>6.2f}x (should be ~11x) ✓")
print(f" • Saturn = {size_dict['Saturn'] / earth_display:>6.2f}x (should be ~9x) ✓")
print(f" • Earth = {size_dict['Earth'] / earth_display:>6.2f}x (baseline)")
print(f" • Pluto = {size_dict['Pluto'] / earth_display:>6.2f}x (should be ~0.19x) ✓")
print(f" • Ceres = {size_dict['Ceres'] / earth_display:>6.2f}x (should be ~0.07x) ✓")
if __name__ == "__main__":
asyncio.run(verify_final())

View File

@ -1,47 +0,0 @@
#!/usr/bin/env python3
"""Test script to verify extra_data in API response"""
import asyncio
import sys
import os
import json
# Add backend to path
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from app.services.db_service import celestial_body_service
async def test_api_data():
"""Simulate API response building"""
async with AsyncSessionLocal() as session:
# Get Earth
body = await celestial_body_service.get_body_by_id("399", session)
if not body:
print("Earth not found in database")
return
# Build response dict like API does
body_dict = {
"id": body.id,
"name": body.name,
"name_zh": body.name_zh,
"type": body.type,
"description": body.description,
"is_active": body.is_active,
"extra_data": body.extra_data,
"positions": []
}
print("=== Database Body Object ===")
print(f"Body ID: {body.id}")
print(f"Body Name: {body.name}")
print(f"Body Type: {body.type}")
print(f"Extra Data Type: {type(body.extra_data)}")
print(f"Extra Data: {body.extra_data}")
print("\n=== API Response Dict ===")
print(json.dumps(body_dict, indent=2, default=str))
if __name__ == "__main__":
asyncio.run(test_api_data())

View File

@ -1,66 +0,0 @@
#!/usr/bin/env python3
"""Direct API test - simulate the exact API call"""
import asyncio
import sys
import os
from datetime import datetime
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from app.services.db_service import celestial_body_service, position_service
async def simulate_api_call():
"""Simulate /celestial/positions endpoint"""
async with AsyncSessionLocal() as db:
# Get all bodies from database
all_bodies = await celestial_body_service.get_all_bodies(db)
# Filter to only Solar System bodies
all_bodies = [b for b in all_bodies if b.system_id == 1]
# Filter to Earth only
all_bodies = [b for b in all_bodies if b.id == "399"]
bodies_data = []
now = datetime.utcnow()
for body in all_bodies:
# Get most recent position
recent_positions = await position_service.get_positions(
body_id=body.id,
start_time=now,
end_time=now,
session=db
)
if recent_positions and len(recent_positions) > 0:
latest_pos = recent_positions[-1]
body_dict = {
"id": body.id,
"name": body.name,
"name_zh": body.name_zh,
"type": body.type,
"description": body.description,
"is_active": body.is_active,
"extra_data": body.extra_data, # THIS IS THE KEY LINE
"positions": [{
"time": latest_pos.time.isoformat(),
"x": latest_pos.x,
"y": latest_pos.y,
"z": latest_pos.z,
}]
}
bodies_data.append(body_dict)
print("=== Simulated API Response for Earth ===")
print(f"ID: {body_dict['id']}")
print(f"Name: {body_dict['name']}")
print(f"Type: {body_dict['type']}")
print(f"Extra Data: {body_dict['extra_data']}")
print(f"Extra Data Type: {type(body_dict['extra_data'])}")
if body_dict['extra_data']:
print(f"Real Radius: {body_dict['extra_data'].get('real_radius')}")
if __name__ == "__main__":
asyncio.run(simulate_api_call())

View File

@ -1,62 +0,0 @@
#!/usr/bin/env python3
"""Update celestial_type_configs to use unified ratio"""
import asyncio
import sys
import os
import json
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from app.services.system_settings_service import system_settings_service
# Unified ratio for all types
# This ensures consistent scaling based on real physical size
UNIFIED_RATIO = 0.00008
async def update_unified_ratio():
"""Update all type ratios to use unified value"""
async with AsyncSessionLocal() as session:
# Get current config
current_config = await system_settings_service.get_setting_value('celestial_type_configs', session)
print("=" * 70)
print("CURRENT celestial_type_configs:")
print("=" * 70)
print(json.dumps(current_config, indent=2))
# Update all ratios to unified value
updated_config = {}
for body_type, config in current_config.items():
updated_config[body_type] = {
'ratio': UNIFIED_RATIO,
'default': config.get('default', 0.5) # Keep existing defaults
}
print("\n" + "=" * 70)
print(f"UPDATED celestial_type_configs (unified ratio = {UNIFIED_RATIO}):")
print("=" * 70)
print(json.dumps(updated_config, indent=2))
# Save updated config
await system_settings_service.update_setting(
'celestial_type_configs',
updated_config,
session
)
await session.commit()
print("\n" + "=" * 70)
print("✅ All type ratios unified successfully!")
print("=" * 70)
print("\nNOW ALL BODIES WILL USE:")
print(f" Display Size = real_radius (km) × {UNIFIED_RATIO}")
print("\nThis ensures:")
print(" • Consistent scaling across all body types")
print(" • Jupiter is correctly 10.97x larger than Earth")
print(" • Ceres is correctly 0.07x the size of Earth")
print(" • Pluto is correctly 0.19x the size of Earth")
if __name__ == "__main__":
asyncio.run(update_unified_ratio())

View File

@ -1,66 +0,0 @@
#!/usr/bin/env python3
"""Verify final sizes after updates"""
import asyncio
import sys
import os
sys.path.append(os.path.join(os.getcwd(), "backend"))
from app.database import AsyncSessionLocal
from sqlalchemy import select
from app.models.db.celestial_body import CelestialBody
from app.services.system_settings_service import system_settings_service
async def verify_final_sizes():
async with AsyncSessionLocal() as session:
# Get updated config
configs = await system_settings_service.get_setting_value('celestial_type_configs', session)
# Solar System body IDs
solar_ids = ['10', '199', '299', '399', '499', '599', '699', '799', '899', '999',
'2000001', '136199', '136108', '136472']
stmt = select(CelestialBody).where(CelestialBody.id.in_(solar_ids))
result = await session.execute(stmt)
bodies = result.scalars().all()
print("=" * 85)
print("FINAL VERIFICATION - All Celestial Body Sizes")
print("=" * 85)
print(f"{'Name':<20} {'Type':<15} {'Real Radius':<15} {'Ratio':<12} {'Display Size':<12} {'vs Earth'}")
print("-" * 85)
earth_size = None
sizes = []
for body in sorted(bodies, key=lambda b: b.extra_data.get('real_radius', 0) if b.extra_data else 0, reverse=True):
if body.extra_data and body.extra_data.get('real_radius'):
real_radius = body.extra_data['real_radius']
ratio = configs[body.type]['ratio']
display_size = real_radius * ratio
sizes.append((body.name, body.type, real_radius, ratio, display_size))
if body.name == 'Earth':
earth_size = display_size
# Print with Earth comparison
for name, btype, real_radius, ratio, display_size in sizes:
vs_earth = f"{display_size / earth_size:.2f}x" if earth_size else "N/A"
print(f"{name:<20} {btype:<15} {real_radius:>8.0f} km {ratio:.6f} {display_size:>10.4f} {vs_earth:>7}")
print("\n" + "=" * 85)
print("✅ VERIFICATION COMPLETE")
print("=" * 85)
print("All bodies now use:")
print(f" • Unified ratio: {configs['planet']['ratio']}")
print(f" • Display Size = real_radius × {configs['planet']['ratio']}")
print("\nSize relationships:")
print(f" • Jupiter = {sizes[0][4] / earth_size:.2f}x Earth (should be ~11x)")
print(f" • Saturn = {sizes[1][4] / earth_size:.2f}x Earth (should be ~9x)")
print(f" • Pluto = {[s for s in sizes if s[0] == 'Pluto'][0][4] / earth_size:.2f}x Earth (should be ~0.19x)")
print(f" • Ceres = {[s for s in sizes if s[0] == 'Ceres'][0][4] / earth_size:.2f}x Earth (should be ~0.07x)")
print("\n🎉 All sizes are now consistent and physically accurate!")
if __name__ == "__main__":
asyncio.run(verify_final_sizes())