urg-forecast-core 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. urg_forecast_core-0.1.0/.gitignore +40 -0
  2. urg_forecast_core-0.1.0/CHANGELOG.md +48 -0
  3. urg_forecast_core-0.1.0/CITATION.cff +32 -0
  4. urg_forecast_core-0.1.0/CONTRIBUTING.md +72 -0
  5. urg_forecast_core-0.1.0/LICENSE +21 -0
  6. urg_forecast_core-0.1.0/PKG-INFO +287 -0
  7. urg_forecast_core-0.1.0/README.es.md +209 -0
  8. urg_forecast_core-0.1.0/README.md +212 -0
  9. urg_forecast_core-0.1.0/docs/decisions.md +185 -0
  10. urg_forecast_core-0.1.0/docs/roadmap.md +52 -0
  11. urg_forecast_core-0.1.0/pyproject.toml +123 -0
  12. urg_forecast_core-0.1.0/scripts/demo_deis.py +12 -0
  13. urg_forecast_core-0.1.0/scripts/demo_synthetic.py +12 -0
  14. urg_forecast_core-0.1.0/scripts/generate_fixture.py +231 -0
  15. urg_forecast_core-0.1.0/scripts/harness_smoke.py +86 -0
  16. urg_forecast_core-0.1.0/src/urgencias_core/__init__.py +1 -0
  17. urg_forecast_core-0.1.0/src/urgencias_core/_logging.py +32 -0
  18. urg_forecast_core-0.1.0/src/urgencias_core/_optional.py +26 -0
  19. urg_forecast_core-0.1.0/src/urgencias_core/data/__init__.py +0 -0
  20. urg_forecast_core-0.1.0/src/urgencias_core/data/_fixtures/deis_demo_snapshot.README.md +59 -0
  21. urg_forecast_core-0.1.0/src/urgencias_core/data/_fixtures/deis_demo_snapshot.parquet +0 -0
  22. urg_forecast_core-0.1.0/src/urgencias_core/data/_fixtures/synthetic_ed_visits_demo.parquet +0 -0
  23. urg_forecast_core-0.1.0/src/urgencias_core/data/deis.py +477 -0
  24. urg_forecast_core-0.1.0/src/urgencias_core/data/fixtures.py +32 -0
  25. urg_forecast_core-0.1.0/src/urgencias_core/data/loader.py +110 -0
  26. urg_forecast_core-0.1.0/src/urgencias_core/data/timeseries.py +150 -0
  27. urg_forecast_core-0.1.0/src/urgencias_core/demos/__init__.py +9 -0
  28. urg_forecast_core-0.1.0/src/urgencias_core/demos/deis.py +369 -0
  29. urg_forecast_core-0.1.0/src/urgencias_core/demos/server.py +43 -0
  30. urg_forecast_core-0.1.0/src/urgencias_core/demos/synthetic.py +160 -0
  31. urg_forecast_core-0.1.0/src/urgencias_core/eval/__init__.py +0 -0
  32. urg_forecast_core-0.1.0/src/urgencias_core/eval/baselines.py +239 -0
  33. urg_forecast_core-0.1.0/src/urgencias_core/eval/harness.py +171 -0
  34. urg_forecast_core-0.1.0/src/urgencias_core/features/__init__.py +0 -0
  35. urg_forecast_core-0.1.0/src/urgencias_core/features/calendar.py +169 -0
  36. urg_forecast_core-0.1.0/src/urgencias_core/features/weather.py +145 -0
  37. urg_forecast_core-0.1.0/src/urgencias_core/models/__init__.py +0 -0
  38. urg_forecast_core-0.1.0/src/urgencias_core/models/lgb_quantile.py +130 -0
  39. urg_forecast_core-0.1.0/src/urgencias_core/models/protocol.py +97 -0
  40. urg_forecast_core-0.1.0/src/urgencias_core/py.typed +0 -0
  41. urg_forecast_core-0.1.0/src/urgencias_core/server/__init__.py +0 -0
  42. urg_forecast_core-0.1.0/src/urgencias_core/server/app.py +231 -0
  43. urg_forecast_core-0.1.0/src/urgencias_core/server/charts.py +99 -0
  44. urg_forecast_core-0.1.0/src/urgencias_core/server/config.py +101 -0
  45. urg_forecast_core-0.1.0/src/urgencias_core/server/static/style.css +115 -0
  46. urg_forecast_core-0.1.0/src/urgencias_core/server/templates/base.html +26 -0
  47. urg_forecast_core-0.1.0/src/urgencias_core/server/templates/baseline.html +22 -0
  48. urg_forecast_core-0.1.0/src/urgencias_core/server/templates/forecast.html +19 -0
  49. urg_forecast_core-0.1.0/src/urgencias_core/server/templates/index.html +35 -0
  50. urg_forecast_core-0.1.0/src/urgencias_core/server/templates/simulation.html +36 -0
  51. urg_forecast_core-0.1.0/src/urgencias_core/simulation/__init__.py +0 -0
  52. urg_forecast_core-0.1.0/src/urgencias_core/simulation/engine.py +156 -0
  53. urg_forecast_core-0.1.0/src/urgencias_core/simulation/los_empirical.py +111 -0
  54. urg_forecast_core-0.1.0/tests/__init__.py +0 -0
  55. urg_forecast_core-0.1.0/tests/conftest.py +33 -0
  56. urg_forecast_core-0.1.0/tests/fixtures/synthetic_ed_visits.parquet +0 -0
  57. urg_forecast_core-0.1.0/tests/test_baselines.py +64 -0
  58. urg_forecast_core-0.1.0/tests/test_calendar.py +91 -0
  59. urg_forecast_core-0.1.0/tests/test_deis.py +140 -0
  60. urg_forecast_core-0.1.0/tests/test_demos.py +57 -0
  61. urg_forecast_core-0.1.0/tests/test_harness.py +91 -0
  62. urg_forecast_core-0.1.0/tests/test_lgb_quantile.py +41 -0
  63. urg_forecast_core-0.1.0/tests/test_loader.py +74 -0
  64. urg_forecast_core-0.1.0/tests/test_los_empirical.py +67 -0
  65. urg_forecast_core-0.1.0/tests/test_packaging.py +47 -0
  66. urg_forecast_core-0.1.0/tests/test_protocol.py +38 -0
  67. urg_forecast_core-0.1.0/tests/test_server.py +72 -0
  68. urg_forecast_core-0.1.0/tests/test_simulation_engine.py +91 -0
  69. urg_forecast_core-0.1.0/tests/test_smoke.py +7 -0
  70. urg_forecast_core-0.1.0/tests/test_timeseries.py +72 -0
  71. urg_forecast_core-0.1.0/tests/test_weather.py +76 -0
  72. urg_forecast_core-0.1.0/urg-forecast-core.toml.example +25 -0
