Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .devcontainer/api/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Use Python base image for development - using Debian Bookworm (stable)
FROM mcr.microsoft.com/devcontainers/python:3.10-bookworm

# Set timezone
ENV TZ="Europe/Helsinki"
ENV PYTHONUNBUFFERED=1

# Install system dependencies including GDAL for geopandas
RUN apt-get update && apt-get install -y \
postgresql-client \
curl \
wget \
git \
gdal-bin \
libgdal-dev \
libgeos-dev \
libproj-dev \
libspatialindex-dev \
build-essential \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/*

# Install Azure Functions Core Tools (only on AMD64, skip on ARM64/Apple Silicon)
RUN ARCH=$(dpkg --print-architecture) && \
if [ "$ARCH" = "amd64" ]; then \
curl https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg && \
mv microsoft.gpg /etc/apt/trusted.gpg.d/microsoft.gpg && \
sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/debian/12/prod bookworm main" > /etc/apt/sources.list.d/dotnetdev.list' && \
apt-get update && \
apt-get install -y azure-functions-core-tools-4 && \
apt-get clean -y && \
rm -rf /var/lib/apt/lists/*; \
else \
echo "Azure Functions Core Tools not available for $ARCH architecture. Skipping installation."; \
fi

# Set working directory
WORKDIR /workspace

# Install Python dependencies
COPY python/requirements.txt /tmp/requirements.txt

# Set GDAL environment variables for building Python packages
ENV GDAL_CONFIG=/usr/bin/gdal-config
ENV CPLUS_INCLUDE_PATH=/usr/include/gdal
ENV C_INCLUDE_PATH=/usr/include/gdal

RUN pip install --upgrade pip \
&& pip install debugpy pytest pytest-asyncio httpx \
&& pip install -r /tmp/requirements.txt

# Create .vscode-server directories with correct permissions
RUN mkdir -p /home/vscode/.vscode-server/bin \
&& mkdir -p /home/vscode/.vscode-server/data/Machine \
&& mkdir -p /home/vscode/.vscode-server/extensions \
&& mkdir -p /home/vscode/.vscode-server-insiders/bin \
&& mkdir -p /home/vscode/.vscode-server-insiders/extensions \
&& chown -R vscode:vscode /home/vscode

# The vscode user already exists in the base image
# Switch to non-root user
USER vscode
95 changes: 95 additions & 0 deletions .devcontainer/api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# HFP Analytics API Development Container

This devcontainer provides a complete development environment for the HFP Analytics FastAPI application.

## What's Included

- **Python 3.10** with all project dependencies
- **Azure Functions Core Tools** for Azure development (AMD64 only)
- **PostgreSQL client** for database operations
- **TimescaleDB** database instance
- **Azurite** for local Azure Storage emulation
- **VS Code extensions** for Python, Azure, Docker, and more
- **ARM64 (Apple Silicon) support** - works on M1/M2/M3 Macs

## Getting Started

1. **Open in Dev Container**
- Open this folder in VS Code
- Press `F1` and select "Dev Containers: Reopen in Container"
- Wait for the container to build and start

2. **Run the FastAPI App**
```bash
cd python
uvicorn api.main:app --host 0.0.0.0 --port 8000 --reload
```

Or use the VS Code task: `Terminal > Run Task > Run FastAPI Development Server`

3. **Access the Application**
- FastAPI: http://localhost:8000
- API Docs: http://localhost:8000/docs
- PostgreSQL: localhost:5433 (user: postgres, password: postgres)
- Azurite Blob: http://localhost:10100
- Azurite Queue: http://localhost:10101
- Azurite Table: http://localhost:10102

## Debugging

Use the built-in debugger:
1. Set breakpoints in your code
2. Press `F5` or go to "Run and Debug"
3. Select "Python: FastAPI" configuration

## Database Setup

Initialize the database schema:
```bash
psql -h localhost -U postgres -d analytics -f db/sql/100_create_global_objects.sql
```

Or use the VS Code task: `Terminal > Run Task > Database: Run Migrations`

## Running Tests

```bash
cd python
pytest
```

Or use the VS Code task: `Terminal > Run Task > Run Tests`

## Tips

- All changes to Python files trigger auto-reload
- Use `.env` file for environment variables
- The workspace is mounted at `/workspace`
- Extensions and settings are pre-configured

## Ports

| Service | Port | Description |
|---------|------|-------------|
| FastAPI | 8000 | Main API server |
| PostgreSQL | 5433 | TimescaleDB database (remapped to avoid conflicts) |
| Azurite Blob | 10100 | Azure Blob Storage emulator (remapped) |
| Azurite Queue | 10101 | Azure Queue Storage emulator (remapped) |
| Azurite Table | 10102 | Azure Table Storage emulator (remapped) |

## Troubleshooting

**Container won't start:**
- Make sure Docker is running
- Check if ports 5433, 8000, 10100, 10101, 10102 are available
- Port 5433 is used instead of 5432 to avoid conflicts with local PostgreSQL
- Ports 10100-10102 are used for Azurite to avoid conflicts with main docker-compose
- Try rebuilding: `F1 > Dev Containers: Rebuild Container`

**Database connection issues:**
- Wait for the database health check to pass (about 10-15 seconds)
- Check connection string in `.env` file

**Import errors:**
- Make sure PYTHONPATH is set correctly: `export PYTHONPATH=/workspace/python`
- Reinstall dependencies: `pip install -r python/requirements.txt`
100 changes: 100 additions & 0 deletions .devcontainer/api/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
{
"name": "HFP Analytics API (FastAPI)",
"dockerComposeFile": [
"../docker-compose.yml"
],
"service": "api",
"workspaceFolder": "/workspace",
"shutdownAction": "stopCompose",

// Forward ports for the API, database, and Azurite
"forwardPorts": [
8000, // FastAPI
5433, // PostgreSQL (remapped to avoid conflict with host)
10100, // Azurite Blob (remapped to avoid conflict)
10101, // Azurite Queue (remapped to avoid conflict)
10102 // Azurite Table (remapped to avoid conflict)
],

"portsAttributes": {
"8000": {
"label": "FastAPI",
"onAutoForward": "notify"
},
"5433": {
"label": "PostgreSQL"
},
"10100": {
"label": "Azurite Blob"
},
"10101": {
"label": "Azurite Queue"
},
"10102": {
"label": "Azurite Table"
}
},

"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-python.black-formatter",
"ms-python.isort",
"ms-python.debugpy",
"ms-azuretools.vscode-azurefunctions",
"ms-azuretools.vscode-docker",
"mtxr.sqltools",
"mtxr.sqltools-driver-pg",
"GitHub.copilot",
"esbenp.prettier-vscode"
],
"settings": {
"python.defaultInterpreterPath": "/usr/local/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": false,
"python.formatting.provider": "black",
"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
},
"[python]": {
"editor.defaultFormatter": "ms-python.python"
},
"launch": {
"configurations": [],
"compounds": []
},
"tasks": {
"version": "2.0.0",
"tasks": []
}
}
}
},

// Commands to run after container is created
"postCreateCommand": "pip install -r python/requirements.txt && pip install debugpy pytest pytest-asyncio httpx && mkdir -p /workspace/.vscode && cp /workspace/.devcontainer/shared/launch.json /workspace/.vscode/launch.json && cp /workspace/.devcontainer/shared/tasks.json /workspace/.vscode/tasks.json",

// Commands to run when attaching to existing container
"postAttachCommand": "echo 'Container attached. Run: cd python && uvicorn api.main:app --host 0.0.0.0 --port 8000 --reload'",

// Features to add to the container
"features": {
"ghcr.io/devcontainers/features/git:1": {},
"ghcr.io/devcontainers/features/github-cli:1": {}
},

// Set environment variables
"containerEnv": {
"PYTHONPATH": "/workspace/python",
"PYTHONUNBUFFERED": "1",
"TZ": "Europe/Helsinki"
},

// Run as non-root user
"remoteUser": "vscode"
}
51 changes: 51 additions & 0 deletions .devcontainer/api/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Python: FastAPI",
"type": "debugpy",
"request": "launch",
"module": "uvicorn",
"args": [
"api.main:app",
"--host",
"0.0.0.0",
"--port",
"8000",
"--reload"
],
"cwd": "${workspaceFolder}/python",
"env": {
"PYTHONPATH": "${workspaceFolder}/python"
},
"jinja": true,
"justMyCode": false
},
{
"name": "Python: Current File",
"type": "debugpy",
"request": "launch",
"program": "${file}",
"console": "integratedTerminal",
"cwd": "${workspaceFolder}/python",
"env": {
"PYTHONPATH": "${workspaceFolder}/python"
}
},
{
"name": "Python: Pytest",
"type": "debugpy",
"request": "launch",
"module": "pytest",
"args": [
"-v",
"${file}"
],
"console": "integratedTerminal",
"cwd": "${workspaceFolder}/python",
"env": {
"PYTHONPATH": "${workspaceFolder}/python"
}
}
]
}
36 changes: 36 additions & 0 deletions .devcontainer/api/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Run FastAPI Development Server",
"type": "shell",
"command": "cd python && uvicorn api.main:app --host 0.0.0.0 --port 8000 --reload",
"problemMatcher": [],
"presentation": {
"reveal": "always",
"panel": "new"
},
"isBackground": true,
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "Run Tests",
"type": "shell",
"command": "cd python && pytest",
"problemMatcher": [],
"presentation": {
"reveal": "always",
"panel": "shared"
}
},
{
"label": "Database: Run Migrations",
"type": "shell",
"command": "psql -h localhost -U postgres -d analytics -f db/sql/100_create_global_objects.sql",
"problemMatcher": []
}
]
}
Loading