Skip to content

feat: first-run experience and friendly error messages - #321

Draft
luabida wants to merge 14 commits into
AlertaDengue:mainfrom
luabida:improve-impl
Draft

feat: first-run experience and friendly error messages#321
luabida wants to merge 14 commits into
AlertaDengue:mainfrom
luabida:improve-impl

Conversation

@luabida

@luabida luabida commented Aug 20, 2026

Copy link
Copy Markdown
Member

No description provided.

…ngue#320)

* feat: OpenDataSUS catalog client and unified metadata layer

- add pysus.api.saude client for dadosabertos.saude.gov.br: Next.js
  buildId discovery, catalog listing, full CKAN package metadata and
  resource downloads, with offline tests backed by captured fixtures
- introduce the unified metadata architecture: MetadataBag with eight
  typed facets (identity, description, temporal, spatial, provenance,
  structure, access, quality), the MetadataExtractor protocol, and
  per-client extractors for FTP, DadosGov, DuckLake and Saude
- wire .metadata/.ametadata() into every remote entity (clients,
  datasets, groups, files) via MetadataMixin; tabular local files
  compute structure metadata from their content
- document both surfaces in docs/source/guides (saude.rst,
  metadata.rst)

* feat(saude): add source-scoped dataset registry for OpenDataSUS

* chore: ignore waypoints and roadmaps from repo

* chore: include JSONL parsing

* feat(saude): add DEMAS REST query path and JSONL file type

- rest.py: EndpointSpec, fetch_swagger (cached), iter_rows (row-offset
  paginator), endpoints_from_swagger
- JSONL: new file type in types.py, full class in extensions.py with
  load/stream/columns/rows, _detect_jsonl detector, factory registration
- SaudeEndpointFile: BaseRemoteFile subclass for paginated DEMAS endpoints
  with SaudeEndpointFileExtractor for metadata
- SaudeDataset._fetch_content() now returns CKAN groups + endpoint files
- 48 new tests (37 saude + 11 extensions), full suite 1006 passed

* feat(saude): integrate Saude into inventory + compare pipeline

- PySUS.get_saude(): lazy SaudeClient init (no auth required)
- Inventory._collect_saude(): walks SaudeGroup (CKAN) + SaudeEndpointFile
  (DEMAS REST) producing FileRecord objects with format=jsonl
- ORIGINS/DOWNLOAD_PRIORITY: added 'saude' as 4th origin (fallback)
- _ORIGIN_TO_CLIENT: registered 'saude' for collect() dispatch
- Cross-origin identity works naturally via stem_of() normalisation
- 8 new tests, full suite 1014 passed

* feat(saude): wire Saude into sync engine (5.A + 5.B)

- upload_file/download_raw_with_retry: widen type from FTPFile|APIFile
  to BaseRemoteFile (unblocks SaudeFile/SaudeEndpointFile)
- _preconnect_adapters: add 'saude' to origin filter
- _pick_source: add Saude as 3rd fallback (ftp > dadosgov > saude)
- run(): collect Saude inventory, include in comparisons
- _reprocess: Saude passes needs_token check naturally

* feat(saude): add --saude-only flag (5.C)

- SyncEngine.run(): add origins parameter to filter which origins
  are collected (defaults to all 4)
- sync_clients.py: add --saude-only flag, passes origins=('ducklake','saude')
- Fix test to match new run() signature

* test(saude): integration tests for sync engine (5.E)

* docs(saude): mark Stage 5 complete in progress tracker

* feat(saude): DuckLake catalog integration (Stage 6)

- DuckDataset.query(): fix state=NULL handling — Saude files are
  national-only (state IS NULL), IN clause never matches NULL; added
  OR state IS NULL to include them in queries
- test_catalog.py: 6 new Saude integration tests — ensure_dataset
  creates entry, upsert_file with/without state, null-state query
  inclusion, multiple datasets, idempotent ensure_dataset
- _catalog_rows already origin-agnostic (reads file.client.name),
  verified no changes needed
- Stage 6 roadmap written

* feat(saude): column metadata backfill + CHANGELOG (Stage 6.3)

- CHANGELOG.md: document Stages 2-6 under 2.9.0
- pysus/api/saude/schemas.py: YAML schema loader with cached reads
- pysus/api/saude/schemas/arboviroses.yaml: dengue/chikungunya/zika
  column definitions from SINAN documentation
- sync.py _catalog_rows: apply YAML descriptions after link_columns
  when client is saude
- 10 new tests for schema loader + apply_column_descriptions

* docs(saude): tick all rollout checklist items, mark integration complete

* docs(saude): commit stage-specific roadmaps

* chore: remove roadmaps from repo, add roadmap*.md to .gitignore

* style: black formatting

