From fa657018ec93eb5a9a3c5b515a45d48369a2c11c Mon Sep 17 00:00:00 2001 From: Matt Redmond <10541289+redmond2742@users.noreply.github.com> Date: Sat, 14 Jun 2025 16:07:09 -0700 Subject: [PATCH] Add FastAPI upload endpoints --- dev-requirements.in | 3 ++ dev-requirements.txt | 93 ++++++++++++++++++++++++++++++++++++++---- requirements.in | 3 ++ requirements.txt | 96 ++++++++++++++++++++++++++++++++++++++++---- src/ssoss/api.py | 37 +++++++++++++++++ 5 files changed, 217 insertions(+), 15 deletions(-) create mode 100644 src/ssoss/api.py diff --git a/dev-requirements.in b/dev-requirements.in index d895d1d..98e68ac 100644 --- a/dev-requirements.in +++ b/dev-requirements.in @@ -15,3 +15,6 @@ icecream click geopandas folium + +fastapi +uvicorn diff --git a/dev-requirements.txt b/dev-requirements.txt index 35a4e54..fb1bcd0 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -4,59 +4,138 @@ # # pip-compile dev-requirements.in # +annotated-types==0.7.0 + # via pydantic +anyio==4.9.0 + # via starlette asttokens==2.4.0 # via icecream +branca==0.8.1 + # via folium +certifi==2025.4.26 + # via + # pyogrio + # pyproj + # requests +cffi==1.17.1 + # via timezonefinder +charset-normalizer==3.4.2 + # via requests +click==8.2.1 + # via + # -r dev-requirements.in + # uvicorn colorama==0.4.6 # via icecream executing==2.0.0 # via icecream +fastapi==0.115.12 + # via -r dev-requirements.in +folium==0.19.7 + # via -r dev-requirements.in geographiclib==2.0 # via geopy +geopandas==1.1.0 + # via -r dev-requirements.in geopy==2.4.0 # via -r dev-requirements.in gpxpy==1.5.0 # via -r dev-requirements.in +h11==0.16.0 + # via uvicorn +h3==4.2.2 + # via timezonefinder icecream==2.1.3 # via -r dev-requirements.in +idna==3.10 + # via + # anyio + # requests imageio==2.31.5 # via -r dev-requirements.in +jinja2==3.1.6 + # via + # branca + # folium lxml==4.9.3 # via -r dev-requirements.in +markupsafe==3.0.2 + # via jinja2 numpy==1.26.0 # via # -r dev-requirements.in + # folium + # geopandas # imageio # opencv-python # pandas + # pyogrio + # shapely + # timezonefinder opencv-python==4.8.1.78 # via -r dev-requirements.in +packaging==25.0 + # via + # geopandas + # pyogrio pandas==2.1.1 + # via + # -r dev-requirements.in + # geopandas +piexif==1.1.3 # via -r dev-requirements.in pillow==10.0.1 # via # -r dev-requirements.in # imageio +pycparser==2.22 + # via cffi +pydantic==2.11.7 + # via fastapi +pydantic-core==2.33.2 + # via pydantic pygments==2.16.1 # via icecream +pyogrio==0.11.0 + # via geopandas +pyproj==3.7.1 + # via geopandas python-dateutil==2.8.2 # via # -r dev-requirements.in # pandas pytz==2023.3.post1 # via pandas +requests==2.32.4 + # via folium +shapely==2.1.1 + # via geopandas six==1.16.0 # via # asttokens # python-dateutil +sniffio==1.3.1 + # via anyio +starlette==0.46.2 + # via fastapi +timezonefinder==6.5.9 + # via -r dev-requirements.in tqdm==4.66.1 # via -r dev-requirements.in +typing-extensions==4.14.0 + # via + # anyio + # fastapi + # pydantic + # pydantic-core + # typing-inspection +typing-inspection==0.4.1 + # via pydantic tzdata==2023.3 # via pandas -timezonefinder==6.5.9 - # via -r dev-requirements.in -click==8.2.1 - # via -r dev-requirements.in -geopandas==1.1.0 - # via -r dev-requirements.in -folium==0.19.7 +urllib3==2.4.0 + # via requests +uvicorn==0.34.3 # via -r dev-requirements.in +xyzservices==2025.4.0 + # via folium diff --git a/requirements.in b/requirements.in index 83c1ab2..e467150 100644 --- a/requirements.in +++ b/requirements.in @@ -15,3 +15,6 @@ click geopandas folium + +fastapi +uvicorn diff --git a/requirements.txt b/requirements.txt index c359b01..8b89a8c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,48 +2,128 @@ # This file is autogenerated by pip-compile with Python 3.11 # by the following command: # -# pip-compile +# pip-compile requirements.in # +annotated-types==0.7.0 + # via pydantic +anyio==4.9.0 + # via starlette +branca==0.8.1 + # via folium +certifi==2025.4.26 + # via + # pyogrio + # pyproj + # requests +cffi==1.17.1 + # via timezonefinder +charset-normalizer==3.4.2 + # via requests +click==8.2.1 + # via + # -r requirements.in + # uvicorn +fastapi==0.115.12 + # via -r requirements.in +folium==0.19.7 + # via -r requirements.in geographiclib==2.0 # via geopy +geopandas==1.1.0 + # via -r requirements.in geopy==2.4.0 # via -r requirements.in gpxpy==1.5.0 # via -r requirements.in +h11==0.16.0 + # via uvicorn +h3==4.2.2 + # via timezonefinder +idna==3.10 + # via + # anyio + # requests imageio==2.31.5 # via -r requirements.in +jinja2==3.1.6 + # via + # branca + # folium lxml==4.9.3 # via -r requirements.in -numpy>=1.26,<2.0 +markupsafe==3.0.2 + # via jinja2 +numpy==2.3.0 # via # -r requirements.in + # folium + # geopandas # imageio # opencv-python # pandas + # pyogrio + # shapely + # timezonefinder opencv-python==4.8.1.78 # via -r requirements.in +packaging==25.0 + # via + # geopandas + # pyogrio pandas==2.1.1 + # via + # -r requirements.in + # geopandas +piexif==1.1.3 # via -r requirements.in pillow==10.0.1 # via # -r requirements.in # imageio -piexif==1.1.3 - # via -r requirements.in +pycparser==2.22 + # via cffi +pydantic==2.11.7 + # via fastapi +pydantic-core==2.33.2 + # via pydantic +pyogrio==0.11.0 + # via geopandas +pyproj==3.7.1 + # via geopandas python-dateutil==2.8.2 # via # -r requirements.in # pandas pytz==2023.3.post1 # via pandas +requests==2.32.4 + # via folium +shapely==2.1.1 + # via geopandas six==1.16.0 # via python-dateutil +sniffio==1.3.1 + # via anyio +starlette==0.46.2 + # via fastapi +timezonefinder==6.5.9 + # via -r requirements.in tqdm==4.66.1 # via -r requirements.in +typing-extensions==4.14.0 + # via + # anyio + # fastapi + # pydantic + # pydantic-core + # typing-inspection +typing-inspection==0.4.1 + # via pydantic tzdata==2023.3 # via pandas -timezonefinder==6.5.9 -click==8.2.1 -geopandas==1.1.0 -folium==0.19.7 +urllib3==2.4.0 + # via requests +uvicorn==0.34.3 # via -r requirements.in +xyzservices==2025.4.0 + # via folium diff --git a/src/ssoss/api.py b/src/ssoss/api.py new file mode 100644 index 0000000..0a377f6 --- /dev/null +++ b/src/ssoss/api.py @@ -0,0 +1,37 @@ +from fastapi import FastAPI, UploadFile, File, HTTPException +from pathlib import Path +import shutil + +app = FastAPI() + +UPLOAD_DIR = Path("uploads") +UPLOAD_DIR.mkdir(parents=True, exist_ok=True) + + +def _save_upload(upload: UploadFile, allowed_exts: set[str]): + ext = Path(upload.filename).suffix.lower() + if ext not in allowed_exts: + raise HTTPException(status_code=400, detail="Invalid file type") + dest = UPLOAD_DIR / upload.filename + with dest.open("wb") as buffer: + shutil.copyfileobj(upload.file, buffer) + return {"filename": upload.filename} + + +@app.post("/upload/csv") +async def upload_csv(file: UploadFile = File(...)): + """Accept a CSV file upload.""" + return _save_upload(file, {".csv"}) + + +@app.post("/upload/gpx") +async def upload_gpx(file: UploadFile = File(...)): + """Accept a GPX file upload.""" + return _save_upload(file, {".gpx"}) + + +@app.post("/upload/video") +async def upload_video(file: UploadFile = File(...)): + """Accept a video file upload.""" + # Accept a few common video extensions + return _save_upload(file, {".mp4", ".mov", ".avi", ".mkv"})