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
46 changes: 41 additions & 5 deletions .github/workflows/python-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,6 @@ jobs:
runs-on: ${{ matrix.os }}
timeout-minutes: ${{ (matrix.os == 'windows-latest' && 30) || 15 }}

defaults:
run:
shell: bash -l {0}

strategy:
matrix:
os: [ubuntu-latest, windows-latest]
Expand All @@ -33,6 +29,7 @@ jobs:
conda-solver: libmamba

- name: Install dependencies
shell: bash -l {0}
run: |
pip install poetry poetry-plugin-export
poetry config virtualenvs.create false
Expand All @@ -46,15 +43,54 @@ jobs:

- name: Linting
if: matrix.os == 'ubuntu-latest'
shell: bash -l {0}
run: pre-commit run --files pysus/**/*

- name: Tests
- name: Tests (Linux)
if: matrix.os != 'windows-latest'
shell: bash -l {0}
run: |
poetry run pytest -vv pysus/tests/ --retries 3 --retry-delay 15 --cov=pysus --cov-report=xml:coverage.xml --cov-report=term-missing

- name: Tests (Windows)
if: matrix.os == 'windows-latest'
shell: bash -l {0}
run: |
export DUCKDB_NO_THREADS=1
poetry run pytest -vv pysus/tests/ --retries 3 --retry-delay 15 --timeout=480 -p no:cacheprovider -p no:asyncio

- name: Upload coverage to Codecov
if: matrix.os == 'ubuntu-latest'
uses: codecov/codecov-action@v5
with:
files: ./coverage.xml
fail_ci_if_error: false

docker-tests:
runs-on: ubuntu-latest
timeout-minutes: 15
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v4

- name: Build Docker image
run: docker compose -f docker/docker-compose.yaml build

- name: Start container
run: docker compose -f docker/docker-compose.yaml up -d

- name: Wait for Jupyter
run: |
for i in $(seq 1 10); do
curl -s -o /dev/null http://127.0.0.1:8888 && break
sleep 2
done

- name: Run tests inside container
run: docker compose -f docker/docker-compose.yaml exec -T -w /usr/src jupyter python3 -m pytest -vv pysus/tests/ --retries 3 --retry-delay 15 -x -o cache_dir=/tmp/.pytest_cache

- name: Cleanup
if: always()
run: docker compose -f docker/docker-compose.yaml down -v
53 changes: 45 additions & 8 deletions .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,27 @@ name: release

on:
workflow_dispatch:
inputs:
version:
description: "Version tag for Docker image (e.g., 9.9.9-test)"
required: false
type: string
push:
branches: [ main ]
branches: [main]
tags:
- "[0-9]*"
pull_request:
branches: [ main ]
branches: [main]

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}

jobs:
build:
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
runs-on: ubuntu-latest

defaults:
run:
shell: bash -l {0}

steps:
- uses: actions/checkout@v4

Expand All @@ -44,7 +48,40 @@ jobs:
poetry config pypi-token.pypi ${PYPI_TOKEN}
make release

docker-push:
if: startsWith(github.ref, 'refs/tags/') || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: alertadengue
password: ${{ secrets.DOCKER_TOKEN }}

- name: Extract version
id: version
run: |
if [ -n "${{ inputs.version }}" ]; then
echo "tag=${{ inputs.version }}" >> "$GITHUB_OUTPUT"
else
echo "tag=${GITHUB_REF_NAME#v}" >> "$GITHUB_OUTPUT"
fi

- name: Build and push
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
push: true
tags: |
alertadengue/pysus:latest
alertadengue/pysus:${{ steps.version.outputs.tag }}

docs:
if: ${{ !startsWith(github.ref, 'refs/tags/') }}
runs-on: ubuntu-latest

defaults:
Expand Down
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,29 @@ For the terminal user interface (TUI):
pip install pysus[tui]
```

### Docker

A pre-built JupyterLab image is available on Docker Hub:

```bash
docker pull alertadengue/pysus
docker run -p 8888:8888 alertadengue/pysus
```

Or build locally and start the container:

```bash
docker compose -f docker/docker-compose.yaml up --build
```

Then open [http://127.0.0.1:8888/lab](http://127.0.0.1:8888/lab) in your browser.

Stop the container:

```bash
docker compose -f docker/docker-compose.yaml down
```

## Quick Start

### Simplified Database Functions (New in 2.0)
Expand All @@ -41,7 +64,7 @@ The easiest way to get data as a pandas DataFrame:
```python
from pysus import sinan, sinasc, sim, sih, sia, pni, ibge, cnes, ciha

# Download SINAN Dengue data for 2024
# Download SINAN Dengue data for 2000
df = sinan(disease="deng", year=2000)