@@ -0,0 +1,40 @@
1
+ # Python
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ .ruff_cache/
7
+ .mypy_cache/
8
+ .venv/
9
+ venv/
10
+
11
+ # Build
12
+ dist/
13
+ build/
14
+
15
+ # Data and models (reference repo: no real data committed)
16
+ /data/raw/
17
+ /data/processed/
18
+ /data/external/
19
+ /models/
20
+ /outputs/
21
+
22
+ # Parquet files by default are not committed, except the synthetic fixtures
23
+ # (the test fixture and the compact demo fixture shipped inside the package).
24
+ *.parquet
25
+ !tests/fixtures/*.parquet
26
+ !src/urgencias_core/data/_fixtures/*.parquet
27
+
28
+ # Editor / OS
29
+ .DS_Store
30
+ .idea/
31
+ .vscode/
32
+ *.swp
33
+
34
+ # Agent/harness scratch (git worktrees created by tooling)
35
+ .claude/worktrees/
36
+
37
+ # Env
38
+ .env
39
+ .env.*
40
+ !.env.example
@@ -0,0 +1,48 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project
5
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) (API may
6
+ change between minor versions while on 0.x).
7
+
8
+ ## [Unreleased]
9
+
10
+ ## [0.1.0] - 2026-07-20
11
+
12
+ First public release — the reference pipeline packaged for `pip install`.
13
+
14
+ ### Added
15
+
16
+ - Core library: visit-level → hourly occupancy time series (event-cumsum),
17
+ Chilean calendar features, empirical LOS sampler, Monte Carlo occupancy
18
+ simulation, `Forecaster` protocol, seasonal-naive / statsforecast / LightGBM
19
+ quantile forecasters, and an evaluation harness.
20
+ - DEIS MINSAL *Atenciones de Urgencia* fetcher with an offline snapshot
21
+ fallback, bundled inside the package.
22
+ - Minimal FastAPI reference server with server-rendered charts.
23
+ - Console entry points: `urgencias-demo-synthetic`, `urgencias-demo-deis`,
24
+ `urgencias-server`.
25
+ - Optional-dependency extras: `models`, `viz`, `server`, `fetch`, `all`. The
26
+ core install ships only pandas/numpy/pyarrow/holidays/pydantic; gated modules
27
+ raise an actionable error naming the extra to install.
28
+ - `py.typed` marker, PyPI classifiers/keywords/URLs, and packaged demo
29
+ datasets so the server and demos run out of the box from an installed wheel.
30
+ - CI: lint gate, Python 3.11/3.12 test matrix, build + `twine check`, and a
31
+ core-only install job.
32
+
33
+ ### Fixed
34
+
35
+ - Default server fixture resolved a repo-relative path (`parents[3]`) that
36
+ escaped `site-packages` in an installed wheel; it now resolves the bundled
37
+ fixture via `importlib.resources`.
38
+ - DEIS weekly aggregation (`demos.deis._to_weekly`) now trims partial weeks at
39
+ both series edges, not only a fully-empty trailing week. A most-recent week
40
+ with a missing day (near-real-time reporting lag) was kept and appeared as a
41
+ spurious end-of-series drop that contaminated the backtest.
42
+
43
+ ### Changed
44
+
45
+ - Unified formatting on `ruff format` (dropped black).
46
+
47
+ [Unreleased]: https://github.com/nicoveraz/urg-forecast-core/compare/v0.1.0...HEAD
48
+ [0.1.0]: https://github.com/nicoveraz/urg-forecast-core/releases/tag/v0.1.0
@@ -0,0 +1,32 @@
1
+ cff-version: 1.2.0
2
+ message: "If you use this software, please cite it using these metadata."
3
+ title: "urg-forecast-core"
4
+ abstract: >-
5
+ Open reference pipeline for Chilean emergency department analytics,
6
+ probabilistic forecasting, and Monte Carlo occupancy simulation: a
7
+ visits-to-hourly-occupancy transform, Chilean calendar features, a uniform
8
+ quantile Forecaster interface (seasonal-naive, statsforecast, LightGBM), an
9
+ evaluation harness, an empirical-LOS Monte Carlo engine, a DEIS MINSAL
10
+ open-data client, and a minimal reference dashboard.
11
+ type: software
12
+ authors:
13
+ - given-names: "Nicolás"
14
+ family-names: "Vera Zúñiga"
15
+ orcid: "https://orcid.org/0009-0007-9249-3736"
16
+ affiliation: "Eunosia, Frutillar, Chile"
17
+ version: "0.1.0"
18
+ date-released: "2026-07-20"
19
+ license: MIT
20
+ repository-code: "https://github.com/nicoveraz/urg-forecast-core"
21
+ url: "https://pypi.org/project/urg-forecast-core/"
22
+ keywords:
23
+ - emergency department
24
+ - emergency medicine
25
+ - forecasting
26
+ - time series
27
+ - Monte Carlo simulation
28
+ - capacity planning
29
+ - Chile
30
+ - DEIS
31
+ # After the first Zenodo release, add the concept DOI here, e.g.:
32
+ # doi: "10.5281/zenodo.XXXXXXX"
@@ -0,0 +1,72 @@
1
+ # Contributing
2
+
3
+ `urg-forecast-core` is an open foundation maintained on a best-effort basis. Fork
4
+ freely, adapt it to your hospital, ship it inside your own product. Pull requests
5
+ and issues are welcome; while on 0.x the API may change between minor versions.
6
+ If you need commercial support or bespoke work on top of this foundation, contact
7
+ the author.
8
+
9
+ ## Development
10
+
11
+ ```bash
12
+ uv sync --all-extras --dev # full toolchain + all optional deps
13
+ uv run pytest -q # tests
14
+ uv run ruff check . # lint
15
+ uv run ruff format . # format (ruff is the single formatter)
16
+ pre-commit install # optional: run lint/format on commit
17
+ ```
18
+
19
+ The package uses a src layout. Heavy dependencies live behind extras
20
+ (`models`, `viz`, `server`, `fetch`, `all`); modules that need them guard the
21
+ import and raise an actionable error. Keep that pattern when adding code that
22
+ depends on an optional library.
23
+
24
+ ## Cutting a release
25
+
26
+ Releases publish to PyPI via [Trusted Publishing](https://docs.pypi.org/trusted-publishers/)
27
+ (OIDC) from `.github/workflows/publish.yml` — no API tokens as secrets.
28
+
29
+ One-time setup: on PyPI, add this repository and the `pypi` environment as a
30
+ trusted publisher for the `urg-forecast-core` project. Optionally validate metadata
31
+ first by publishing a pre-release (e.g. `0.1.0rc1`) to TestPyPI.
32
+
33
+ Per release:
34
+
35
+ 1. Bump `version` in `pyproject.toml` (semver).
36
+ 2. Move the `## [Unreleased]` notes in `CHANGELOG.md` under a new
37
+ `## [X.Y.Z] - YYYY-MM-DD` heading and update the compare links.
38
+ 3. Commit, then tag and push:
39
+
40
+ ```bash
41
+ git tag vX.Y.Z
42
+ git push origin main --tags
43
+ ```
44
+
45
+ The `publish` workflow then runs tests, verifies the tag matches the package
46
+ version, builds, publishes to PyPI, and creates a GitHub Release with the
47
+ changelog section as its notes.
48
+
49
+ ## Archiving on Zenodo (citable DOI)
50
+
51
+ Releases are archived on [Zenodo](https://zenodo.org/) for a citable DOI.
52
+ Metadata for the archive comes from `.zenodo.json`; `CITATION.cff` provides the
53
+ citation shown on GitHub.
54
+
55
+ One-time setup:
56
+
57
+ 1. Sign in to Zenodo with GitHub and, under *GitHub* settings, flip the switch
58
+ **on** for the `urg-forecast-core` repository.
59
+ 2. Cut a release (the tag flow above creates a GitHub Release). Zenodo detects
60
+ the published GitHub Release, archives the source, and mints two DOIs: a
61
+ **concept DOI** (always resolves to the latest version) and a
62
+ **version DOI** (this specific release).
63
+
64
+ After the first release:
65
+
66
+ 3. Add the concept DOI to `CITATION.cff` (`doi:` field), the README "Citing"
67
+ section (DOI badge), and `paper/paper.md` if submitting to JOSS.
68
+
69
+ For submitting the software paper (JOSS), the archived Zenodo DOI is required at
70
+ submission; draft papers are under `paper/`. Author ORCID and affiliation are
71
+ set in `paper/paper.md`, `CITATION.cff`, and `.zenodo.json`; keep them in sync
72
+ if authorship changes.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Nicolás Vera Z.
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,287 @@
1
+ Metadata-Version: 2.4
2
+ Name: urg-forecast-core
3
+ Version: 0.1.0
4
+ Summary: Reference code for Chilean ED analytics, simulation, and forecasting. Open foundation of Eunosia.
5
+ Project-URL: Homepage, https://github.com/nicoveraz/urg-forecast-core
6
+ Project-URL: Repository, https://github.com/nicoveraz/urg-forecast-core
7
+ Project-URL: Issues, https://github.com/nicoveraz/urg-forecast-core/issues
8
+ Author: Nicolás Vera Z.
9
+ License: MIT License
10
+
11
+ Copyright (c) 2026 Nicolás Vera Z.
12
+
13
+ Permission is hereby granted, free of charge, to any person obtaining a copy
14
+ of this software and associated documentation files (the "Software"), to deal
15
+ in the Software without restriction, including without limitation the rights
16
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17
+ copies of the Software, and to permit persons to whom the Software is
18
+ furnished to do so, subject to the following conditions:
19
+
20
+ The above copyright notice and this permission notice shall be included in all
21
+ copies or substantial portions of the Software.
22
+
23
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29
+ SOFTWARE.
30
+ License-File: LICENSE
31
+ Keywords: chile,deis,emergency-department,forecasting,healthcare,monte-carlo,simulation,time-series
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Intended Audience :: Healthcare Industry
34
+ Classifier: Intended Audience :: Science/Research
35
+ Classifier: License :: OSI Approved :: MIT License
36
+ Classifier: Operating System :: OS Independent
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3.11
39
+ Classifier: Programming Language :: Python :: 3.12
40
+ Classifier: Topic :: Scientific/Engineering
41
+ Classifier: Topic :: Scientific/Engineering :: Information Analysis
42
+ Classifier: Typing :: Typed
43
+ Requires-Python: <3.13,>=3.11
44
+ Requires-Dist: holidays>=0.50
45
+ Requires-Dist: numpy>=1.26
46
+ Requires-Dist: pandas>=2.2
47
+ Requires-Dist: pyarrow>=16.0
48
+ Requires-Dist: pydantic>=2.6
49
+ Provides-Extra: all
50
+ Requires-Dist: fastapi>=0.110; extra == 'all'
51
+ Requires-Dist: httpx>=0.27; extra == 'all'
52
+ Requires-Dist: jinja2>=3.1; extra == 'all'
53
+ Requires-Dist: lightgbm>=4.3; extra == 'all'
54
+ Requires-Dist: matplotlib>=3.8; extra == 'all'
55
+ Requires-Dist: scikit-learn>=1.4; extra == 'all'
56
+ Requires-Dist: statsforecast>=1.7; extra == 'all'
57
+ Requires-Dist: tabulate>=0.9; extra == 'all'
58
+ Requires-Dist: uvicorn>=0.29; extra == 'all'
59
+ Provides-Extra: fetch
60
+ Requires-Dist: httpx>=0.27; extra == 'fetch'
61
+ Provides-Extra: models
62
+ Requires-Dist: lightgbm>=4.3; extra == 'models'
63
+ Requires-Dist: scikit-learn>=1.4; extra == 'models'
64
+ Requires-Dist: statsforecast>=1.7; extra == 'models'
65
+ Provides-Extra: server
66
+ Requires-Dist: fastapi>=0.110; extra == 'server'
67
+ Requires-Dist: jinja2>=3.1; extra == 'server'
68
+ Requires-Dist: matplotlib>=3.8; extra == 'server'
69
+ Requires-Dist: tabulate>=0.9; extra == 'server'
70
+ Requires-Dist: uvicorn>=0.29; extra == 'server'
71
+ Provides-Extra: viz
72
+ Requires-Dist: matplotlib>=3.8; extra == 'viz'
73
+ Requires-Dist: tabulate>=0.9; extra == 'viz'
74
+ Description-Content-Type: text/markdown
75
+
76
+ # urg-forecast-core
77
+
78
+ [![ci](https://github.com/nicoveraz/urg-forecast-core/actions/workflows/ci.yml/badge.svg)](https://github.com/nicoveraz/urg-forecast-core/actions/workflows/ci.yml)
79
+ [![PyPI](https://img.shields.io/pypi/v/urg-forecast-core.svg)](https://pypi.org/project/urg-forecast-core/)
80
+ [![Python](https://img.shields.io/pypi/pyversions/urg-forecast-core.svg)](https://pypi.org/project/urg-forecast-core/)
81
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/nicoveraz/urg-forecast-core/blob/main/LICENSE)
82
+
83
+ Reference code for Chilean emergency department (ED) analytics, simulation, and
84
+ forecasting. Open foundation of **Eunosia**.
85
+
86
+ *Leer en [español](https://github.com/nicoveraz/urg-forecast-core/blob/main/README.es.md).*
87
+
88
+ It turns visit-level ED data into hourly occupancy series, adds Chilean calendar
89
+ features, forecasts arrivals/occupancy with quantile bands, and runs a Monte
90
+ Carlo census simulator — plus a reader for the public DEIS MINSAL dataset and a
91
+ minimal web dashboard.
92
+
93
+ ## Install
94
+
95
+ ```bash
96
+ pip install urg-forecast-core # core library (data, timeseries, features, simulation)
97
+ pip install "urg-forecast-core[all]" # everything: models, viz, server, and data fetchers
98
+ ```
99
+
100
+ The core install is intentionally light (pandas/numpy/pyarrow/holidays/pydantic).
101
+ Heavier capabilities live behind extras — a module that needs one raises a clear
102
+ "install the extra" error:
103
+
104
+ | Extra | Pulls in | Enables |
105
+ |---|---|---|
106
+ | `models` | lightgbm, statsforecast, scikit-learn | LightGBM/statsforecast quantile forecasters |
107
+ | `viz` | matplotlib, tabulate | charts and markdown tables (demos, server) |
108
+ | `server` | fastapi, uvicorn, jinja2 (+ `viz`) | the reference dashboard server |
109
+ | `fetch` | httpx | DEIS MINSAL and Open-Meteo network clients |
110
+ | `all` | all of the above | the demos and the full test suite |
111
+
112
+ ## Quickstart
113
+
114
+ After `pip install "urg-forecast-core[all]"`, three console commands are available.
115
+ They run on datasets bundled with the package and write to `./outputs`:
116
+
117
+ ```bash
118
+ urgencias-demo-synthetic # full pipeline incl. simulation → 3 PNGs
119
+ urgencias-demo-deis --offline # forecasting on real DEIS hospital data
120
+ urgencias-server # reference dashboard at http://127.0.0.1:8000
121
+ ```
122
+
123
+ From a source checkout with [uv](https://docs.astral.sh/uv/):
124
+
125
+ ```bash
126
+ git clone https://github.com/nicoveraz/urg-forecast-core
127
+ cd urg-forecast-core
128
+ uv sync --all-extras
129
+
130
+ uv run urgencias-demo-synthetic
131
+ uv run urgencias-demo-deis --offline
132
+ uv run urgencias-server
133
+ ```
134
+
135
+ The synthetic demo runs in seconds on a compact one-year synthetic fixture
136
+ (~13,000 visits) bundled with the package. The DEIS demo fetches real public
137
+ data (or falls back to the bundled offline snapshot) and writes backtesting
138
+ tables plus 6-month-ahead forecasts.
139
+
140
+ ## What the pipeline produces
141
+
142
+ The figures below are the direct output of running the two demos against the
143
+ bundled data.
144
+
145
+ ### 1. From visits to an hourly occupancy series
146
+
147
+ `urgencias_core.data.timeseries` converts the visit-level parquet (one row per
148
+ visit, with arrival and discharge timestamps) into an hourly census series using
149
+ the event-cumsum trick (+1 at arrival, −1 at discharge, cumulative sum reindexed
150
+ to the hourly grid). The figure shows the last week of the synthetic fixture: a
151
+ clear diurnal cycle with overnight troughs and evening peaks.
152
+
153
+ ![Synthetic hourly occupancy](https://raw.githubusercontent.com/nicoveraz/urg-forecast-core/main/docs/img/demo_synthetic_occupancy.png)
154
+
155
+ ### 2. Forecasting layer — 48-hour hourly forecast
156
+
157
+ On that series, `SeasonalNaiveBaseline` produces a 48-hour hourly forecast with
158
+ P50–P80 and P80–P95 quantile bands. The stronger models (`AutoARIMA`,
159
+ `AutoETS`, `LGBQuantile`) are compared in the evaluation harness and live behind
160
+ the same `Forecaster` interface.
161
+
162
+ ![Synthetic 48h hourly forecast](https://raw.githubusercontent.com/nicoveraz/urg-forecast-core/main/docs/img/demo_synthetic_forecast.png)
163
+
164
+ ### 3. Monte Carlo simulation engine
165
+
166
+ `urgencias_core.simulation.engine` takes future arrivals (sampled from the
167
+ forecast) and, for each arrival, samples an empirical length-of-stay conditional
168
+ on (acuity, arrival hour). Iterating M replicates yields census uncertainty
169
+ bands 24 hours ahead — the basis for surge decisions and shift tables.
170
+
171
+ ![24h Monte Carlo simulation](https://raw.githubusercontent.com/nicoveraz/urg-forecast-core/main/docs/img/demo_synthetic_simulation.png)
172
+
173
+ ### 4. Real data — weekly backtest on DEIS
174
+
175
+ The DEIS demo runs the same forecasting layer against real ED attendances from
176
+ Hospital de Puerto Montt. The harness holds out the last 12 complete weeks
177
+ (partial edge weeks are trimmed, since near-real-time DEIS data can under-count
178
+ the latest week) and trains on the prior history; `AutoARIMA` is selected by P80
179
+ pinball loss and tracks the autumn rise — evidence the pipeline isn't overfit to
180
+ the synthetic regime.
181
+
182
+ ![12-week holdout, Puerto Montt](https://raw.githubusercontent.com/nicoveraz/urg-forecast-core/main/docs/img/deis_holdout_hospital_base_puerto_montt.png)
183
+
184
+ ### 5. Operational 6-month forecast
185
+
186
+ With the model validated, the demo retrains on all history and emits a 26-week
187
+ weekly forecast — the useful horizon for staffing, budget, and supplies. The
188
+ P80–P95 band widens with the horizon, as expected.
189
+
190
+ ![6-month forecast, Puerto Montt](https://raw.githubusercontent.com/nicoveraz/urg-forecast-core/main/docs/img/deis_forecast_hospital_base_puerto_montt.png)
191
+
192
+ ### 6. Reference dashboard
193
+
194
+ The FastAPI server (`urgencias-server`) exposes four routes with the same charts
195
+ as the pipeline, live in the browser. It is deliberately minimal — Jinja2 plus
196
+ base64-embedded matplotlib, no JavaScript — a starting point for a hospital to
197
+ clone and adapt.
198
+
199
+ <p align="center">
200
+ <img src="https://raw.githubusercontent.com/nicoveraz/urg-forecast-core/main/docs/img/dashboard_index.png" width="48%" alt="Dashboard - index"/>
201
+ <img src="https://raw.githubusercontent.com/nicoveraz/urg-forecast-core/main/docs/img/dashboard_baseline.png" width="48%" alt="Dashboard - descriptive analytics"/>
202
+ </p>
203
+ <p align="center">
204
+ <img src="https://raw.githubusercontent.com/nicoveraz/urg-forecast-core/main/docs/img/dashboard_forecast.png" width="48%" alt="Dashboard - forecast"/>
205
+ <img src="https://raw.githubusercontent.com/nicoveraz/urg-forecast-core/main/docs/img/dashboard_simulation.png" width="48%" alt="Dashboard - simulation"/>
206
+ </p>
207
+
208
+ ## What's inside
209
+
210
+ | Module | Purpose |
211
+ |---|---|
212
+ | `urgencias_core.data.loader` | Read a visit-level parquet and validate the schema. |
213
+ | `urgencias_core.data.timeseries` | Convert visits to an hourly series (arrivals, discharges, occupancy by acuity, mean LOS) via the event-cumsum trick. |
214
+ | `urgencias_core.data.deis` | DEIS MINSAL client (fetch + cache + filter to demo hospitals) with an offline snapshot fallback. |
215
+ | `urgencias_core.features.calendar` | `holidays.CL` holidays, bridge days, school calendar, configurable regional events. |
216
+ | `urgencias_core.features.weather` | Open-Meteo client with on-disk cache, Puerto Montt by default. |
217
+ | `urgencias_core.models.protocol` | `Forecaster` protocol and `HorizonSpec` (grain-agnostic: hourly, daily, weekly, monthly). |
218
+ | `urgencias_core.models.lgb_quantile` | LightGBM quantile regression, one model per quantile, calendar features. |
219
+ | `urgencias_core.eval.baselines` | `SeasonalNaiveBaseline` + `statsforecast` wrappers (AutoARIMA, AutoETS, AutoTheta, MSTL). |
220
+ | `urgencias_core.eval.harness` | Side-by-side evaluation with a ≥5% warning rule (a model that doesn't beat the baselines shouldn't ship). |
221
+ | `urgencias_core.simulation.los_empirical` | Empirical LOS sampler conditional on (acuity, arrival hour). |
222
+ | `urgencias_core.simulation.engine` | Monte Carlo census simulation 24 hours forward. |
223
+ | `urgencias_core.server` | Minimal FastAPI server (4 routes, Jinja2, base64 matplotlib, no JS). |
224
+
225
+ Docstrings are in English; user-facing strings in the server, demo outputs, and
226
+ fixtures are in Spanish.
227
+
228
+ ## Public data: DEIS MINSAL
229
+
230
+ The DEIS demo uses open data from Chile's Ministry of Health Department of Health
231
+ Statistics and Information ([deis.minsal.cl](https://deis.minsal.cl/#datosabiertos))
232
+ for two hospitals in the Servicio de Salud Reloncaví network: **Hospital de
233
+ Puerto Montt** (code 24-105, high-complexity, locally "Hospital Base de Puerto
234
+ Montt") and **Hospital de Frutillar** (code 24-115, low-complexity).
235
+
236
+ DEIS publishes this series from 2008 to the present. The current-year file is
237
+ updated weekly during the winter respiratory campaign (March–September) and
238
+ roughly monthly otherwise. The demo uses the latest data available at run time
239
+ and produces a six-month-ahead forecast.
240
+
241
+ **Why these two hospitals.** Regional reference centers in Los Lagos with
242
+ publicly available data. Chosen on geographic and pragmatic grounds, not on any
243
+ evaluation of quality.
244
+
245
+ **Methodological framing only.** This demo uses public DEIS MINSAL data to show
246
+ how the forecasting tools behave on real Chilean hospital data. It is NOT an
247
+ operational, clinical, or quality evaluation of either hospital.
248
+
249
+ **Attribution and license.** Data published by DEIS MINSAL under Chile's open
250
+ data framework. If you use this code or derivatives for research, maintain DEIS
251
+ attribution and, where relevant, cite `urg-forecast-core`. The offline snapshot
252
+ bundled with the package is a filtered extract of the public dataset for
253
+ reproducibility; it does not replace fetching directly from the source for
254
+ operational use.
255
+
256
+ ## Project status
257
+
258
+ `urg-forecast-core` is an open foundation, developed and maintained by Nicolás
259
+ Vera Z. as the base of **Eunosia**, a clinical AI platform for emergency
260
+ medicine. It is published on PyPI and maintained on a best-effort basis: while
261
+ on 0.x the API may change between minor versions, and issues and pull requests
262
+ are welcome but not guaranteed a fast response. If you need commercial support
263
+ or bespoke work on top of this foundation, contact the author.
264
+
265
+ See [`CONTRIBUTING.md`](https://github.com/nicoveraz/urg-forecast-core/blob/main/CONTRIBUTING.md)
266
+ for development and release procedures, [`docs/decisions.md`](https://github.com/nicoveraz/urg-forecast-core/blob/main/docs/decisions.md)
267
+ for architecture decisions, and [`docs/roadmap.md`](https://github.com/nicoveraz/urg-forecast-core/blob/main/docs/roadmap.md)
268
+ for deferred items (xlsx/mdb support for DEIS 2017–2019, neuralforecast, EMR
269
+ integration to separate workup from boarding time).
270
+
271
+ ## Citing
272
+
273
+ If you use `urg-forecast-core` in research, please cite it. Machine-readable
274
+ metadata lives in [`CITATION.cff`](https://github.com/nicoveraz/urg-forecast-core/blob/main/CITATION.cff)
275
+ (GitHub renders a "Cite this repository" button from it). Each tagged release is
276
+ archived on [Zenodo](https://zenodo.org/) with a DOI.
277
+
278
+ <!-- After the first Zenodo release, add the concept-DOI badge and BibTeX here:
279
+ [![DOI](https://zenodo.org/badge/DOI/10.5281/zenodo.XXXXXXX.svg)](https://doi.org/10.5281/zenodo.XXXXXXX) -->
280
+
281
+ A software paper (JOSS format) and a fuller methods preprint are drafted under
282
+ [`paper/`](https://github.com/nicoveraz/urg-forecast-core/tree/main/paper).
283
+ Please also mention Eunosia and link back to the repository.
284
+
285
+ ## License
286
+
287
+ MIT. See [LICENSE](https://github.com/nicoveraz/urg-forecast-core/blob/main/LICENSE).