A fullstack application for interactive analysis and visualization of solar and lunar eclipses, based on NASA's Five Millennium Catalogs. The project separates backend and frontend into independent modules, packages them with Docker, and deploys them to Microsoft Azure via Terraform.
- Overview & Architecture
- Features
- Project Structure
- Prerequisites
- Local Setup & Development
- Running with Docker
- Deploying to Azure
- API Documentation
- Tech Stack
The application consists of two independent services:
-
Backend (FastAPI):
- Reads and cleans data from NASA's catalogs (
lunar.csvandsolar.csv). - Provides a RESTful API with automatic OpenAPI/Swagger documentation.
- Converts NaN values correctly before sending data as JSON over HTTP.
- Reads and cleans data from NASA's catalogs (
-
Frontend (Streamlit):
- An interactive web UI (eClipseBord) that fetches data from the backend API via
httpx. - Separate views per dataset (lunar, solar), plus shared components for what's identical between them.
- KPI cards (
st.metric), interactive charts, and a searchable data table.
- An interactive web UI (eClipseBord) that fetches data from the backend API via
┌───────────────────────┐
│ Browser / Client │
└──────────┬────────────┘
│ Port 8501
▼
┌───────────────────────┐
│ Streamlit Frontend │ (eClipseBord UI)
└──────────┬────────────┘
│ HTTP / REST (httpx)
│ Port 8000
▼
┌───────────────────────┐
│ FastAPI Backend │ (Uvicorn ASGI)
└──────────┬────────────┘
│ Pandas I/O
▼
┌───────────────────────┐
│ CSV Data (NASA) │ (backend/data/lunar.csv, solar.csv)
└───────────────────────┘
In Azure, the backend is deployed as a Container App and the frontend as a Web App for Containers, both provisioned with Terraform.
- Separated architecture: the frontend has no direct file access to the data sources, everything is fetched through API calls.
- Lunar eclipses: KPI overview (total count, average Gamma), relationship analysis between Gamma and total eclipse duration, distribution across eclipse types.
- Solar eclipses: same structure as lunar, with Gamma plotted against magnitude instead of duration.
- Robust error handling: a failed API call (
httpx.RequestError/HTTPStatusError) shows a clear error message in the UI instead of crashing the app. - Cloud-ready: the backend URL is controlled via the
BACKEND_URLenvironment variable, defaulting tohttp://127.0.0.1:8000locally — no configuration needed to run it on your own machine.
azure_python_fullstack_lab/
├── backend/
│ ├── data/
│ │ ├── lunar.csv
│ │ └── solar.csv
│ ├── src/backend/
│ │ ├── api.py # Endpoints
│ │ ├── constants.py
│ │ └── data_processing.py # Loading & cleaning with Pandas
│ └── pyproject.toml
├── frontend/
│ ├── images/
│ │ ├── lunar_eclipse.jpg
│ │ └── solar_eclipse.jpg
│ ├── src/frontend/
│ │ ├── app.py # Entry point (routing & API calls)
│ │ ├── .streamlit/config.toml # Theme
│ │ └── views/
│ │ ├── lunar.py
│ │ ├── solar.py
│ │ ├── gamma.py # Shared Gamma metric
│ │ └── eclipse_type.py # Shared eclipse type chart
│ └── pyproject.toml
├── dockerfiles/
│ ├── backend.dockerfile
│ └── frontend.dockerfile
├── infra/ # Terraform: resource group, ACR, Container App, Web App
│ ├── acr.tf
│ ├── api.tf
│ ├── web_app.tf
│ ├── resource-group.tf
│ ├── providers.tf
│ ├── variables.tf
│ └── outputs.tf
├── eda/
│ └── eda.ipynb
├── .gitignore
├── .python-version
├── docker-compose.yaml
├── pyproject.toml # uv workspace (backend + frontend as members)
└── uv.lock
- Python: 3.13 or later
- Package manager: uv — the project has no
requirements.txt, everything installs through uv. - Docker Desktop (optional, for running in containers)
- Terraform and Azure CLI (only needed for deployment)
git clone https://github.com/rickard-garnau/azure_python_fullstack_lab.git
cd azure_python_fullstack_labcd backend
uv sync
cd src/backend
uv run uvicorn api:app --reloadThe backend responds at http://127.0.0.1:8000, Swagger docs are at http://127.0.0.1:8000/docs.
cd frontend
uv sync
cd src/frontend
uv run streamlit run app.pyThe app opens at http://localhost:8501. No .env file needed — the frontend falls back to http://127.0.0.1:8000 if BACKEND_URL isn't set.
From the repo root:
docker compose up --buildThis builds and starts both services on the same network, with the frontend automatically pointed at http://backend:8000.
./deploy_infra.shThis handles the ACR chicken-and-egg problem automatically: it creates the Container Registry first, builds and pushes the images, then applies the rest of the infrastructure. See the script for the individual steps.
terraform destroy when done — the App Service plan has a fixed hourly cost.
- Swagger UI:
http://127.0.0.1:8000/docs - ReDoc:
http://127.0.0.1:8000/redoc
| Method | Endpoint | Description |
|---|---|---|
GET |
/lunar/data |
Returns NASA's lunar eclipse data as JSON |
GET |
/solar/data |
Returns NASA's solar eclipse data as JSON |
- Language: Python 3.13
- Backend: FastAPI, Uvicorn, Pandas
- Frontend: Streamlit, Pandas, HTTPX
- DevOps & Cloud: Docker, Docker Compose, Terraform, Azure Container Registry, Azure Container App, Azure Web App