# Multiple years
Expand Down Expand Up @@ -238,6 +261,12 @@ Run tests:
pytest tests/
```

Run tests inside the Docker container:

```bash
docker compose -f docker/docker-compose.yaml exec -T -w /usr/src jupyter python3 -m pytest pysus/tests/
```

## License

GPL
49 changes: 18 additions & 31 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,52 +1,39 @@
FROM condaforge/mambaforge
FROM python:3.12-slim

LABEL maintainer="es.loch@gmail.com"

USER root
LABEL maintainer="luabidaa@gmail.com"
LABEL org.opencontainers.image.source="https://github.com/AlertaDengue/PySUS"
LABEL org.opencontainers.image.description="PySUS - Data extraction from Brazilian public health systems"

ENV DEBIAN_FRONTEND=noninteractive

ENV HOME "/home/pysus"
ENV PATH "$PATH:/home/pysus/.local/bin"
ENV ENV_NAME pysus
ENV PATH "/opt/conda/envs/$ENV_NAME/bin:$PATH"
ENV PATH "/opt/poetry/bin:$PATH"

RUN apt-get -qq update --yes \
&& apt-get -qq install --yes --no-install-recommends \
libffi-dev \
build-essential \
firefox \
ca-certificates \
sudo \
curl \
&& rm -rf /var/lib/apt/lists/*

RUN useradd -ms /bin/bash pysus \
&& echo "pysus ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/pysus \
&& chmod 0440 /etc/sudoers.d/ \
&& echo 'source /opt/conda/bin/activate "$ENV_NAME" && exec "$@"' > /activate.sh \
&& echo 'source activate "$ENV_NAME"' > /home/pysus/.bashrc \
&& chmod +x /activate.sh \
&& chmod -R a+rwx /opt/conda /tmp \
&& sudo chown -R pysus:pysus /usr/src

USER pysus
&& chmod 0440 /etc/sudoers.d/pysus \
&& mkdir -p /home/pysus/Notebooks \
&& chown -R pysus:pysus /home/pysus

RUN mkdir -p /home/pysus/Notebooks/
COPY pyproject.toml poetry.lock LICENSE README.md /usr/src/
COPY pysus /usr/src/pysus
COPY docker/scripts/entrypoint.sh /entrypoint.sh
COPY docker/notebooks/ /home/pysus/Notebooks/

COPY --chown=pysus:pysus conda/dev.yaml /tmp/dev.yaml
COPY --chown=pysus:pysus docker/scripts/entrypoint.sh /entrypoint.sh
COPY --chown=pysus:pysus docker/scripts/poetry-install.sh /tmp/poetry-install.sh
COPY --chown=pysus:pysus pyproject.toml poetry.lock LICENSE README.md /usr/src/
COPY --chown=pysus:pysus pysus /usr/src/pysus
COPY --chown=pysus:pysus docs/source/**/*.ipynb /home/pysus/Notebooks/
COPY --chown=pysus:pysus docs/source/data /home/pysus/Notebooks/

RUN mamba env create -n $ENV_NAME --file /tmp/dev.yaml \
&& mamba clean -afy

RUN cd /usr/src/ && bash /tmp/poetry-install.sh
RUN pip install poetry \
&& cd /usr/src && poetry config virtualenvs.create false && poetry install --with docs --extras dbc \
&& pip install 'httpx<0.28' \
&& chown -R pysus:pysus /home/pysus

USER pysus
WORKDIR /home/pysus/Notebooks

ENTRYPOINT ["bash", "/activate.sh", "jupyter", "notebook", "--port=8888", "--ip=0.0.0.0"]
ENTRYPOINT ["/entrypoint.sh"]
4 changes: 2 additions & 2 deletions docker/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
version: '3.3'
services:
jupyter:
build:
context: ".."
dockerfile: docker/Dockerfile
hostname: pysus-jupyter
container_name: pysus-jupyter
ports:
- "8888:8888"
privileged: true
environment:
- DISPLAY=:0
- CI=${CI:-0}
volumes:
- /tmp/.X11-unix:/tmp/.X11-unix
entrypoint: ["/entrypoint.sh"]
command: ["/usr/bin/firefox"]
89 changes: 89 additions & 0 deletions docker/notebooks/Welcome.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Welcome to PySUS\n",
"\n",
"PySUS provides tools for dealing with Brazil's public health data (SINAN, SINASC, SIM, SIH, SIA, PNI, IBGE, CNES, CIHA).\n",
"\n",
"## Quick start\n",
"\n",
"List available datasets and files:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pysus\n",
"\n",
"pysus.list_files()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Fetch SINAN data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pysus import sinan\n",
"\n",
"df = sinan(\"deng\", year=2023)\n",
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Fetch SINASC data"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from pysus import sinasc\n",
"\n",
"df = sinasc(state=\"RJ\", year=2023)\n",
"df.head()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Documentation\n",
"\n",
"- [PySUS GitHub](https://github.com/InfoDengue/PySUS)\n",
"- [PySUS Docs](https://pysus.readthedocs.io/)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"name": "python",
"version": "3.12.0"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
Loading
Loading