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
28 changes: 16 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ PySUS is a Python package for accessing and analyzing Brazil's public health dat
## What's New in PySUS 2.0

- **Simplified API**: New high-level functions for direct DataFrame access
- **CLI & TUI**: Launch the text-based user interface from command line
- **Streamlit Web UI**: Launch a local web interface for browsing and downloading datasets
- **Flexible Schema Modes**: Read multiple parquet files with union, intersection, or strict modes
- **SQL Query**: Filter catalog queries by dataset, group, state, year, and month

Expand All @@ -20,9 +20,9 @@ PySUS is a Python package for accessing and analyzing Brazil's public health dat
pip install pysus
```

For the terminal user interface (TUI):
For the local Streamlit web interface:
```bash
pip install pysus[tui]
pip install pysus[http]
```

### Docker
Expand Down Expand Up @@ -123,20 +123,24 @@ async def main():
df = pysus.read_parquet(paths, mode="union").df()
```

### Using the TUI (unstable/under testing)
### Using the Streamlit Web UI

Launch the interactive text-based interface:
Launch the local web interface:

```bash
pysus tui -l pt
pysus http
```

Or from Python:
Or with a custom port:

```python
from pysus.tui.app import PySUS
app = PySUS(lang="pt")
app.run()
```bash
pysus http -p 8080
```

Or run directly with Streamlit:

```bash
streamlit run pysus/http/app.py
```

## Features
Expand All @@ -146,7 +150,7 @@ app.run()
- **DuckLake Integration**: S3-compatible cloud storage for parquet catalogs
- **Local Catalog**: SQLite-based tracking of download history to avoid re-downloads
- **Type Inference**: Automatic data type conversion from legacy formats (DBF, DBC)
- **CLI with TUI**: Command-line interface with interactive text-based UI
- **CLI with Streamlit UI**: Command-line interface with local web-based UI

## Architecture

Expand Down
501 changes: 4 additions & 497 deletions poetry.lock

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,10 @@ pyreaddbc = ">=2.0.4"
dotenv = "^0.9.9"
boto3 = "^1.42.89"
typer = "^0.24.1"

humanize = { version = "^4.8.0", optional = true }
textual = { extras = ["syntax"], version = "^8.2.1", optional = true }
humanize = "^4.8.0"

[tool.poetry.extras]
tui = ["textual", "humanize"]
http = ["streamlit"]

[tool.poetry.group.dev.dependencies]
pytest = ">=6.1.0"
Expand Down
52 changes: 38 additions & 14 deletions pysus/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,52 @@


@app.command()
def tui(
lang: str = typer.Option( # noqa
"en",
"-l",
"--lang",
help="Language (en, pt)",
def version():
print(__version__)


@app.command()
def http(
port: int = typer.Option(
8501,
"-p",
"--port",
help="Port to bind the server to",
),
):
"""Launch the local Streamlit visual interface."""
try:
from pysus.tui.app import PySUS
import streamlit.web.bootstrap as bootstrap
from streamlit.runtime.scriptrunner import get_script_run_ctx
except ImportError:
raise ImportError(
"The TUI requires extra dependencies. "
"Install them with: pip install pysus[tui]"
"The HTTP UI requires extra dependencies. "
"Install them with: pip install pysus[http]"
)
app = PySUS(lang=lang)
app.run()
import os
import sys
import webbrowser

app_path = os.path.join(os.path.dirname(__file__), "..", "http", "app.py")
app_path = os.path.abspath(app_path)

@app.command()
def version():
print(__version__)
from streamlit.web import cli as stcli

sys.argv = [
"streamlit",
"run",
app_path,
"--server.port",
str(port),
"--server.headless",
"true",
"--server.address",
"localhost",
]

webbrowser.open(f"http://localhost:{port}")

stcli.main()


if __name__ == "__main__":
Expand Down
3 changes: 3 additions & 0 deletions pysus/http/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""PySUS HTTP — Streamlit-based visual interface for localhost use."""

from pysus.http.app import home # noqa
90 changes: 90 additions & 0 deletions pysus/http/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
"""PySUS Streamlit App — localhost visual interface for the PySUS package.

Run with:
streamlit run pysus/http/app.py
or
pysus http
"""

import asyncio

import streamlit as st

from pysus import __version__
from pysus.http.translations import t
from pysus.api.client import PySUS

LANGUAGES = {"English": "en", "Português": "pt"}
LANG_LABELS = {v: k for k, v in LANGUAGES.items()}

st.set_page_config(
page_title="PySUS",
page_icon=":hospital:",
layout="wide",
)

st.markdown(
"""
<style>
[data-testid="stAppDeployButton"] { display: none; }
</style>
""",
unsafe_allow_html=True,
)


@st.cache_data(show_spinner="Loading datasets...")
def load_catalog() -> None:
async def _fetch():
async with PySUS():
return

return asyncio.run(_fetch())


def _init_lang() -> None:
if "lang" not in st.session_state:
st.session_state.lang = "en"


def _on_lang_change() -> None:
label = st.session_state.get("_lang_select", "English")
st.session_state.lang = LANGUAGES[label]


def _lang_selector() -> None:
_init_lang()
current_label = LANG_LABELS.get(st.session_state.lang, "English")
st.sidebar.selectbox(
t("lang_label", st.session_state.lang),
list(LANGUAGES.keys()),
index=list(LANGUAGES.keys()).index(current_label),
key="_lang_select",
on_change=_on_lang_change,
)


def home() -> None:
catalog = load_catalog()
st.session_state.catalog = catalog

_lang_selector()
lang: str = st.session_state.lang


if __name__ == "__main__":
_init_lang()
lang = st.session_state.lang

home_page = st.Page(home, title=t("home_page", lang), default=True)
datasets_page = st.Page("pages/1_ducklake.py", title="Datasets")
ftp_page = st.Page("pages/2_ftp.py", title="FTP")
dadosgov_page = st.Page("pages/3_dadosgov.py", title="DadosGov")

st.logo(
"https://raw.githubusercontent.com/luabida/PySUS/709a96d0cc9199894a2d9619a0189617d8f46a55/pysus/http/assets/logo_large.svg",
icon_image="https://raw.githubusercontent.com/luabida/PySUS/7a3c210c80c47362d70996c0a005b60321f4bffa/pysus/http/assets/logo.svg",
)

pg = st.navigation([home_page, datasets_page, ftp_page, dadosgov_page])
pg.run()
7 changes: 7 additions & 0 deletions pysus/http/assets/logo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions pysus/http/assets/logo_large.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions pysus/http/pages/1_ducklake.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import streamlit as st

st.title("Datasets")
3 changes: 3 additions & 0 deletions pysus/http/pages/2_ftp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import streamlit as st

st.title("FTP Client")
3 changes: 3 additions & 0 deletions pysus/http/pages/3_dadosgov.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import streamlit as st

st.title("DadosGov Client")
85 changes: 85 additions & 0 deletions pysus/http/translations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
"""Translation dictionaries for the PySUS Streamlit UI."""

from typing import Final

EN: Final[dict[str, str]] = {
"lang_label": "Language",
"home_page": "Home",
"sidebar_title": "Datasets",
"sidebar_select": "Select a dataset",
"home_title": "PySUS",
"home_subtitle": "Tools for dealing with Brazil's Public health data (SUS — Sistema Único de Saúde).",
"coming_soon": "coming soon",
"datasets": "Datasets",
"data_sources": "Data sources",
"about_title": "About PySUS",
"about_intro": "PySUS v{version} — Tools for dealing with Brazil's Public health data (SUS — Sistema Único de Saúde).",
"sinan_desc": "Notifiable Diseases Information System",
"sinasc_desc": "Live Births Information System",
"sim_desc": "Mortality Information System",
"sih_desc": "Hospital Information System",
"sia_desc": "Ambulatory Information System",
"pni_desc": "National Immunization Program",
"ibge_desc": "Brazilian Institute of Geography and Statistics",
"cnes_desc": "National Registry of Health Facilities",
"ciha_desc": "Hospital Admission Communication",
"ducklake_desc": "Modern data lake backend (default).",
"ftp_desc": "Legacy FTP downloads from DATASUS.",
"dadosgov_desc": "Brazilian open-data portal (dados.gov.br).",
"browser_title": "Datasets",
"browser_choose": "Choose a dataset",
"browser_placeholder": "{dataset} browser — coming soon.",
"state": "State",
"year": "Year",
"month": "Month",
"group": "Group",
"fetch": "Fetch data",
}

PT: Final[dict[str, str]] = {
"lang_label": "Idioma",
"home_page": "Início",
"sidebar_title": "Bases de dados",
"sidebar_select": "Selecione uma base",
"home_title": "PySUS",
"home_subtitle": "Ferramentas para dados públicos de saúde do Brasil (SUS — Sistema Único de Saúde).",
"coming_soon": "em breve",
"datasets": "Bases de dados",
"data_sources": "Fontes de dados",
"about_title": "Sobre o PySUS",
"about_intro": "PySUS v{version} — Ferramentas para dados públicos de saúde do Brasil (SUS — Sistema Único de Saúde).",
"sinan_desc": "Sistema de Informação de Agravos de Notificação",
"sinasc_desc": "Sistema de Informações sobre Nascidos Vivos",
"sim_desc": "Sistema de Informação sobre Mortalidade",
"sih_desc": "Sistema de Informações Hospitalares",
"sia_desc": "Sistema de Informações Ambulatoriais",
"pni_desc": "Programa Nacional de Imunizações",
"ibge_desc": "Instituto Brasileiro de Geografia e Estatística",
"cnes_desc": "Cadastro Nacional de Estabelecimentos de Saúde",
"ciha_desc": "Comunicação de Internação Hospitalar e Ambulatorial",
"ducklake_desc": "Backend moderno de data lake (padrão).",
"ftp_desc": "Downloads legados via FTP do DATASUS.",
"dadosgov_desc": "Portal de dados abertos do governo (dados.gov.br).",
"browser_title": "Bases de dados",
"browser_choose": "Escolha uma base",
"browser_placeholder": "Navegador {dataset} — em breve.",
"state": "Estado",
"year": "Ano",
"month": "Mês",
"group": "Grupo",
"fetch": "Baixar dados",
}

TRANSLATIONS: Final[dict[str, dict[str, str]]] = {
"en": EN,
"pt": PT,
}


def t(
key: str, lang: str = "en", **kwargs: str
) -> str:
text = TRANSLATIONS.get(lang, EN).get(key, key)
if kwargs:
text = text.format(**kwargs)
return text
Empty file removed pysus/tui/__init__.py
Empty file.
Loading
Loading