| """ |
| FastAPI Main Application - REMB Optimization Engine |
| """ |
| from fastapi import FastAPI, UploadFile, File, HTTPException |
| from fastapi.middleware.cors import CORSMiddleware |
| from pydantic import BaseModel |
| from typing import Optional, List |
| import logging |
|
|
| from config.settings import settings |
|
|
| |
| logging.basicConfig(level=logging.INFO) |
| logger = logging.getLogger(__name__) |
|
|
| |
| app = FastAPI( |
| title=settings.PROJECT_NAME, |
| version=settings.VERSION, |
| description="AI-Powered Industrial Estate Master Planning Optimization Engine" |
| ) |
|
|
| |
| app.add_middleware( |
| CORSMiddleware, |
| allow_origins=["*"], |
| allow_credentials=True, |
| allow_methods=["*"], |
| allow_headers=["*"], |
| ) |
|
|
|
|
| |
| class OptimizationRequest(BaseModel): |
| """Request model for optimization""" |
| site_id: str |
| population_size: int = 100 |
| n_generations: int = 200 |
| n_plots: int = 20 |
|
|
|
|
| class OptimizationResponse(BaseModel): |
| """Response model for optimization""" |
| optimization_id: str |
| status: str |
| n_solutions: int |
| generation_time_seconds: float |
|
|
|
|
| class LayoutMetricsResponse(BaseModel): |
| """Layout metrics response""" |
| layout_id: str |
| total_area_sqm: float |
| sellable_area_sqm: float |
| green_space_area_sqm: float |
| sellable_ratio: float |
| green_space_ratio: float |
| is_compliant: bool |
|
|
|
|
| @app.get("/") |
| async def root(): |
| """Root endpoint""" |
| return { |
| "message": "REMB Industrial Estate Master Planning Optimization Engine", |
| "version": settings.VERSION, |
| "status": "operational" |
| } |
|
|
|
|
| @app.get("/api/v1/health") |
| async def health_check(): |
| """Health check endpoint""" |
| return { |
| "status": "healthy", |
| "version": settings.VERSION |
| } |
|
|
|
|
| @app.post("/api/v1/sites/upload") |
| async def upload_site_boundary(file: UploadFile = File(...)): |
| """ |
| Upload site boundary file (Shapefile or DXF) |
| |
| Args: |
| file: Shapefile (.shp) or DXF file |
| |
| Returns: |
| Site ID and metadata |
| """ |
| if not file.filename: |
| raise HTTPException(status_code=400, detail="No file provided") |
| |
| |
| extension = file.filename.split('.')[-1].lower() |
| if f".{extension}" not in settings.ALLOWED_EXTENSIONS: |
| raise HTTPException( |
| status_code=400, |
| detail=f"File type .{extension} not allowed. Allowed: {settings.ALLOWED_EXTENSIONS}" |
| ) |
| |
| |
| site_id = "site_123" |
| |
| return { |
| "site_id": site_id, |
| "filename": file.filename, |
| "status": "uploaded", |
| "message": "Site boundary uploaded successfully" |
| } |
|
|
|
|
| @app.post("/api/v1/sites/{site_id}/optimize", response_model=OptimizationResponse) |
| async def optimize_site(site_id: str, request: OptimizationRequest): |
| """ |
| Run optimization for a site |
| |
| Args: |
| site_id: Site identifier |
| request: Optimization parameters |
| |
| Returns: |
| Optimization results with Pareto front |
| """ |
| logger.info(f"Starting optimization for site {site_id}") |
| |
| |
| |
| |
| return OptimizationResponse( |
| optimization_id="opt_123", |
| status="completed", |
| n_solutions=8, |
| generation_time_seconds=120.5 |
| ) |
|
|
|
|
| @app.get("/api/v1/layouts/{layout_id}/metrics", response_model=LayoutMetricsResponse) |
| async def get_layout_metrics(layout_id: str): |
| """ |
| Get metrics for a specific layout |
| |
| Args: |
| layout_id: Layout identifier |
| |
| Returns: |
| Layout metrics |
| """ |
| |
| return LayoutMetricsResponse( |
| layout_id=layout_id, |
| total_area_sqm=250000.0, |
| sellable_area_sqm=162500.0, |
| green_space_area_sqm=37500.0, |
| sellable_ratio=0.65, |
| green_space_ratio=0.15, |
| is_compliant=True |
| ) |
|
|
|
|
| @app.get("/api/v1/layouts/{layout_id}/export") |
| async def export_layout_dxf(layout_id: str): |
| """ |
| Export layout as DXF file |
| |
| Args: |
| layout_id: Layout identifier |
| |
| Returns: |
| DXF file download |
| """ |
| |
| return { |
| "layout_id": layout_id, |
| "status": "exported", |
| "download_url": f"/downloads/{layout_id}.dxf" |
| } |
|
|
|
|
| if __name__ == "__main__": |
| import uvicorn |
| uvicorn.run(app, host="0.0.0.0", port=8000) |
|
|