Skip to content
Merged
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
10 changes: 10 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
.git
.github
.venv
.vscode
__pycache__
*.pyc
tests/
.dockerignore
.gitignore
README.md
27 changes: 27 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

jobs:
lint-and-test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
pip install poetry
poetry install
- name: Lint
run: poetry run ruff check app/ tests/
- name: Test
run: poetry run pytest tests/ -v
39 changes: 39 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
name: Docker Publish

on:
release:
types: [published]
push:
tags: ["v*"]

env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}

jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- uses: docker/metadata-action@v5
id: meta
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
- uses: docker/build-push-action@v6
with:
context: .
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
15 changes: 4 additions & 11 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
{
"editor.formatOnSave": true,
"python.linting.enabled": true,
"python.linting.lintOnSave": true,
"python.testing.unittestEnabled": true,
"python.testing.pytestEnabled": false,
"jupyter.notebookFileRoot": "${workspaceFolder}",
"python.testing.cwd": "${cwd}",
"python.defaultInterpreterPath": ".venv/bin/python",
"python.formatting.provider": "black",
"python.testing.unittestArgs": ["-v", "-s", "./test", "-p", "*test*.py"],
"restructuredtext.confPath": "${workspaceFolder}/docs"
}

"python.testing.pytestEnabled": true,
"python.testing.unittestEnabled": false,
"python.testing.pytestArgs": ["-v", "tests/"]
}
50 changes: 14 additions & 36 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,43 +1,21 @@
FROM python:3.8
FROM python:3.11-slim

