Metadata-Version: 2.4
Name: sysml-windstorm
Version: 0.8.0
Summary: SysML v2.0 Parser
License: MIT
License-File: LICENSE
Author: Christopher Cox
Author-email: chris.cox@westfall.io
Requires-Python: >=3.9
Classifier: Development Status :: 3 - Alpha
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Dist: fire (==0.7.1)
Requires-Dist: jinja2 (==3.1.4)
Requires-Dist: requests (==2.32.3)
Project-URL: Bug Tracker, https://github.com/Westfall-io/windstorm/issues
Project-URL: Homepage, https://github.com/Westfall-io/windstorm
Description-Content-Type: text/markdown

# windstorm

SysML 2.0 Analysis Toolset — model-driven templating for any text-based file.

[![PyPI version](https://badge.fury.io/py/sysml-windstorm.svg)](https://badge.fury.io/py/sysml-windstorm)
[![PyPI status](https://img.shields.io/pypi/status/sysml-windstorm.svg)](https://pypi.python.org/pypi/sysml-windstorm/)
[![Coverage Status](https://coveralls.io/repos/github/Westfall-io/windstorm/badge.svg)](https://coveralls.io/github/Westfall-io/windstorm)
![Docstring Coverage](https://raw.githubusercontent.com/Westfall-io/windstorm/main/doc-cov.svg)
[![MIT license](https://img.shields.io/badge/License-MIT-blue.svg)](https://lbesson.mit-license.org/)

---

## Overview

**windstorm** reads a SysML v2 analysis model — either from a live
[SysMLv2 API](https://github.com/Systems-Modeling/SysML-v2-API-Services)
or from **local `.sysml` files** — and turns the model's parameter values
into variables you can use inside any text-based template.

It extends [Jinja2](https://jinja.palletsprojects.com) with a custom
`windstorm(...)` function, so anything Jinja can render (files, code,
config, Excel workbooks) can be driven from your SysML model.

---

## Installation

```bash
pip install sysml-windstorm
```

Requires Python 3.9+.

---

## Quick start

Point windstorm at a model and render a template:

```bash
# Live API mode: pull parameters from a SysMLv2 API
windstorm -a http://your_api_address:9000 -p project_uuid element_name

# Local-file mode: use a directory of .sysml files (or exported JSON) instead
windstorm --source ./models element_name
```

Inside any text file, reference model values with Jinja:

```
{{ windstorm('myVar') }}
```

Run windstorm with that file's directory as the input:

```bash
windstorm --source ./models --in_directory ./templates --out_directory ./output element_name
```

---

## Modes

### 1. Live SysMLv2 API (default)

When pointed at an appropriate SysMLv2 API, windstorm pulls all parameter
values for the requested analysis element and makes them available to the
Jinja templates.

```
windstorm -a http://your_api_address:9000 -p project_uuid element_name
```

### 2. Local files / offline (`--source`)

Since **v0.6.0**, windstorm can read a model entirely from local files —
no API address and no network required:

```
windstorm --source ./models element_name
```

`--source DIR` accepts a directory that may contain **both**:

- **Raw `.sysml` files** — windstorm runs the
  [windtrader-java](https://github.com/Westfall-io/windtrader-java)
  `batch-export` CLI (Java 21+) to produce the export JSON next to each
  source, then loads it. Set the jar location with the `WINDTRADER_JAR`
  environment variable.
- **Already-exported JSON** (`*.export.json`) — loaded directly, no Java
  needed. This is the fastest path and is fully offline.

The two are **complementary**: a `.sysml` whose export is missing or older
than the source is (re-)exported; an up-to-date export is reused. Mixing
both in one tree is fine.

All files build an in-memory element store (the same API-shaped graph
windstorm uses in live mode), so templating behaves identically.

> **Note:** `--source` is deterministic *given a stable set of exports*.
> The jar writes `<path>.export.json` **next to each source** (the tree must
> be writable), and re-export happens only when an export is missing or
> **older than or equal to** its `.sysml` (equal catches same-tick writes).
> `--dump-export-json DIR` (must be **outside**
> `--source`) copies the full export bundle to a directory you can reuse as
> a Java-free `--source` on later runs.

---

## Templating

Any text-based file (`.json`, `.py`, `.txt`, `.m`, ...) can be templated
with Jinja. Versions after 0.4.0 also support **Excel files**, even though
they are not pure text.

windstorm supports all Jinja features (for loops, if statements, filters,
etc.) and adds the custom `windstorm()` function to resolve a model value
by tool-variable name.

Example template (`template.txt`):

```
{{ windstorm('deltaT') }}
```

Example with a loop:

```
{% for w in windstorm('weights') %}{{ w }}{% if not loop.last %},{% endif %}{% endfor %}
```

Run windstorm with `--in_directory` (template folder) and `--out_directory`
(rendered output):

```bash
windstorm -a http://your_api_address:9000 -p project_uuid \
  --in_directory ./templates --out_directory ./output \
  element_name
```

---

## CLI reference

```
windstorm [element_name] [options]

Options:
  -a, --api URL              SysMLv2 API address (default: http://sysml2-dev.intercax.com:9000)
  -p, --project_id UUID      Specific project UUID (default: latest project)
  --source DIR               Read from local .sysml / .export.json files instead of an API
  --dump-export-json DIR     Copy the full export bundle to DIR (must be outside --source)
  --exports-only             Trust existing .export.json files; never invoke Java
  --skip-export-errors       Skip .sysml files whose export fails, don't abort
  -t, --element_type TYPE    Element type to select (default: AnalysisCaseDefinition; also AnalysisCaseUsage, VerificationCaseDefinition, VerificationCaseUsage)
  --in_directory DIR         Template input directory (default: .)
  --out_directory DIR        Rendered output directory (default: .)
  --insecure                 Disable TLS verification
  --debug                    Enable debug logging
  --force_render_error_continue  Keep rendering sibling files after an error
```

---

## Using windstorm as a library (exception contract)

`windstorm()` is the programmatic entrypoint (the CLI is a thin wrapper around
it). When it cannot resolve a requested element, marker, or template key it
raises **`WindstormLookupError`** (a subclass of `KeyError`) — a *data-level*
condition, not a process crash:

```python
from windstorm.main import windstorm
from windstorm.common.errors import WindstormLookupError

try:
    windstorm("verify_case1", source="./models", element_type="VerificationCaseDefinition")
except WindstormLookupError as e:
    record = {"requirement": "REQ-12", "status": "UNVERIFIABLE", "detail": str(e)}
```

This lets an automation pipeline record a per-requirement result and keep going
instead of aborting. The CLI boundary catches `WindstormLookupError` at the top
level and exits with **code 1** (so shell scripts can distinguish "lookup
failed" from success).

> Windstorm used to call `sys.exit()` / raise a bare `KeyError` from its
> internal lookup paths, which made programmatic driving impossible — a missing
> element killed the process. The fix landed in **#96**.

**Behavior note:** a missing template key used to exit with **code 0** (looking
like success); it now aborts that render with `WindstormLookupError` (exit
`1` at the CLI). Pipelines that previously treated a missing key as a silent
no-op will now see it fail — that is the intended, safer behaviour.

---

## What windstorm extracts (element/marker contract)

Windstorm renders a model by selecting a *case* — an element of a supported
element type — and walking its owned inputs for tool variables. For an element
to be **windstorm-visible**, two things must line up:

1. **The element type is selectable.** Pass it with `-t / --element_type`:
   - `AnalysisCaseDefinition` (the default) / `AnalysisCaseUsage`
   - `VerificationCaseDefinition` / `VerificationCaseUsage`
   Selection matches the element's exported `@type` exactly.

2. **The case carries a `ToolExecution` marker.** The case (or its action)
   declares `metadata ToolExecution { toolName = "Windstorm"; }`. This is the
   marker windstorm uses to recognize a tool entry point. Its inputs then hang
   `ToolVariable` metadata: `in <var> = <expr> {@ToolVariable {name = "<name>";}}`
   — that `<name>` is the key you reference in templates via
   `{{ windstorm("<name>") }}`.

The supported scope is **Analysis and Verification** cases (issue #95). A
minimal verification case looks like:

```sysml
package Verify {
    public import AnalysisTooling::*;

    verification def verify_case1 {       // -> VerificationCaseDefinition
        subject A;
        metadata ToolExecution {          // -> windstorm-visible marker
            toolName = "Windstorm";
        }
        in engine_weight = 5.0 {@ToolVariable {name = "deltaT";}}
    }
}
```

Render it with:

```bash
windstorm verify_case1 --source models/ -t VerificationCaseDefinition
```

The adapter preserves these markers when reconstructing API-shaped payloads
from the real Java export, so the contract holds end-to-end through the
`windtrader-java` export round-trip.

---
## Development

```bash
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
pytest tests
```

The test suite uses **committed mocks** (API-shaped JSON under
`tests/mocks/`) — no live API or Jupyter kernel required. To regenerate
the mocks from the `.sysml` fixtures, run:

```bash
python ./tests/mocks/make_mocks.py
```

(Requires a SysML Jupyter kernel; CI does not do this — the committed
mocks are authoritative for tests.)

### Release

Releases are automated by the GitHub Release Workflow on every push to
`main`:

1. Tests + coverage must pass.
2. `python-semantic-release` parses commit messages (emoji parser) to
   decide the next version:
   - `:boom:` → major bump
   - `:sparkles:` (and friends) → minor bump
   - `:bug:`, `:ambulance:`, etc. → patch bump
3. On a release, the package is published to **PyPI** and a **GitHub
   Release** is created.

---

## License

MIT