* test(saude): improve coverage to 99% across saude module; fix sphinx CI

- Add 100+ new tests for saude client, catalog, download, metadata,
  models, and resources modules
- New test_metadata.py: full coverage of all 4 metadata extractors
  with all input shapes
- catalog.py: 98% → 100%
- client.py: 91% → 100%
- download.py: 94% → 100%
- metadata.py: 90% → 100%
- models.py: 88% → 99%
- resources.py: 99% → 100%
- Fix sphinx CI: comment out automodule:: pysus.cli in api.rst
  since saude.py is intentionally uncommitted
- Black/isort formatting fixes

* fix(test): chdir to tmp_path for SaudeEndpointFile._download(None) test

The test writes a relative path file when output=None; on CI the CWD
is read-only, causing PermissionError. Fix by chdir to tmp_path.

* fix(test): avoid CWD writes in SaudeFile._download(None) test

The mock download_resource wrote to dest_dir=Path(".") which is
read-only on Docker/CI. Write to tmp_path directly instead.
Phase 0.2 of the UX roadmap:
- First-run welcome message on import (cache path, set_cache hint)
- pysus.info() prints a table of all 27 datasets across FTP/Saude/DadosGov origins
- Friendly error messages with actionable hints on all key exceptions:
  ConnectionError, AuthenticationError, ValidationError, DownloadError,
  FormatError now include hints about network, tokens, env vars, etc.
- 9 new tests for info() and set_cache()
- 1197 total tests passing
@luabida luabida changed the title feat: OpenDataSUS catalog client and unified metadata layer (#320) feat: first-run experience and friendly error messages Aug 20, 2026
luabida added 12 commits August 20, 2026 05:32
- Add pysus/api/progress.py with ProgressCallback wrapping tqdm
- Auto-inject progress bars into download() when no callback given
- Add pysus.enable_progress_bars() / disable_progress_bars() globals
- 10 new tests, 1207 total passing
- Add as_dataframe, columns, dtypes params to PySUS.query()
- When as_dataframe=True: downloads files, converts to Parquet,
  reads via DuckDB, returns single concatenated DataFrame
- columns param selects subset, dtypes overrides column types
- 5 new tests, 1212 total passing
- Add pysus/api/mappings.py with 140+ PT→EN column name mappings
- Add pysus/api/flatten.py with flatten_json_columns() utility
- Add pysus.to_english() and pysus.flatten_json_columns() top-level
- 16 new tests, 1228 total passing
- Add pysus/api/columns.py with ColumnInfo dataclass and search_columns()
- Searches YAML schemas (arboviroses) + SINAN typecast dictionaries
- Exports pysus.search_columns() and pysus.ColumnInfo
- Fix setup.cfg max-line-length to match pre-commit (80)
- 12 new tests, 1240 total passing
- pysus ftp list: list all FTP datasets with descriptions
- pysus ftp search: fuzzy search by name/description
- pysus ftp show: display dataset details (groups, paths)
- pysus ftp files: list available files with filters
- pysus ftp download: download files with filters
- 7 new CLI tests, 1247 total passing
- pysus dadosgov list/search/show/download commands
- Token validation with helpful error messages
- 7 new CLI tests, 1254 total passing
- pysus ducklake list/search/show/download commands
- Mocked tests for offline testing
- 5 new CLI tests, 1259 total passing
- pysus search: cross-origin dataset search with fuzzy suggestions
- pysus info: show all available datasets
- pysus open: web interface alias
- 5 new CLI tests, 1264 total passing
…DATASUS databases

- pysus/api/metadata/columns.py: load_column_metadata(), available_databases(), available_groups()
- YAML schemas for SIH (rd/rj/er), SIA (pa/bi), SIM (do), SINASC (dn)
- SINAN typecast fallback for columns not in YAML
- 13 tests in pysus/tests/api/test_metadata_columns.py
- pysus/api/metadata/cache.py: get/set/invalidate/clear cache in ~/.cache/pysus/metadata/
- MD5-hashed cache keys, 7-day default TTL, JSON serialization
- 10 tests covering roundtrip, expiry, invalidation, corruption handling
- pysus/api/metadata/versioning.py: get_schema_version(), list_schema_versions(), detect_schema_change(), schema_fingerprint()
- Tracks year-to-schema mapping, detects added/removed/changed columns
- 10 tests covering version lookup, change detection, fingerprinting
Exports: metadata (available_databases, load_column_metadata),
quality (missing_values, validate_data, column_stats, quality_score, profile_report),
transform (detect_units, link_datasets, aggregate_*, stream_parquet, mask_data, etc),
export (to_csv, to_excel, to_geojson, to_sql),
diff (diff_dfs, diff_summary, diff_rows)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant