From 155eb06f8af18ac37f9743ac354947d5874fad60 Mon Sep 17 00:00:00 2001 From: "mula.liu" Date: Mon, 1 Dec 2025 00:45:43 +0800 Subject: [PATCH] 0.9.9 --- backend/app/api/routes.py | 139 ++++++++++++++++++++++++++++- backend/scripts/add_tasks_menu.sql | 13 +++ 2 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 backend/scripts/add_tasks_menu.sql diff --git a/backend/app/api/routes.py b/backend/app/api/routes.py index 2ac0037..dc746cf 100644 --- a/backend/app/api/routes.py +++ b/backend/app/api/routes.py @@ -1327,8 +1327,8 @@ class DownloadPositionRequest(BaseModel): dates: list[str] # List of dates in YYYY-MM-DD format -@router.post("/positions/download") -async def download_positions( +@router.post("/positions/download-async") +async def download_positions_async( request: DownloadPositionRequest, background_tasks: BackgroundTasks, db: AsyncSession = Depends(get_db) @@ -1359,6 +1359,141 @@ async def download_positions( } +@router.post("/positions/download") +async def download_positions( + request: DownloadPositionRequest, + db: AsyncSession = Depends(get_db) +): + """ + Download position data for specified bodies on specified dates (Synchronous) + + This endpoint will: + 1. Query NASA Horizons API for the position at 00:00:00 UTC on each date + 2. Save the data to the positions table + 3. Return the downloaded data + + Args: + - body_ids: List of celestial body IDs + - dates: List of dates (YYYY-MM-DD format) + + Returns: + - Summary of downloaded data with success/failure status + """ + logger.info(f"Downloading positions (sync) for {len(request.body_ids)} bodies on {len(request.dates)} dates") + + try: + results = [] + total_success = 0 + total_failed = 0 + + for body_id in request.body_ids: + # Check if body exists + body = await celestial_body_service.get_body_by_id(body_id, db) + if not body: + results.append({ + "body_id": body_id, + "status": "failed", + "error": "Body not found" + }) + total_failed += 1 + continue + + body_results = { + "body_id": body_id, + "body_name": body.name_zh or body.name, + "dates": [] + } + + for date_str in request.dates: + try: + # Parse date and set to midnight UTC + target_date = datetime.strptime(date_str, "%Y-%m-%d") + + # Check if data already exists for this date + existing = await position_service.get_positions( + body_id=body_id, + start_time=target_date, + end_time=target_date.replace(hour=23, minute=59, second=59), + session=db + ) + + if existing and len(existing) > 0: + body_results["dates"].append({ + "date": date_str, + "status": "exists", + "message": "Data already exists" + }) + total_success += 1 + continue + + # Download from NASA Horizons + positions = horizons_service.get_body_positions( + body_id=body_id, + start_time=target_date, + end_time=target_date, + step="1d" + ) + + if positions and len(positions) > 0: + # Save to database + position_data = [{ + "time": target_date, + "x": positions[0].x, + "y": positions[0].y, + "z": positions[0].z, + "vx": getattr(positions[0], 'vx', None), + "vy": getattr(positions[0], 'vy', None), + "vz": getattr(positions[0], 'vz', None), + }] + + await position_service.save_positions( + body_id=body_id, + positions=position_data, + source="nasa_horizons", + session=db + ) + + body_results["dates"].append({ + "date": date_str, + "status": "success", + "position": { + "x": positions[0].x, + "y": positions[0].y, + "z": positions[0].z + } + }) + total_success += 1 + else: + body_results["dates"].append({ + "date": date_str, + "status": "failed", + "error": "No data returned from NASA" + }) + total_failed += 1 + + except Exception as e: + logger.error(f"Failed to download {body_id} on {date_str}: {e}") + body_results["dates"].append({ + "date": date_str, + "status": "failed", + "error": str(e) + }) + total_failed += 1 + + results.append(body_results) + + return { + "message": f"Downloaded {total_success} positions ({total_failed} failed)", + "total_success": total_success, + "total_failed": total_failed, + "results": results + } + + except Exception as e: + logger.error(f"Download failed: {e}") + raise HTTPException(status_code=500, detail=str(e)) + + @router.get("/tasks") async def list_tasks( limit: int = 20, diff --git a/backend/scripts/add_tasks_menu.sql b/backend/scripts/add_tasks_menu.sql new file mode 100644 index 0000000..e7a8909 --- /dev/null +++ b/backend/scripts/add_tasks_menu.sql @@ -0,0 +1,13 @@ +-- Insert Tasks menu if it doesn't exist +INSERT INTO menus (name, title, icon, path, component, parent_id, sort_order, is_active) +SELECT 'system_tasks', 'System Tasks', 'schedule', '/admin/tasks', 'admin/Tasks', m.id, 30, true +FROM menus m +WHERE m.name = 'platform_management' +AND NOT EXISTS (SELECT 1 FROM menus WHERE name = 'system_tasks' AND parent_id = m.id); + +-- Assign to admin role +INSERT INTO role_menus (role_id, menu_id, created_at) +SELECT r.id, m.id, NOW() +FROM roles r, menus m +WHERE r.name = 'admin' AND m.name = 'system_tasks' +AND NOT EXISTS (SELECT 1 FROM role_menus rm WHERE rm.role_id = r.id AND rm.menu_id = m.id);