COPY requirements.txt config/config.yaml /project/

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
libatlas-base-dev gfortran nginx supervisor \
&& rm -rf /var/lib/apt/lists/* \
&& pip3 install -r /project/requirements.txt \
&& rm -r /root/.cache

ARG uid=210
ARG gid=210

RUN groupadd -g ${gid} dock_sbtiapi \
&& useradd -u ${uid} -g ${gid} dock_sbtiapi \
&& mkdir /home/dock_sbtiapi \
&& chown -R dock_sbtiapi:dock_sbtiapi /home/dock_sbtiapi

RUN rm /etc/nginx/sites-enabled/default /etc/nginx/sites-available/default \
&& mkdir -p /vol/log/nginx /vol/tmp/nginx \
&& touch /vol/log/nginx/{access.log,error.log} \
&& rm -rf /var/log/nginx \
&& ln -s /vol/log/nginx /var/log/nginx

COPY app /project/app
COPY config/nginx.conf /etc/nginx/nginx.conf
COPY config/api-nginx.conf /etc/nginx/sites-available/api-nginx.conf
COPY config/supervisord.conf /etc/supervisord.conf
COPY app/config.json /project/config.json
COPY app/data /project/data
WORKDIR /project

COPY pyproject.toml poetry.lock* ./
RUN pip install --no-cache-dir poetry \
&& poetry config virtualenvs.create false \
&& poetry install --without dev --no-interaction --no-ansi \
&& pip uninstall -y poetry

RUN ln -s /etc/nginx/sites-available/api-nginx.conf /etc/nginx/sites-enabled/api-nginx.conf \
&& chown -R dock_sbtiapi:dock_sbtiapi /project /vol
COPY app/ ./app/

WORKDIR /project
RUN useradd -m -r appuser && chown -R appuser:appuser /project
USER appuser

USER dock_sbtiapi
EXPOSE 80
CMD ["/usr/bin/supervisord"]
EXPOSE 8000

HEALTHCHECK --interval=30s --timeout=5s --retries=3 \
CMD python -c "import urllib.request; urllib.request.urlopen('http://localhost:8000/health')"

CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000", "--workers", "2"]
163 changes: 57 additions & 106 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,132 +1,83 @@
> Visit http://getting-started.sbti-tool.org/ for the full documentation

> If you have any additional questions or comments send a mail to: finance@sciencebasedtargets.org

# SBTi Temperature Alignment tool API
This package helps companies and financial institutions to assess the temperature alignment of current
targets, commitments, and investment and lending portfolios, and to use this information to develop
targets for official validation by the SBTi.

Under the hood, this API uses the SBTi Python module. The complete structure that consists of a Python module, API and a UI looks as follows:

+-------------------------------------------------+
| UI : Simple user interface on top of API |
| Install: via dockerhub |
| docker.io/sbti/ui:latest |
| |
| +-----------------------------------------+ |
| | REST API: Dockerized FastAPI/NGINX | |
| | Source : github.com/OFBDABV/SBTi_api | |
| | Install: via source or dockerhub | |
| | docker.io/sbti/sbti/api:latest | |
| | | |
| | +---------------------------------+ | |
| | | | | |
| | |Core : Python Module | | |
| | |Source : github.com/OFBDABV/SBTi | | |
| | |Install: via source or PyPi | | |
| | | | | |
| | +---------------------------------+ | |
| +-----------------------------------------+ |
+-------------------------------------------------+

Note that one can deploy the api also including a User interface. This repo depends on a docker image
(docker.io/sbti/ui:latest) that can be spinned up if necessary, see instruction in the deployment section.

## Structure
The folder structure for this project is as follows:

.
├── .github # Github specific files (Github Actions workflows)
├── app # FastAPI app files for the API endpoints
└── config # Config files for the Docker container

## Deployment
This service can be deployed in two ways, either as a standalone API or in conjunction with a no-frills UI.
For both of these options a docker configuration has been set up.
# SBTi Temperature Alignment Tool — REST API

In order to run the docker container locally or non linux machines one needs to install [Docker Desktop](https://www.docker.com/products/docker-desktop) available for Mac and Windows
REST API for portfolio temperature scoring, coverage analysis, and what-if scenario modeling using the [SBTi Finance Tool](https://github.com/ScienceBasedTargets/SBTi-finance-tool) Python package.

### API-only
The master branch of this repo has a public image at Dockerhub. To run them, use the following commands:

```bash
docker run -d -p 5000:8080 sbti/api:latest # to run the latest stable release
```
In order to run a locally build version run:
## Quickstart

```bash
docker-compose up --build
```

The API swagger documentation should now be available at [http://localhost:5000/docs/](http://localhost:5000/docs/).
API docs available at [http://localhost:8000/docs](http://localhost:8000/docs).

### API and UI
To launch both the API and the UI, you need to use the provided docker-compose files.
This will spin up two containers that work in conjunction with one another.
## Endpoints

To launch the latest release:
```bash
docker-compose -f docker-compose-ui.yml up -d --build
```
| Method | Path | Description |
|--------|------|-------------|
| GET | `/health` | Health check |
| GET | `/health/ready` | Readiness check with version |
| GET | `/v1/data-providers` | List configured data providers |
| POST | `/v1/temperature/score` | Calculate portfolio temperature scores |
| POST | `/v1/coverage` | Calculate portfolio coverage |
| POST | `/v1/temperature/whatif` | Run what-if scenario analysis |
| POST | `/v1/upload/csv` | Upload CSV portfolio and score |
| POST | `/v1/upload/excel` | Upload Excel portfolio and score |
| POST | `/v1/upload/parse` | Parse Excel file to JSON |

To use your local code-base:
```bash
docker-compose -f docker-compose-ui-dev.yml up -d --build
```

The UI should now be available at [http://localhost:5000/](http://localhost:5000/) and check [http://localhost:5001/docs/](http://localhost:5001/docs/) for the API documentation
## Structure

To build an run the docker container locally use the following command:
```bash
docker-compose up -d --build
```
.
├── .github/workflows # CI and Docker publish workflows
├── app/ # FastAPI application
│ ├── main.py # App entry point
│ ├── config.py # Configuration loader
│ ├── config.json # Data provider configuration
│ ├── dependencies.py # Shared utilities
│ ├── routers/ # Endpoint definitions
│ ├── schemas/ # Request/response models
│ └── data/ # Sample data files
├── tests/ # Pytest test suite
├── Dockerfile # Container image
└── docker-compose.yml # Local deployment
```

## Deploy on Amazon Web Services
These instructions assume that you've installed and configured the Amazon [AWS CLI tools](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html) and the [ECS CLI tools](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/ECS_CLI_Configuration.html) with an IAM account that has at least write access to ECS and EC2 and the capability of creating AIM roles.
## Configuration

Data providers are configured in `app/config.json`. Override the config path with the `SBTI_CONFIG_PATH` environment variable.

## Development

1. Configure the cluster. You can update the region and names as you see fit
```bash
ecs-cli configure --cluster sbti-ecs-cluster --region eu-central-1 --config-name sbti-ecs-conf --cfn-stack-name sbti-ecs-stack --default-launch-type ec2
```
2. Create a new key pair. The result of this command is a key. Store this safely as you can later use it to access your instance through SSH.
```bash
aws ec2 create-key-pair --key-name sbti
```
3. Create the instance that'll run the image. Here we used 1 server of type t2.medium. Change this as you see fit.
```bash
ecs-cli up --keypair sbti --capability-iam --size 1 --instance-type t2.medium --cluster-config sbti-ecs-conf
```
4. Update the server and make it run the docker image.
```bash
ecs-cli compose -f docker-compose_aws.yml up --cluster-config sbti-ecs-conf
```
5. Now that the instance is running we can't access it yet. That's because NGINX only listens to localhost. We need to change this to make sure it's accessible on the WWW.
6. Login to the Amazon AWS console
7. Go to the EC2 service
8. In the instance list find the instance running the Docker image
9. Copy the public IP address of the instance
10. In ```config/api-nginx.conf``` update the server name to the public IP.
11. Now we need to rebuild and re-upload the image.
```bash
docker-compose -f docker-compose_aws.yml build --no-cache
docker-compose -f docker-compose_aws.yml push
ecs-cli compose -f docker-compose_aws.yml up --cluster-config sbti-ecs-conf --force-update
```
12. You should now be able to access the API.
# Install dependencies
poetry install

> :warning: This will make the API publicly available on the world wide web! Please note that this API is not protected in any way. Therefore it's recommended to run your instance in a private subnet and only access it through there. Alternatively you can change the security group settings to only allow incoming connections from your local IP or company VPN.
# Run locally
uvicorn app.main:app --reload

## Development
# Run tests
pytest tests/ -v

# Lint
ruff check app/ tests/
```

To set up the local dev environment with all dependencies, [install poetry](https://python-poetry.org/docs/#osx--linux--bashonwindows-install-instructions) and run
## Docker

```bash
poetry install
# Build and run
docker-compose up --build

# Or directly
docker build -t sbti-api .
docker run -p 8000:8000 sbti-api
```

This will create a virtual environment inside the project folder under `.venv`.
The container runs as a non-root user with a built-in health check.

### Updating Dependencies
## Requirements

always run `poetry export -f requirements.txt --output requirements.txt --without-hashes` after updating a dependency to keep the `requirements.txt` file up to date as well.
- Python >= 3.11
- [sbti-finance-tool](https://github.com/ScienceBasedTargets/SBTi-finance-tool) >= 1.2.5
Loading
Loading