from fastapi import APIRouter, Depends, HTTPException, Header, status from fastapi.responses import JSONResponse from sqlalchemy.orm import Session from models import StatusRecord, Device, StatusRecordBatch, SystemSetting from database import get_db from datetime import datetime, timedelta import uuid as uuid_module import random from sqlalchemy.exc import IntegrityError from typing import Dict api_router = APIRouter(prefix="/api", tags=["api"]) def authenticate_device(device_id: str, device_password: str, db: Session = Depends(get_db)): device = db.query(Device).filter(Device.device_id == device_id).first() if not device or device.password != device_password: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid device credentials", ) return device @api_router.post("/generate-data") def generate_data( device_id: str = Header(...), device_password: str = Header(...), db: Session = Depends(get_db), ): authenticate_device(device_id, device_password, db) base_latitude = 35.6837 base_longitude = 139.6805 start_date = datetime(2024, 8, 1) end_date = datetime(2024, 8, 7) delta = end_date - start_date for _ in range(100): random_days = random.randint(0, delta.days) random_seconds = random.randint(0, 86400) random_time = start_date + timedelta(days=random_days, seconds=random_seconds) random_latitude = base_latitude + random.uniform(-0.01, 0.01) random_longitude = base_longitude + random.uniform(-0.01, 0.01) random_connect_status = random.choice([0, 1]) status_record = StatusRecord( device_id=device_id, latitude=random_latitude, longitude=random_longitude, timestamp=random_time, connect_status=random_connect_status, ) db.add(status_record) db.commit() return {"message": "Demo data generated successfully"} @api_router.delete("/delete-data", summary="Delete all status records") def delete_all_data( device_id: str = Header(...), device_password: str = Header(...), db: Session = Depends(get_db), ): """ Delete all status records from the database. Requires device authentication. """ authenticate_device(device_id, device_password, db) try: db.query(StatusRecord).delete() db.commit() return {"message": "All data deleted successfully"} except Exception as e: db.rollback() raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}") @api_router.delete( "/delete-data/{device_id}", summary="Delete status records for a specific device" ) def delete_device_data( device_id: str, auth_device_id: str = Header(...), device_password: str = Header(...), db: Session = Depends(get_db), ): """ Delete status records for a specific device ID. Requires device authentication. """ authenticate_device(auth_device_id, device_password, db) try: deleted_count = ( db.query(StatusRecord).filter(StatusRecord.device_id == device_id).delete() ) db.commit() if deleted_count == 0: return {"message": f"No data found for device ID: {device_id}"} return {"message": f"Data for device ID {device_id} deleted successfully"} except Exception as e: db.rollback() raise HTTPException(status_code=500, detail=f"An error occurred: {str(e)}") @api_router.post("/upload_batch") async def upload_data_batch( records: StatusRecordBatch, device_id: str = Header(...), device_password: str = Header(...), db: Session = Depends(get_db), ): """ Upload multiple status records in a single request. Requires device authentication and unique UUIDs for each record. Uses the device_id from the header for all records. """ authenticate_device(device_id, device_password, db) successful_uploads = 0 failed_uploads = 0 error_messages = [] failed_records = [] for record in records.records: try: # Validate UUID uuid_obj = uuid_module.UUID(record.uuid) # Validate timestamp timestamp_dt = datetime.strptime(record.timestamp, "%Y-%m-%d %H:%M:%S") status_record = StatusRecord( uuid=str(uuid_obj), device_id=device_id, latitude=record.latitude, longitude=record.longitude, timestamp=timestamp_dt, connect_status=record.connect_status, ) db.add(status_record) successful_uploads += 1 except ValueError as ve: failed_uploads += 1 error_messages.append(f"Invalid data format: {str(ve)}") failed_records.append(str(uuid_obj)) except IntegrityError: db.rollback() failed_uploads += 1 error_messages.append(f"Duplicate UUID: {record.uuid}") failed_records.append(str(uuid_obj)) except Exception as e: db.rollback() failed_uploads += 1 error_messages.append(f"Error processing record: {str(e)}") failed_records.append(str(uuid_obj)) try: db.commit() except Exception as e: db.rollback() return JSONResponse( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content={"message": f"Error committing to database: {str(e)}"}, ) return JSONResponse( status_code=status.HTTP_200_OK, content={ "status": "ok", "message": "Batch upload completed", "successful_uploads": successful_uploads, "failed_uploads": failed_uploads, "errors": error_messages, "failed_records": failed_records, }, ) @api_router.get("/health_check", summary="Check if the API is functioning correctly") def health_check( device_id: str = Header(...), device_password: str = Header(...), db: Session = Depends(get_db), ): """ Perform a health check on the API. Requires device authentication. Returns a 200 status code if successful. Returns a 401 Unauthorized error if authentication fails. """ try: authenticate_device(device_id, device_password, db) return JSONResponse(content={"status": "ok"}, status_code=status.HTTP_200_OK) except HTTPException as e: if e.status_code == status.HTTP_401_UNAUTHORIZED: return JSONResponse( content={"status": "error", "detail": "Unauthorized"}, status_code=status.HTTP_401_UNAUTHORIZED, ) raise e @api_router.get( "/config", summary="Get system configuration", response_model=Dict[str, int] ) def get_config( device_id: str = Header(...), device_password: str = Header(...), db: Session = Depends(get_db), ): """ Retrieve the system configuration from SystemSetting. Requires device authentication. """ authenticate_device(device_id, device_password, db) system_setting = db.query(SystemSetting).first() if not system_setting: raise HTTPException(status_code=404, detail="System settings not found") return { "check_connect_period": system_setting.check_connect_period, "data_sync_period": system_setting.data_sync_period, "get_config_period": system_setting.get_config_period, "point_distance": system_setting.point_distance, }