diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 00000000..6f529729 Binary files /dev/null and b/.DS_Store differ diff --git a/CLAUDE.md b/CLAUDE.md index 742a903e..9841688e 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,15 +1,15 @@ -# CLAUDE.md - ExpertAssist +# CLAUDE.md - Co-Study4Grid ## Project Overview -ExpertAssist is a full-stack web application for **power grid contingency analysis and N-1 planning**. It provides an interface to the `expert_op4grid_recommender` library, allowing operators to simulate element disconnections, visualize network overflow graphs, and receive prioritized remedial action recommendations. +Co-Study4Grid is a full-stack web application for **power grid contingency analysis and N-1 planning**. It provides an interface to the `expert_op4grid_recommender` library, allowing operators to simulate element disconnections, visualize network overflow graphs, and receive prioritized remedial action recommendations. ## Architecture **Monorepo** with two main components: ``` -ExpertAssist/ +Co-Study4Grid/ ├── .gitignore # Excludes __pycache__/, *.pyc, *.pyo ├── CLAUDE.md # Project documentation for AI assistants ├── expert_backend/ # Python FastAPI backend @@ -190,7 +190,7 @@ npm run test # Run Vitest test suite ### Session Save/Load - **Save**: `buildSessionResult()` in `sessionUtils.ts` serializes all state (config, contingency, actions with status tags, combined pairs) -> `POST /api/save-session` writes to disk - **Load**: `POST /api/load-session` reads `session.json` -> frontend restores all state without re-simulating actions -- **Output folder**: `/expertassist_session__/` contains `session.json` + overflow PDF +- **Output folder**: `/costudy4grid_session__/` contains `session.json` + overflow PDF - **Interaction logging**: Every user interaction is logged as a timestamped, replay-ready event via `interactionLogger`. Saved as `interaction_log.json` alongside `session.json`. - See `docs/save-results.md` for session save/load and `docs/interaction-logging.md` for the replay contract diff --git a/README.md b/README.md index 3730a752..91255879 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# ExpertAssist +# Co-Study4Grid -A full-stack web application for **power grid contingency analysis and N-1 planning**. ExpertAssist provides an interface to the [`expert_op4grid_recommender`](https://github.com/rte-france/expert_op4grid_recommender) library, allowing grid operators to: +A full-stack web application for **power grid contingency analysis and N-1 planning**. Co-Study4Grid provides an interface to the [`expert_op4grid_recommender`](https://github.com/rte-france/expert_op4grid_recommender) library, allowing grid operators to: - Simulate element disconnections (N-1 contingency analysis) - Visualize network overflow graphs and network-area diagrams @@ -8,10 +8,10 @@ A full-stack web application for **power grid contingency analysis and N-1 plann ## Architecture -ExpertAssist is a monorepo with a **Python FastAPI backend** and a **React + TypeScript frontend**. +Co-Study4Grid is a monorepo with a **Python FastAPI backend** and a **React + TypeScript frontend**. ``` -ExpertAssist/ +Co-Study4Grid/ ├── expert_backend/ # FastAPI backend (Python) │ ├── main.py # API endpoints and app configuration │ └── services/ diff --git a/docs/interaction-logging.md b/docs/interaction-logging.md index 42340752..d55ebf4e 100644 --- a/docs/interaction-logging.md +++ b/docs/interaction-logging.md @@ -2,7 +2,7 @@ ## Overview -ExpertAssist logs every user interaction during a session as a timestamped, **replay-ready** event log. The log contains enough data for an automated agent (e.g. browser automation) to deterministically reproduce the exact same session — same clicks, same selections, same analysis runs — without human input. +Co-Study4Grid logs every user interaction during a session as a timestamped, **replay-ready** event log. The log contains enough data for an automated agent (e.g. browser automation) to deterministically reproduce the exact same session — same clicks, same selections, same analysis runs — without human input. The log is saved as a dedicated `interaction_log.json` file alongside `session.json` when saving results. @@ -229,7 +229,7 @@ After saving, the session folder contains: ``` / - expertassist_session_LINE_XYZ_2026-03-18T10-30-00/ + costudy4grid_session_LINE_XYZ_2026-03-18T10-30-00/ session.json ← full state snapshot interaction_log.json ← replay-ready event log overflow_abc123.pdf ← overflow graph copy @@ -363,7 +363,7 @@ After saving, the session folder contains: "type": "session_saved", "correlation_id": "e1f2a3b4", "details": { - "session_name": "expertassist_session_LINE_XYZ_2026-03-18T10-25-00", + "session_name": "costudy4grid_session_LINE_XYZ_2026-03-18T10-25-00", "output_folder": "/data/output" } } diff --git a/docs/rendering-optimization-plan.md b/docs/rendering-optimization-plan.md index 3b1bd1c8..1f9a5bd2 100644 --- a/docs/rendering-optimization-plan.md +++ b/docs/rendering-optimization-plan.md @@ -2,7 +2,7 @@ ## Overview -ExpertAssist renders pypowsybl Network Area Diagrams (NAD) for power grids with 11,000+ lines and 500+ voltage levels. At this scale, naive rendering causes multi-second tab switches, invisible line colors at zoom-out, and zoom lag/crashes. This document traces the critical rendering features, their rationale, and regression risks. +Co-Study4Grid renders pypowsybl Network Area Diagrams (NAD) for power grids with 11,000+ lines and 500+ voltage levels. At this scale, naive rendering causes multi-second tab switches, invisible line colors at zoom-out, and zoom lag/crashes. This document traces the critical rendering features, their rationale, and regression risks. ## Critical CSS Properties diff --git a/docs/save-results.md b/docs/save-results.md index 0258f572..df695c6e 100644 --- a/docs/save-results.md +++ b/docs/save-results.md @@ -13,7 +13,7 @@ Co-Study4Grid supports **saving** and **reloading** full analysis sessions: - **`interaction_log.json`** — timestamped log of every user interaction, suitable for automated session replay (see [docs/interaction-logging.md](interaction-logging.md)) - **`.pdf`** — a copy of the overflow graph PDF (when an analysis has been run) -All files are written to a **session folder** named `expertassist_session__/` inside the configured **Output Folder Path**. +All files are written to a **session folder** named `costudy4grid_session__/` inside the configured **Output Folder Path**. --- @@ -52,7 +52,7 @@ Settings → gear icon → Paths tab → Output Folder Path ``` / - expertassist_session_LINE_XYZ_2026-03-11T14-23-05/ + costudy4grid_session_LINE_XYZ_2026-03-11T14-23-05/ session.json interaction_log.json <- replay-ready event log overflow_abc123.pdf <- copy of the overflow graph diff --git a/docs/state-reset-and-confirmation-dialogs.md b/docs/state-reset-and-confirmation-dialogs.md index 338e57d8..ae4fa64f 100644 --- a/docs/state-reset-and-confirmation-dialogs.md +++ b/docs/state-reset-and-confirmation-dialogs.md @@ -2,7 +2,7 @@ ## Overview -ExpertAssist manages complex analysis state across contingency selections, N-1 simulations, manual action simulations, and prioritized action results. To prevent stale data from interfering with new analyses, the application implements two complementary mechanisms: +Co-Study4Grid manages complex analysis state across contingency selections, N-1 simulations, manual action simulations, and prioritized action results. To prevent stale data from interfering with new analyses, the application implements two complementary mechanisms: 1. **Confirmation dialogs** when switching contingency or reloading a study with existing analysis state 2. **Full state reset** when loading a new study or applying new settings diff --git a/expert_backend/main.py b/expert_backend/main.py index e0684e7b..03c1c53d 100644 --- a/expert_backend/main.py +++ b/expert_backend/main.py @@ -365,7 +365,7 @@ def list_sessions(folder_path: str = Query(...)): try: for entry in os.listdir(folder_path): entry_path = os.path.join(folder_path, entry) - if os.path.isdir(entry_path) and entry.startswith("expertassist_session"): + if os.path.isdir(entry_path) and (entry.startswith("costudy4grid_session") or entry.startswith("expertassist_session")): json_path = os.path.join(entry_path, "session.json") if os.path.isfile(json_path): sessions.append(entry) diff --git a/expert_backend/tests/test_environment_detection.py b/expert_backend/tests/test_environment_detection.py index 1fb0cf0b..78d3db9b 100644 --- a/expert_backend/tests/test_environment_detection.py +++ b/expert_backend/tests/test_environment_detection.py @@ -10,7 +10,7 @@ def test_non_reconnectable_detection_with_date(): Verify that non-reconnectable lines are detected correctly even when an analysis_date is provided. This was previously a bug where the detection was bypassed if analysis_date was not None. """ - # 1. Setup paths relative to ExpertAssist root + # 1. Setup paths relative to Co-Study4Grid root project_root = Path(__file__).parent.parent.parent test_env_path = project_root / "data" / "bare_env_small_grid_test" diff --git a/frontend/index.html b/frontend/index.html index 072a57e8..f87b033b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -4,7 +4,7 @@ - frontend + Co-Study4Grid
diff --git a/frontend/lint_output.txt b/frontend/lint_output.txt deleted file mode 100644 index a1f11158..00000000 --- a/frontend/lint_output.txt +++ /dev/null @@ -1,13 +0,0 @@ - -> frontend@0.0.0 lint -> eslint . - - -/home/marotant/dev/AntiGravity/ExpertAssist/frontend/src/components/CombinedActionsModal.test.tsx - 133:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 161:25 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 180:31 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - 182:90 error Unexpected any. Specify a different type @typescript-eslint/no-explicit-any - -✖ 4 problems (4 errors, 0 warnings) - diff --git a/frontend/package.json b/frontend/package.json index 86d1800e..2880726d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,5 +1,5 @@ { - "name": "frontend", + "name": "co-study4grid", "private": true, "version": "0.0.0", "type": "module", diff --git a/frontend/src/App.settings.test.tsx b/frontend/src/App.settings.test.tsx index a5eb97cb..bcc26d1c 100644 --- a/frontend/src/App.settings.test.tsx +++ b/frontend/src/App.settings.test.tsx @@ -211,7 +211,7 @@ describe('Save Results button', () => { expect(createObjectURL).toHaveBeenCalledOnce(); expect(anchorClicks).toHaveLength(1); const anchor = anchorClicks[0]; - expect(anchor.download).toMatch(/^expertassist_session_BRANCH_A_/); + expect(anchor.download).toMatch(/^costudy4grid_session_BRANCH_A_/); expect(anchor.download).toMatch(/\.json$/); }); diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index b1d240f2..4850ed7a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -617,7 +617,7 @@ function App() { value={configFilePath} onChange={e => setConfigFilePath(e.target.value)} onBlur={e => changeConfigFilePath(e.target.value).catch(() => { })} - placeholder="e.g. /home/user/my_expertassist_config.json" + placeholder="e.g. /home/user/my_costudy4grid_config.json" style={{ flex: 1, padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }} /> - - - - - - - - - - - - - {/* Settings Modal */} - {isSettingsOpen && ( -
-
-
- - - -
- - {settingsTab === 'paths' && ( -
-
- -
Synchronized with the banner field
-
- setNetworkPath(e.target.value)} placeholder="load your grid xiidm file path" style={{ flex: 1, padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }} /> - -
-
-
- -
- setActionPath(e.target.value)} style={{ flex: 1, padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }} /> - -
-
-
- -
- setLayoutPath(e.target.value)} style={{ flex: 1, padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }} /> - -
-
-
- -
Session folders (JSON + PDF) are saved here. Leave empty to download JSON to browser.
-
- setOutputFolderPath(e.target.value)} placeholder="e.g. /home/user/sessions" style={{ flex: 1, padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }} /> - -
-
-
- )} - - {settingsTab === 'recommender' && ( -
-
- - setMinLineReconnections(parseFloat(e.target.value))} style={{ width: '80px', padding: '5px', border: '1px solid #ccc', borderRadius: '4px' }} /> -
-
- - setMinCloseCoupling(parseFloat(e.target.value))} style={{ width: '80px', padding: '5px', border: '1px solid #ccc', borderRadius: '4px' }} /> -
-
- - setMinOpenCoupling(parseFloat(e.target.value))} style={{ width: '80px', padding: '5px', border: '1px solid #ccc', borderRadius: '4px' }} /> -
-
- - setMinLineDisconnections(parseFloat(e.target.value))} style={{ width: '80px', padding: '5px', border: '1px solid #ccc', borderRadius: '4px' }} /> -
-
- - setMinPst(parseFloat(e.target.value))} style={{ width: '80px', padding: '5px', border: '1px solid #ccc', borderRadius: '4px' }} /> -
-
- - setNPrioritizedActions(parseInt(e.target.value, 10))} style={{ width: '80px', padding: '5px', border: '1px solid #ccc', borderRadius: '4px' }} /> -
-
- setIgnoreReconnections(e.target.checked)} style={{ width: '16px', height: '16px' }} /> - -
-
- )} - - {settingsTab === 'configurations' && ( -
-
- -
- setLinesMonitoringPath(e.target.value)} placeholder="Leave empty for IGNORE_LINES_MONITORING=True" style={{ flex: 1, padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }} /> - -
-
-
- - setPreExistingOverloadThreshold(parseFloat(e.target.value))} style={{ width: '80px', padding: '5px', border: '1px solid #ccc', borderRadius: '4px' }} /> -
-
- Pre-existing overloads excluded from N-1 & max loading unless worsened by this fraction (default 2%) -
-
-
-
- setPypowsyblFastMode(e.target.checked)} style={{ width: '16px', height: '16px' }} /> - -
-
- Disable voltage control in pypowsybl for faster simulations (may affect convergence) -
-
-
-
- )} - -
- - -
-
-
- )} - - {/* Reload Session Modal */} - {showReloadModal && ( -
-
-
-

Reload Session

- -
-
- From: {outputFolderPath} -
-
- {sessionListLoading ? ( -
Loading sessions...
- ) : sessionList.length === 0 ? ( -
No saved sessions found in this folder.
- ) : ( - sessionList.map(name => ( -
!sessionRestoring && handleRestoreSession(name)} - style={{ - padding: '10px 12px', margin: '4px 0', - border: '1px solid #eee', borderRadius: '6px', - cursor: sessionRestoring ? 'not-allowed' : 'pointer', - fontSize: '0.85rem', fontFamily: 'monospace', - transition: 'background 0.15s', - opacity: sessionRestoring ? 0.5 : 1, - }} - onMouseOver={e => { if (!sessionRestoring) (e.currentTarget as HTMLElement).style.background = '#e7f1ff'; }} - onMouseOut={e => { (e.currentTarget as HTMLElement).style.background = 'transparent'; }} - > - {name} -
- )) - )} -
-
- -
-
-
- )} - -
-
- {/* Target Contingency selector */} - {branches.length > 0 && ( -
- - setSelectedBranch(e.target.value)} - placeholder="Search line/bus..." - style={{ width: '100%', padding: '7px 10px', border: '1px solid #ccc', borderRadius: '4px', boxSizing: 'border-box', fontSize: '0.85rem' }} - /> - - {branches.map(b => - -
- )} - -
- void} - showMonitoringWarning={showMonitoringWarning} - monitoredLinesCount={monitoredLinesCount} - totalLinesCount={totalLinesCount} - monitoringFactor={monitoringFactor} - preExistingOverloadThreshold={preExistingOverloadThreshold} - onDismissWarning={() => setShowMonitoringWarning(false)} - onOpenSettings={() => { setIsSettingsOpen(true); setSettingsTab('configurations'); }} - selectedOverloads={selectedOverloads} - onToggleOverload={handleToggleOverload} - monitorDeselected={monitorDeselected} - onToggleMonitorDeselected={() => setMonitorDeselected(prev => !prev)} - /> -
-
- -
-
-
- 0} - selectedBranch={selectedBranch} - vlOverlay={vlOverlay} - onOverlayClose={handleOverlayClose} - onOverlaySldTabChange={handleOverlaySldTabChange} - voltageLevels={voltageLevels} - onVlOpen={(vlName) => handleVlDoubleClick(activeTab === 'action' ? selectedActionId || '' : '', vlName)} - networkPath={networkPath} - layoutPath={layoutPath} - onOpenSettings={handleOpenSettings} - /> -
-
- {/* Confirmation Dialog for contingency change / load study */} - {confirmDialog && ( -
-
-
⚠
-

- {confirmDialog.type === 'contingency' ? 'Change Contingency?' : 'Reload Study?'} -

-

- All previous analysis results, manual simulations, action selections, and diagrams will be cleared. - {confirmDialog.type === 'contingency' - ? ' The network state will be preserved.' - : ' The network will be reloaded from scratch.'} -

-
- - -
-
-
- )} - {error && ( -
- {error} -
- )} - {infoMessage && ( -
- {infoMessage} -
- )} - - ); -} - -export default App; diff --git a/frontend/src/hooks/useSession.ts b/frontend/src/hooks/useSession.ts index 2a853aa1..77838ffe 100644 --- a/frontend/src/hooks/useSession.ts +++ b/frontend/src/hooks/useSession.ts @@ -124,7 +124,7 @@ export function useSession(): SessionState { const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); const contingencyLabel = params.selectedBranch ? `_${params.selectedBranch.replace(/[^a-zA-Z0-9_-]/g, '_')}` : ''; - const sessionName = `expertassist_session${contingencyLabel}_${ts}`; + const sessionName = `costudy4grid_session${contingencyLabel}_${ts}`; if (params.outputFolderPath) { try { diff --git a/pyproject.toml b/pyproject.toml index 988998ca..a4cbd424 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,12 +3,12 @@ requires = ["setuptools>=61.0"] build-backend = "setuptools.build_meta" [project] -name = "expert_assist" +name = "co_study4grid" version = "0.1.0" authors = [ { name="RTE", email="rte@rte-france.com" }, ] -description = "Expert Assist interface for power grid analysis." +description = "Co-Study4Grid — power grid contingency analysis interface" readme = "README.md" requires-python = ">=3.10" license = "MPL-2.0" diff --git a/standalone_interface.html b/standalone_interface.html index 6d734407..904d4a3e 100644 --- a/standalone_interface.html +++ b/standalone_interface.html @@ -1924,7 +1924,7 @@ const handleSaveSession = async () => { const ts = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19); const contingencyLabel = selectedBranch ? `_${selectedBranch.replace(/[^a-zA-Z0-9_-]/g, '_')}` : ''; - const sessionName = `expertassist_session${contingencyLabel}_${ts}`; + const sessionName = `costudy4grid_session${contingencyLabel}_${ts}`; // Build the session JSON (same structure as the React app) const session = { @@ -4806,7 +4806,7 @@

setConfigFilePath(e.target.value)} onBlur={e => changeConfigFilePath(e.target.value)} - placeholder="e.g. /home/user/my_expertassist_config.json" + placeholder="e.g. /home/user/my_costudy4grid_config.json" style={{ flex: 1, padding: '8px', border: '1px solid #ccc', borderRadius: '4px' }} />