2025-12-04 13:43:57 -07:00
|
|
|
from fastapi import FastAPI, File, UploadFile
|
2025-12-22 16:15:50 -07:00
|
|
|
from fastapi.responses import FileResponse, PlainTextResponse, JSONResponse
|
2025-12-04 13:43:57 -07:00
|
|
|
from fastapi.middleware.cors import CORSMiddleware
|
2025-12-22 16:15:50 -07:00
|
|
|
from pydantic import BaseModel
|
2025-12-04 13:43:57 -07:00
|
|
|
import zipfile
|
|
|
|
|
import os
|
|
|
|
|
import shutil
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
from qc_validator import validate_shapefiles
|
2025-12-22 16:15:50 -07:00
|
|
|
from verofy_uploader import upload_to_verofy
|
2025-12-04 13:43:57 -07:00
|
|
|
|
|
|
|
|
app = FastAPI()
|
|
|
|
|
|
|
|
|
|
# Enable CORS for frontend
|
|
|
|
|
app.add_middleware(
|
|
|
|
|
CORSMiddleware,
|
|
|
|
|
allow_origins=["*"],
|
|
|
|
|
allow_credentials=True,
|
|
|
|
|
allow_methods=["*"],
|
|
|
|
|
allow_headers=["*"],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
TEMP_DIR = Path("../temp")
|
|
|
|
|
TEMP_DIR.mkdir(exist_ok=True)
|
|
|
|
|
|
2025-12-22 16:15:50 -07:00
|
|
|
|
|
|
|
|
class VerofyMapRequest(BaseModel):
|
|
|
|
|
mapId: int
|
|
|
|
|
verofyEmail: str
|
|
|
|
|
verofyPassword: str
|
|
|
|
|
|
2025-12-04 13:43:57 -07:00
|
|
|
@app.post("/upload")
|
|
|
|
|
async def upload_shapefile(file: UploadFile = File(...)):
|
|
|
|
|
"""Handle shapefile ZIP upload and QC validation"""
|
|
|
|
|
|
|
|
|
|
# Clear temp directory
|
|
|
|
|
for item in TEMP_DIR.glob("*"):
|
|
|
|
|
if item.is_file():
|
|
|
|
|
item.unlink()
|
|
|
|
|
elif item.is_dir():
|
|
|
|
|
shutil.rmtree(item)
|
|
|
|
|
|
|
|
|
|
# Save uploaded file
|
|
|
|
|
zip_path = TEMP_DIR / file.filename
|
|
|
|
|
with open(zip_path, "wb") as f:
|
|
|
|
|
content = await file.read()
|
|
|
|
|
f.write(content)
|
|
|
|
|
|
|
|
|
|
# Unzip file
|
|
|
|
|
try:
|
|
|
|
|
with zipfile.ZipFile(zip_path, 'r') as zip_ref:
|
|
|
|
|
zip_ref.extractall(TEMP_DIR)
|
2025-12-22 16:15:50 -07:00
|
|
|
|
|
|
|
|
# Check if files are in a subdirectory and flatten if needed
|
|
|
|
|
shp_files = list(TEMP_DIR.glob("*.shp"))
|
|
|
|
|
if len(shp_files) == 0:
|
|
|
|
|
# Look for shapefiles in subdirectories
|
|
|
|
|
subdirs = [d for d in TEMP_DIR.iterdir() if d.is_dir()]
|
|
|
|
|
if len(subdirs) == 1:
|
|
|
|
|
# Move all files from subdirectory to temp root
|
|
|
|
|
subdir = subdirs[0]
|
|
|
|
|
for item in subdir.iterdir():
|
|
|
|
|
shutil.move(str(item), str(TEMP_DIR / item.name))
|
|
|
|
|
# Remove empty subdirectory
|
|
|
|
|
subdir.rmdir()
|
2025-12-04 13:43:57 -07:00
|
|
|
except Exception as e:
|
|
|
|
|
return PlainTextResponse(f"Error extracting ZIP file: {str(e)}", status_code=400)
|
|
|
|
|
|
|
|
|
|
# Run QC validation
|
|
|
|
|
qc_result = validate_shapefiles(TEMP_DIR)
|
|
|
|
|
|
|
|
|
|
if qc_result["passed"]:
|
|
|
|
|
return {"message": "success"}
|
|
|
|
|
else:
|
|
|
|
|
# Generate QC report
|
|
|
|
|
report_path = TEMP_DIR / "QC_report.txt"
|
|
|
|
|
with open(report_path, "w") as f:
|
|
|
|
|
f.write("QC VALIDATION FAILED\n")
|
|
|
|
|
f.write("=" * 50 + "\n\n")
|
|
|
|
|
for error in qc_result["errors"]:
|
|
|
|
|
f.write(f"{error}\n")
|
|
|
|
|
|
|
|
|
|
return FileResponse(
|
|
|
|
|
path=report_path,
|
|
|
|
|
media_type="text/plain",
|
|
|
|
|
filename="QC_report.txt"
|
|
|
|
|
)
|
|
|
|
|
|
2025-12-22 16:15:50 -07:00
|
|
|
@app.post("/push-to-verofy")
|
|
|
|
|
async def push_to_verofy(request: VerofyMapRequest):
|
|
|
|
|
"""Push shapefiles from temp folder to Verofy"""
|
|
|
|
|
|
|
|
|
|
# Use credentials from the request
|
|
|
|
|
verofy_email = request.verofyEmail
|
|
|
|
|
verofy_password = request.verofyPassword
|
|
|
|
|
|
|
|
|
|
if not verofy_email or not verofy_password:
|
|
|
|
|
return JSONResponse(
|
|
|
|
|
status_code=400,
|
|
|
|
|
content={
|
|
|
|
|
"success": False,
|
|
|
|
|
"error": "Verofy credentials are required. Please provide your email and password."
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Check if temp directory has shapefiles
|
|
|
|
|
shapefile_count = len(list(TEMP_DIR.glob("*.shp")))
|
|
|
|
|
if shapefile_count == 0:
|
|
|
|
|
return JSONResponse(
|
|
|
|
|
status_code=400,
|
|
|
|
|
content={
|
|
|
|
|
"success": False,
|
|
|
|
|
"error": "No shapefiles found in temp directory. Please upload files first."
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
# Upload to Verofy
|
|
|
|
|
try:
|
|
|
|
|
result = upload_to_verofy(
|
|
|
|
|
temp_dir=str(TEMP_DIR),
|
|
|
|
|
map_id=request.mapId,
|
|
|
|
|
email=verofy_email,
|
|
|
|
|
password=verofy_password,
|
|
|
|
|
limit=None # Upload ALL records (no limit)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if result["success"]:
|
|
|
|
|
return JSONResponse(
|
|
|
|
|
status_code=200,
|
|
|
|
|
content={
|
|
|
|
|
"success": True,
|
|
|
|
|
"message": "Successfully uploaded to Verofy",
|
|
|
|
|
"uploaded": result["uploaded"],
|
|
|
|
|
"errors": result.get("errors", [])
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
else:
|
|
|
|
|
return JSONResponse(
|
|
|
|
|
status_code=500,
|
|
|
|
|
content={
|
|
|
|
|
"success": False,
|
|
|
|
|
"error": "Upload to Verofy failed",
|
|
|
|
|
"details": result.get("errors", [])
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
return JSONResponse(
|
|
|
|
|
status_code=500,
|
|
|
|
|
content={
|
|
|
|
|
"success": False,
|
|
|
|
|
"error": f"Error uploading to Verofy: {str(e)}"
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
2025-12-04 13:43:57 -07:00
|
|
|
if __name__ == "__main__":
|
|
|
|
|
import uvicorn
|
|
|
|
|
uvicorn.run(app, host="0.0.0.0", port=8000)
|