Metadata-Version: 2.4
Name: psr-cloud
Version: 0.5.0
Summary: PSR Cloud Python Module
Author-email: Lucas Storino <lstorino@psr-inc.com>, Pedro Henrique <pedrohenrique@psr-inc.com>
Description-Content-Type: text/markdown
Requires-Dist: zeep
Requires-Dist: filelock
Requires-Dist: boto3
Requires-Dist: tqdm
Requires-Dist: keyring>=25.7.0
Provides-Extra: test
Requires-Dist: doit; extra == "test"
Requires-Dist: fastapi>=0.110; extra == "test"
Requires-Dist: python-dotenv; extra == "test"
Requires-Dist: pytest>=7; extra == "test"
Requires-Dist: pytest-cov>=4; extra == "test"
Requires-Dist: httpx>=0.27; extra == "test"
Provides-Extra: server
Requires-Dist: fastapi>=0.110; extra == "server"
Requires-Dist: uvicorn[standard]>=0.29; extra == "server"
Provides-Extra: desktop
Requires-Dist: fastapi>=0.110; extra == "desktop"
Requires-Dist: uvicorn[standard]>=0.29; extra == "desktop"
Provides-Extra: docs
Requires-Dist: mkdocs>=1.6; extra == "docs"
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
Requires-Dist: mkdocstrings[python]>=0.25; extra == "docs"

# psr.cloud (pycloud)

PSR Cloud Python module. Authenticate with PSR Cloud and submit, monitor, and
retrieve model cases programmatically from Python.

## Installation

### From source (recommended)

Copy the `psr` folder and its contents to your working directory, or add its
parent path to the `PYTHONPATH` environment variable.

### pip

```sh
pip install git+https://github.com/psrenergy/pycloud.git
```

## Quick example

```python
import psr.cloud

client = psr.cloud.Client()

case = psr.cloud.Case(
    name="Test PyCloud",
    program="SDDP",
    program_version="18.0.9",
    data_path="C:\\PSR\\SDDP18.0\\examples\\operation",
    price_optimized=True,
    execution_type="Operation Planning (Default)",
    number_of_processes=64,
    memory_per_process_ratio="2:1",
)

client.run_case(case)
```

## Interfaces

Besides the Python API above, the same capabilities are available through a CLI
(`console/`), a REST API (`api/`, `pip install -e .[server]`), and an Electron
**desktop app**. Browser and Electron renderers share one REST client and delegate
to the transport-agnostic `api/service.py`; Electron manages a token-protected
FastAPI sidecar bound only to loopback. Run `scripts\start_desktop_app.bat` for
development or `scripts\build_desktop_exe.bat` to create the Windows installer.

## Desktop development, testing, and Windows setup

The commands in this section are intended for Windows PowerShell and must be run
from the repository root:

```powershell
cd D:\Repositorios\pycloud
```

### 1. Prerequisites

Install the following tools before preparing the project:

