Skip to content

Commit fd4832c

Browse files
committed
Add data publish/sync tooling (DVC/R2) + docs page (run_data_sync)
1 parent 71bf5f9 commit fd4832c

14 files changed

Lines changed: 419 additions & 0 deletions

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,13 @@
55
**/~*
66
*.dat
77
*.sh
8+
!tools/sync.sh
89
*~
910

11+
# Data publishing (DVC) — see docs/run/run_data_sync.md
12+
tools/.env
13+
epm/output_view/
14+
1015
# GAMS artifacts
1116
*.bk
1217
*.g00

Publish.bat

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@echo off
2+
REM Double-clic : publie tes donnees (code+pointeurs sur GitHub, donnees sur le store, EPM View a jour)
3+
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0tools\publish.ps1"
4+
echo.
5+
pause

Sync.bat

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
@echo off
2+
REM Double-clic : recupere code + donnees a jour (git pull + dvc pull)
3+
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0tools\sync.ps1"
4+
echo.
5+
pause

docs/run/run_data_sync.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Publishing & Syncing Data (DVC)
2+
3+
EPM **code** is public, but the **data** (input CSVs, results) is kept in a **private
4+
store** — not in this public repository. The repo only keeps tiny **pointers**; the real
5+
data lives in the store and is fetched on demand.
6+
7+
This page explains, from scratch, how to **publish** your data and how to **get it back**
8+
on another machine or on the server. No prior knowledge of DVC is assumed.
9+
10+
!!! note "Prototype"
11+
The store is currently a Cloudflare R2 bucket (test data). For confidential data,
12+
the production store will be the World Bank S3 — same workflow, different endpoint.
13+
14+
---
15+
16+
## The idea in one picture
17+
18+
```
19+
YOU (laptop) — you edit inputs/outputs in EPM
20+
21+
│ one action (double-click Publish):
22+
│ ├─ git push → GitHub : code + .dvc pointers (no data)
23+
│ ├─ dvc push → STORE : the data (for runs)
24+
│ └─ publish → STORE : a readable copy (for EPM View)
25+
26+
┌──────────────── PRIVATE STORE (R2 / S3) ────────────────┐
27+
└───────────────┬─────────────────────────┬──────────────┘
28+
│ dvc pull │ gated read
29+
SERVER (run the model) EPM View (display)
30+
```
31+
32+
**What is DVC?** Think *"git for data"*. Git stays light because the data is replaced by a
33+
6-line pointer file (`*.dvc` = a hash + a path, **no values**). The real data lives in the
34+
store, moved with `dvc push` / `dvc pull`.
35+
36+
The store keeps **two layouts** of the same data: a hash-addressed copy for DVC (runs), and
37+
a readable copy by path for EPM View (the browser can't speak DVC). Both are written by the
38+
same "publish" action — you don't manage this by hand.
39+
40+
---
41+
42+
## One-time setup (per machine)
43+
44+
1. Install the dependencies (DVC is included):
45+
```bash
46+
pip install -r requirements.txt
47+
```
48+
2. Add your store keys: copy `tools/.env.example` to `tools/.env` and paste the 4 values
49+
(ask the store admin).
50+
```
51+
tools/.env ← your keys (git-ignored — NEVER commit real keys)
52+
tools/.env.example ← template
53+
```
54+
55+
That's it. The remote URL/endpoint is already in the repo (`.dvc/config`).
56+
57+
---
58+
59+
## Publish your data
60+
61+
After you change inputs (or want to show results), **publish in one action**:
62+
63+
=== "Windows"
64+
Double-click **`Publish.bat`** at the repo root.
65+
66+
=== "Command line"
67+
```bash
68+
powershell -File tools/publish.ps1 # Windows
69+
```
70+
71+
This does everything: re-hash the data (`dvc add`) → commit + push the pointer →
72+
`dvc push` (data to the store, for the server) → upload readable copies (inputs +
73+
`epm/output_view/`) for EPM View.
74+
75+
!!! tip "Showing results in EPM View"
76+
Copy the runs you want to display into
77+
`epm/output_view/<run>/<scenario>/output_csv/` **before** publishing (only `.csv`
78+
files are sent). `epm/output_view/` is a local staging folder (git-ignored).
79+
80+
---
81+
82+
## Get the data (another machine / the server)
83+
84+
=== "Server (Linux)"
85+
```bash
86+
bash tools/sync.sh
87+
```
88+
=== "Windows"
89+
Double-click **`Sync.bat`**.
90+
91+
This runs `git pull` (code + pointers) then `dvc pull` (data from the store) — then you can
92+
run the model with up-to-date data.
93+
94+
---
95+
96+
## Onboarding a new model (one-time per model)
97+
98+
Moving a model's data out of git into the store (the "switch"):
99+
100+
```bash
101+
dvc init # once per repo
102+
dvc remote add -d store s3://<bucket>/dvcstore
103+
dvc remote modify store endpointurl <endpoint>
104+
# in .gitignore: remove the whitelist of the data folder, add !epm/input/<data>.dvc
105+
git rm -r --cached epm/input/<data_folder> # untrack (files stay on disk)
106+
dvc add epm/input/<data_folder> # create the pointer
107+
```
108+
Then publish (`Publish.bat`). After this, everyone just does *setup + publish/sync*.
109+
110+
To make **EPM View** read this branch from the store, add the branch name to `R2_BRANCHES`
111+
in `src/utils/epmFetch.js` of the `epm-data-explorer` repo.
112+
113+
---
114+
115+
## Troubleshooting
116+
117+
- **`Unable to locate credentials`** → keys not loaded. On the server, either
118+
`export AWS_ACCESS_KEY_ID=… AWS_SECRET_ACCESS_KEY=…`, or set them once with
119+
`dvc remote modify --local store access_key_id …` (stays in git-ignored `config.local`).
120+
- **`CERTIFICATE_VERIFY_FAILED` / SSL** → a corporate TLS proxy is intercepting the
121+
connection. Try off-VPN, or `pip install pip-system-certs` (makes Python trust the OS
122+
certificate store). The AWS server is not affected.
123+
- **`dvc` command not found** (server) → use `python -m dvc …`, or add `~/.local/bin` to
124+
your `PATH`.
125+
126+
---
127+
128+
## Files (in `tools/`)
129+
130+
| File | Role |
131+
|------|------|
132+
| `publish.ps1` | engine behind `Publish.bat` |
133+
| `sync.ps1` / `sync.sh` | get code + data (Windows / server) |
134+
| `upload_to_r2.py`, `upload_output_view_to_r2.py` | upload the readable copies |
135+
| `.env.example` | keys template (copy to `.env`, git-ignored) |

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ nav:
4848
- Run from Python: run/run_python.md
4949
- Run from GAMS Studio: run/run_gams_studio.md
5050
- Run on Remote Server: run/run_remote_server.md
51+
- Publish & Sync Data (DVC): run/run_data_sync.md
5152
- Dispatch Mode: run/run_dispatch.md
5253
- Solver Options: run/options_solver.md
5354
- Performance & Threads: run/options_thread.md

requirements.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,8 @@ rasterio
2020
entsoe-py
2121
pycountry
2222
mcp
23+
# Data publishing/sync to private store (R2/S3) — see docs/run/run_data_sync.md
24+
dvc[s3]
25+
s3fs
2326
# Optional (Monte Carlo analysis only):
2427
# chaospy==4.3.18

tools/.env.example

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Cles d'acces au data store (R2 maintenant ; S3 WB plus tard).
2+
# 1. Copie ce fichier en tools/.env
3+
# 2. Colle tes cles (fournies par l'admin du store)
4+
# /!\ tools/.env est gitignore : NE JAMAIS committer de vraies cles.
5+
6+
AWS_ACCESS_KEY_ID=
7+
AWS_SECRET_ACCESS_KEY=
8+
STORE_ENDPOINT=https://25d26c21c96631ccd04cb2178ed3fb6b.r2.cloudflarestorage.com
9+
STORE_BUCKET=epm-view-data

tools/DATA_PUBLISH.md

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# Publier / récupérer les données du modèle (store privé)
2+
3+
> 📖 **Guide complet (doc EPM) :** `docs/run/run_data_sync.md`*Installation & Run →
4+
> Publish & Sync Data (DVC)*. Ce fichier-ci est un aide-mémoire rapide à côté des scripts.
5+
6+
Les **données** d'EPM (input/output) ne vivent plus dans ce repo public : elles sont
7+
dans un **store privé** (R2 aujourd'hui, S3 WB plus tard). Le repo ne garde que de
8+
petits **pointeurs `.dvc`**. Ce dossier fournit l'outillage pour publier et récupérer.
9+
10+
> **Prototype.** Le store actuel est un bucket Cloudflare R2 (données de test).
11+
> Données confidentielles → attendre le store S3 de la Banque.
12+
13+
---
14+
15+
## 🔧 Mise en place — une fois par machine
16+
17+
1. Installer les dépendances (inclut DVC) :
18+
```
19+
pip install -r requirements.txt
20+
```
21+
2. Mettre tes clés d'accès au store :
22+
- copie `tools/.env.example``tools/.env`
23+
- colle les 4 valeurs (fournies par l'admin du store)
24+
- ⚠️ `tools/.env` est **gitignoré** : ne jamais committer de vraies clés.
25+
26+
C'est tout. La config du remote (URL/endpoint) est déjà dans le repo (`.dvc/config`).
27+
28+
---
29+
30+
## ⬆️ Publier (après avoir modifié des données) — Windows
31+
32+
**Double-clic sur `Publish.bat`** (à la racine du repo).
33+
34+
Ça fait tout : re-hash des données (`dvc add`) → commit + push des pointeurs →
35+
`dvc push` (données → store, pour le serveur) → upload des copies lisibles
36+
(inputs + `epm/output_view/`) → store, pour **EPM View**.
37+
38+
> Pour montrer des **résultats** dans EPM View : copie les runs voulus dans
39+
> `epm/output_view/<run>/<scenario>/output_csv/` avant de publier (seuls les `.csv`
40+
> sont envoyés). `epm/output_view/` est gitignoré (zone de staging locale).
41+
42+
---
43+
44+
## ⬇️ Récupérer code + données
45+
46+
- **Windows** : double-clic sur `Sync.bat`
47+
- **Serveur Linux** : `bash tools/sync.sh`
48+
49+
= `git pull` (code + pointeurs) + `dvc pull` (données depuis le store).
50+
51+
---
52+
53+
## Onboarder un NOUVEAU modèle (une fois par modèle)
54+
55+
Sortir les données d'un modèle de git vers le store (la « bascule ») :
56+
```
57+
dvc init # une fois par repo
58+
dvc remote add -d store s3://<bucket>/dvcstore
59+
dvc remote modify store endpointurl <endpoint>
60+
# retirer la whitelist du dossier dans .gitignore, garder !epm/input/<data>.dvc
61+
git rm -r --cached epm/input/<data_folder>
62+
dvc add epm/input/<data_folder>
63+
```
64+
Puis publier (`Publish.bat`). Après ça, tout le monde fait juste *mise en place + publish/sync*.
65+
66+
---
67+
68+
## Côté EPM View (l'app)
69+
70+
EPM View lit les données par branche. Les branches dont les données sont dans le
71+
store privé sont listées dans `R2_BRANCHES` (fichier `src/utils/epmFetch.js` du repo
72+
`epm-data-explorer`). Pour brancher une nouvelle région : ajouter sa branche là.
73+
74+
---
75+
76+
## Fichiers de ce dossier (publication données)
77+
78+
- `publish.ps1` — moteur de `Publish.bat`
79+
- `sync.ps1` / `sync.sh` — récupération (Windows / serveur)
80+
- `upload_to_r2.py`, `upload_output_view_to_r2.py` — helpers d'upload lisible
81+
- `.env.example` — modèle de clés (copier en `.env`, gitignoré)

tools/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ This directory contains utility scripts and tools for working with EPM.
66

77
- **`mcp_server.py`** - MCP (Model Context Protocol) server that allows Cursor AI to run EPM directly
88
- **`test_epm_simple.py`** - Simple test script to run EPM with minimal configuration
9+
- **Data publishing/sync** (`publish.ps1`, `sync.ps1`/`sync.sh`, `upload_*.py`) - publish model data to / pull it from the private store (R2/S3). See **[DATA_PUBLISH.md](DATA_PUBLISH.md)** / **docs/run/run_data_sync.md**, and the `Publish.bat` / `Sync.bat` launchers at the repo root.
910

1011
## MCP Server
1112

tools/publish.ps1

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
# publish.ps1 — publie TOUT en un geste (lance par Publish.bat) :
2+
# re-hash (dvc add) -> commit+push des pointeurs -> dvc push (donnees, pour le serveur)
3+
# -> copies lisibles inputs + output_view (pour EPM View)
4+
# Auto-detecte le repo et la branche. Lit les cles dans tools/.env (gitignore).
5+
6+
$ErrorActionPreference = "Stop"
7+
$TOOLS = $PSScriptRoot
8+
$REPO = Split-Path $TOOLS -Parent
9+
10+
# --- 1. charger les cles depuis tools/.env ---
11+
$envFile = Join-Path $TOOLS ".env"
12+
if (-not (Test-Path $envFile)) {
13+
Write-Error "Manque $envFile -> copie tools/.env.example en tools/.env et mets tes cles."
14+
exit 1
15+
}
16+
Get-Content $envFile | Where-Object { $_ -match '^\s*[^#].*=' } | ForEach-Object {
17+
$k, $v = $_ -split '=', 2
18+
Set-Item -Path "env:$($k.Trim())" -Value $v.Trim()
19+
}
20+
21+
# --- 2. auto-detect repo + branche ---
22+
Set-Location $REPO
23+
$env:EPM_REPO = $REPO
24+
$env:EPM_BRANCH = (git rev-parse --abbrev-ref HEAD).Trim()
25+
Write-Host ""
26+
Write-Host "Repo : $REPO" -ForegroundColor DarkGray
27+
Write-Host "Branche : $($env:EPM_BRANCH)" -ForegroundColor DarkGray
28+
29+
# --- 3. pour chaque dossier de donnees suivi par DVC : re-hash + copie lisible ---
30+
$pointers = Get-ChildItem "epm\input" -Filter "data_*.dvc" -ErrorAction SilentlyContinue
31+
if (-not $pointers) { Write-Host "Aucun dossier data_*.dvc (modele pas encore migre vers DVC ?)" -ForegroundColor Yellow }
32+
foreach ($p in $pointers) {
33+
$folder = $p.BaseName # ex: data_blacksea
34+
Write-Host ""
35+
Write-Host "[data] $folder : dvc add + upload lisible ..." -ForegroundColor Cyan
36+
dvc add "epm/input/$folder"
37+
$env:EPM_DATA_FOLDER = $folder
38+
python "$TOOLS\upload_to_r2.py"
39+
}
40+
41+
# --- 4. commit + push des pointeurs (si change) ---
42+
Write-Host ""
43+
Write-Host "[git] commit + push des pointeurs (si change) ..." -ForegroundColor Cyan
44+
git add epm/input/*.dvc
45+
git diff --cached --quiet
46+
if ($LASTEXITCODE -ne 0) {
47+
git commit -m "update data ($(Get-Date -Format 'yyyy-MM-dd HH:mm'))"
48+
git push
49+
} else {
50+
Write-Host " (pointeurs inchanges -> rien a committer)" -ForegroundColor DarkGray
51+
}
52+
53+
# --- 5. donnees -> store (DVC, pour le serveur) ---
54+
Write-Host ""
55+
Write-Host "[dvc] push des donnees vers le store (pour le serveur) ..." -ForegroundColor Cyan
56+
dvc push
57+
58+
# --- 6. resultats lisibles -> store (pour EPM View) ---
59+
Write-Host ""
60+
Write-Host "[results] upload epm/output_view -> store (pour EPM View) ..." -ForegroundColor Cyan
61+
python "$TOOLS\upload_output_view_to_r2.py"
62+
63+
Write-Host ""
64+
Write-Host "OK - publie : GitHub (pointeurs) + store (DVC + lisible pour EPM View)." -ForegroundColor Green

0 commit comments

Comments
 (0)