- Windows x64.
- Python 3.13 and [`uv`](https://docs.astral.sh/uv/).
- Git.
- Internet access for the initial Python, Node, and npm dependency downloads.

Your existing system-wide Node.js installation does not need to be removed or
downgraded. This repository installs Node.js 22 under `.tools/` and the desktop
scripts automatically put that copy first on `PATH`. Other projects can continue
using your system Node.js 24 installation.

Confirm the Python tools PowerShell is using:

```powershell
python --version
uv --version
where.exe python
```

### 2. Prepare Python and Node dependencies

Install the exact Node.js 22 version declared in `.node-version`. The installer
downloads the official Windows x64 archive from `nodejs.org`, verifies its
SHA-256 checksum against the official release checksum file, and extracts it
only inside this worktree:

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\install_node22.ps1
```

Activate the project-local Node version in the current PowerShell session:

```powershell
. .\scripts\use_node22.ps1
node --version
npm --version
where.exe node
```

The first `where.exe node` result should be under:

```text
D:\Repositorios\pycloud\.tools\node-v22.23.1-win-x64\node.exe
```

Dot-source `use_node22.ps1` again in each new PowerShell session where you run
`node`, `npm`, or `npx` manually. The desktop start/build batch scripts select
the local version automatically and do not require this activation step.

Create/synchronize the local Python environment and install the dependencies
used by the API, desktop sidecar, tests, documentation, and PyInstaller:

```powershell
uv sync --extra desktop --extra test --extra docs

uv pip install `
  --python .\.venv\Scripts\python.exe `
  "pyinstaller>=6" `
  pyinstaller-hooks-contrib `
  -e ".[desktop]"
```

Install the exact Node dependency versions from `ui/package-lock.json`:

```powershell
cd .\ui
npm ci
cd ..
```

Run this preparation again after either Python or Node lockfiles change.

### 3. Run browser mode with a working frontend and backend

Browser mode uses two terminals. In the first terminal, start the normal,
deployable REST API:

```powershell
cd D:\Repositorios\pycloud
.\.venv\Scripts\python.exe -m uvicorn api.server:app `
  --host 127.0.0.1 `
  --port 8000 `
  --reload
```

The following addresses should become available:

- API health: `http://127.0.0.1:8000/health`
- OpenAPI documentation: `http://127.0.0.1:8000/docs`

In a second terminal, start the React application:

```powershell
cd D:\Repositorios\pycloud\ui
. ..\scripts\use_node22.ps1
npm run dev
```

Open `http://localhost:5173`. Vite proxies `/api/*` requests to
`http://localhost:8000`, so browser development does not require permissive
CORS settings. The API configures every UI client with `sso=True`. If no valid
cached session exists, the React application shows its Login card; SSO opens
only after the user presses **Login**.

### 4. Run Electron development mode

The coordinated development script starts Vite and Electron. Electron then
starts `desktop.backend` on an ephemeral loopback port and passes the renderer
the address and per-launch access token:

```powershell
cd D:\Repositorios\pycloud
.\scripts\start_desktop_app.bat
```

Electron uses the same SSO-only Login card as browser mode. Environment/PAT
credentials are intentionally not accepted by either UI host; those
authentication methods remain available to Python, console, and C# callers.

### 5. Run automated verification

Run the offline Python unit suite:

```powershell
cd D:\Repositorios\pycloud
.\.venv\Scripts\python.exe -m pytest tests\unit -q
```

Run the React/Electron tests, TypeScript check, and production renderer build:

```powershell
cd D:\Repositorios\pycloud\ui
. ..\scripts\use_node22.ps1
npm test -- --run
npm run typecheck
npm run build
```

Optionally validate the documentation:

```powershell
cd D:\Repositorios\pycloud
.\.venv\Scripts\python.exe -m mkdocs build --strict
```

The Python sidecar can also validate its imports and configuration without
starting a persistent server:

```powershell
cd D:\Repositorios\pycloud
$env:PYCLOUD_DESKTOP_TOKEN = "local-check-token"
.\.venv\Scripts\python.exe -m desktop.backend --check
Remove-Item Env:PYCLOUD_DESKTOP_TOKEN
```

The expected output is:

```json
{ "type": "check", "ok": true }
```

### 6. Generate the complete Windows installer

The installer does **not** exist after only building the backend sidecar or the
React application. Run the repository build script and wait for its final
`[done]` message:

First close any terminal currently running `start_desktop_app.bat`, Vite, or
Electron. Windows does not allow `npm ci` to replace native modules while a
running process has loaded them. Check for worktree development processes with:

```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\desktop_dev_processes.ps1
```

If necessary, stop only the processes launched from this worktree:

```powershell
powershell -ExecutionPolicy Bypass `
  -File .\scripts\desktop_dev_processes.ps1 `
  -Stop
```

Then build:

```powershell
cd D:\Repositorios\pycloud
.\scripts\build_desktop_exe.bat
```

The script performs all production stages in order:

1. Selects and verifies the project-local Node 22 version.
2. Runs `npm ci` using a repository-local cache.
3. Builds `desktop/backend.py` as a PyInstaller sidecar.
4. Builds React and packages Electron with the sidecar as an external resource.
5. Creates an assisted, all-users NSIS `Setup.exe`.
6. Starts the unpacked application in check mode to verify the UI/backend lifecycle.

The three output locations have different purposes:

| Output                                     | Meaning                                                                 |
| ------------------------------------------ | ----------------------------------------------------------------------- |
| `ui\backend\psrcloud-backend\`             | Python sidecar only; this is not the desktop installer.                 |
| `ui\release\win-unpacked\`                 | Unpacked Electron application, automatically smoke-tested by the build. |
| `ui\release\PSR Cloud-<version>-Setup.exe` | Shareable Windows x64 installer.                                        |

The version is read from `psr/cloud/version.py`. Locate the generated installer
without hardcoding that version:

```powershell
Get-ChildItem .\ui\release -Filter "PSR Cloud-*-Setup.exe"
```

For version `0.4.0`, the expected path is:

```text
D:\Repositorios\pycloud\ui\release\PSR Cloud-0.4.0-Setup.exe
```

The current internal-test build is unsigned. Windows SmartScreen can therefore
show an unknown-publisher warning. PSR's release signing process can be applied
later without changing the application/backend boundary.

### 7. Smoke-test the packaged application

Before running the installer, test the unpacked application. Check mode starts
the bundled sidecar, verifies `/health`, loads the packaged UI, shuts down the
sidecar, and exits:

```powershell
cd D:\Repositorios\pycloud
.\scripts\test_desktop_package.ps1
```

The expected final line is `[pass] Packaged UI and backend sidecar started and
shut down cleanly.` The main build script runs this check automatically.

### 8. Install and manually test the application

First uninstall any older Squirrel-based PSR Cloud test build. Then run the
newest installer returned by `Get-ChildItem`. The wizard requests administrator
approval, defaults to `C:\PSR\PSR Cloud`, lets the user choose another folder,
and creates desktop and Start Menu shortcuts for all users. An unsigned internal
build may trigger Windows SmartScreen; bypass it only for a trusted artifact.

On a clean disposable Windows test machine, an administrator can automate the
install/check/shortcut/uninstall validation:

```powershell
cd D:\Repositorios\pycloud
.\scripts\test_desktop_installer.ps1 -ConfirmCleanMachine
```

This test intentionally refuses to run when PSR Cloud is already registered.
Pass `-KeepInstalled` only when the test installation should remain afterward.

After installation, verify these workflows:

1. Confirm that the application shows the Login card without opening SSO automatically.
2. Press **Login**, complete SSO, and confirm that the user, clusters, cases, and catalogs load.
3. Open the user account menu, log out, and confirm that the Login card returns.
4. Log in again and confirm the saved cluster and execution-grid view are preserved.
5. Run a case and verify incremental progress followed by one terminal result.
6. Download results, select a native Windows folder, and verify progress.
7. Cancel the folder picker and confirm that no download starts.
8. Exercise logs, cancellation, status polling, and error presentation.
9. Close Electron and confirm that the Python sidecar also exits.

The grid's **Refresh** button is at the far right of its toolbar. Use the adjacent
**Last 7/30/90/180 days** selector to choose how far back to load executions;
30 days is the default. Larger periods can slow updates. The selected period also
applies to Insights and is saved with the view, even when filters are excluded.
See [grid views and controls](ui/README.md) for details.

After closing the application, this command should return no
`psrcloud-backend` process:

```powershell
Get-Process psrcloud-backend -ErrorAction SilentlyContinue
```

### Troubleshooting

- **`Setup.exe` is missing:** the electron-builder/NSIS stage did not finish. Look for
  `[done] Installer artifact:` in the build output. A successful sidecar build
  under `ui\backend` is not sufficient.
- **`npm ci` reports `EPERM` while unlinking a `.node` file:** a Vite or Electron
  process has the native module loaded. Close the development terminal or run
  `powershell -ExecutionPolicy Bypass -File .\scripts\desktop_dev_processes.ps1 -Stop`,
  then build again. The build script detects worktree processes before `npm ci`
  and stops immediately if dependency installation fails.
- **Project-local Node is not installed:** run
  `powershell -ExecutionPolicy Bypass -File .\scripts\install_node22.ps1` from
  the repository root. Your global Node 24 installation can remain installed.
- **Manual `node` or `npm` commands still report Node 24:** dot-source
  `. .\scripts\use_node22.ps1` in that PowerShell session, then confirm that
  `where.exe node` lists the worktree's `.tools` path first. The desktop batch
  scripts perform this selection automatically.
- **No project virtual environment was found:** run the dependency preparation
  commands in step 2 from the repository root.
- **PyInstaller cannot be imported:** rerun the `uv pip install` command in step
  2, making sure `--python` points to this worktree's `.venv`.
- **Port 5173 or 8000 is busy:** stop the process using that port, or change the
  browser development port/proxy target. Electron itself uses an ephemeral API
  port.
- **Old installer remains beside the new one:** use the version reported by
  `psr/cloud/version.py`, or select the newest `Setup.exe` by modification time.
- **The new setup reports an existing installation unexpectedly:** uninstall the
  previous Squirrel test build before installing the first NSIS build.

## Documentation

Full documentation lives in [`docs/`](docs/index.md): authentication, querying
PSR Cloud options, common workflows, and the complete API reference.

Preview it locally with:

```sh
uv run mkdocs serve
```

## Contributing

The architecture and change conventions live in
[`docs/architecture/`](docs/architecture/overview.md) — start there (an
auto-generated [code map](docs/architecture/index.md) shows where every public
symbol lives). The "definition of done" for any change is in
[`docs/architecture/conventions.md`](docs/architecture/conventions.md).
