Metadata-Version: 2.4
Name: rfem-table-export
Version: 0.4.0
Summary: Terminal UI for exporting RFEM 6 input and result tables to Excel
Project-URL: Homepage, https://github.com/Mark-Milkis/rfem-table-export
Project-URL: Repository, https://github.com/Mark-Milkis/rfem-table-export
Project-URL: Issues, https://github.com/Mark-Milkis/rfem-table-export/issues
Author-email: Mark Milkis <markmilkis@gmail.com>
License: MIT
License-File: LICENSE
Keywords: dlubal,engineering,excel,export,rfem,structural,tui
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: Microsoft :: Windows
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering
Requires-Python: >=3.11
Requires-Dist: dlubal-api==2.14.3
Requires-Dist: openpyxl>=3.1
Requires-Dist: pandas>=2.0
Requires-Dist: pint>=0.20
Requires-Dist: textual>=0.80
Description-Content-Type: text/markdown

# rfem-table-export

A terminal UI (TUI) for exporting [RFEM 6](https://www.dlubal.com/en/products/rfem-fea-software/what-is-rfem)
tables — both model **inputs** and analysis **results** — to a formatted Excel
workbook. It attaches to an already-running RFEM 6 process over the Dlubal gRPC
API, lets you confirm the active model, pick tables from a tree that mirrors the
RFEM GUI navigators, and writes a new timestamped `.xlsx` with one sheet per
table plus an info sheet capturing export metadata and any warnings.

The table-picker UX is modeled on SAP2000's "Export Tables to Excel" dialog.

## Features

- **Attach to a live model** — connects to the running RFEM 6 process and shows
  the active model's name and path so you can verify you're exporting the right
  one before you start.
- **Navigator-style table tree** — tables are organized exactly like the RFEM
  GUI (Structure → Basic Objects → Nodes, …; Load Cases and Combinations; Loads;
  Results → Nodes/Members/Lines/Surfaces).
- **Inputs and results** — model objects (nodes, members, surfaces, materials,
  cross-sections, supports, hinges, load cases/combinations, loads) and static
  result tables (support forces, internal forces, deformations) iterated across
  every load case and combination.
- **Object filtering** (`o`) — toggle between *all objects* and *only the
  objects currently selected in the RFEM model* (read live via the API's
  `only_selected` query); the status bar shows a live count of what's selected
  (e.g. "1 member"). To export a saved RFEM object selection or Group of Object
  Selections, activate it in RFEM (which sets the live selection), then toggle to
  *Currently selected objects*. Tables whose rows aren't tied to selectable
  objects (materials, cross-sections, load cases/combinations) ignore the filter
  and always export in full; loads, supports and result rows are filtered by the
  object they act on/report.
- **Case / combination filtering** (`c`) — for result tables, pick exactly which
  load cases and combinations to export from a check-list instead of iterating
  every one.
- **Result-row detail** (`t`) — cycle a result table's rows between *all*,
  *envelope only* (extreme rows whose `tag` ends in `min`/`max`), and *itemized
  only* (the per-location rows, with no `min`/`max` tag).
- **Analysis-aware** — result tables are greyed out and unselectable until the
  model has been solved, so you never queue a result export that can't run.
- **Metric or imperial units** — pick a unit system and every value is
  converted to it (metric: kN, kN·m, m/mm, MPa; imperial: kip, kip·ft, ft/in,
  ksi). Conversion is dimensionality-driven via [Pint](https://pint.readthedocs.io/):
  no multipliers are hard-coded, so compound units (kN·m, kN/m, kN/m²) convert
  correctly and can't be cross-applied. The info sheet records the system and
  the target unit for each magnitude; converted columns carry their unit in the
  header (e.g. `n [kip]`, `area_axial [in²]`).
- **One workbook per export** — a new file is always written to `results/`;
  nothing is overwritten.
- **Resilient** — a single problematic table produces a warning on the info
  sheet rather than aborting the whole export.

## Requirements

- Windows with **RFEM 6.14** installed and running, a model open, and the gRPC
  web service / API enabled.
- A valid Dlubal API key (see [Configuration](#configuration)).
- Python 3.11+.

> **Version pin:** the `dlubal-api` client version must match the RFEM server
> version. This project pins `dlubal-api==2.14.3` for **RFEM 6.14**. If you run a
> different RFEM version, change the pin to match — mismatched versions cause
> object-type enum drift and silently wrong/empty exports.

## Installation

### Run without installing (recommended)

With [uv](https://docs.astral.sh/uv/) you can run the latest published release
directly — no clone, no virtualenv:

```bash
uvx rfem-table-export
```

Pin a specific version (handy for matching your RFEM/`dlubal-api` version):

```bash
uvx rfem-table-export@0.2.0
```

> The console command is also available as `rfem-export`; with `uvx` use the
> distribution name (`rfem-table-export`) so version pinning works.

### From a clone (for development)

```bash
uv sync
```

Or with pip:

```bash
pip install -e .
```

## Usage

1. Start RFEM 6, open and (for result tables) solve your model.
2. Launch the TUI:

   ```bash
   uv run rfem-export
   # or
   python -m rfem_export.app
   ```

3. Confirm the banner shows the correct active model.
4. Navigate the tree and toggle the tables you want:

   | Key     | Action                                    |
   |---------|-------------------------------------------|
   | `space` | Toggle table                              |
   | `a`     | Select all                                |
   | `n`     | Clear selection                           |
   | `o`     | Object scope (all / currently selected)   |
   | `c`     | Case & combination filter (results)       |
   | `t`     | Result rows (all / envelope / itemized)   |
   | `u`     | Cycle units (metric/imperial)             |
   | `e`     | Export                                     |
   | `r`     | Reconnect to RFEM                          |
   | `q`     | Quit                                      |

5. Press `e`. The workbook is written to `results/rfem_export_<model>_<timestamp>.xlsx`.
   The info sheet records the active object and case filters, and each table's
   row lists how many rows the filter kept.

## Units

Choose the unit system with `u` in the TUI, or `--units {metric,imperial}` on
the command line (default: `imperial`). Values are converted by *dimensionality*,
never by a hard-coded factor:

- **Input tables** carry a unit per column straight from the RFEM API
  (`m`, `kN`, `kNm`, `mm⁴`, …). Pint parses that unit, derives its dimensionality
  and computes the conversion.
- **Result tables** expose no units over the API, so each result table declares
  the *SI* unit of its value columns (in [`catalog.py`](rfem_export/catalog.py)).
  Only the dimension is asserted — Pint still computes every factor.

A length lands in feet/metres for geometry but inches/millimetres for section
dimensions and deflections (RFEM's own unit categories drive this). Quantities
with no conversion rule (e.g. dimensionless ratios) are passed through unchanged
and noted in the export log. If Pint isn't installed, values are written in
their source units and the info sheet says so.

## Configuration

The API key is resolved in this order:

1. `--api-key` command-line argument
2. `RFEM_API_KEY` environment variable
3. Dlubal's bundled `config.ini` (`[api_keys] default = …`), scanned across
   installed RFEM versions

```bash
uv run rfem-export --api-key YOUR_KEY
```

## Project layout

```
rfem_export/
  __init__.py
  connection.py   # API-key resolution, attach to RFEM, model info, unit summary
  catalog.py      # curated navigator tree of exportable tables (TableDef / Group)
  units.py        # Pint-based, dimensionality-driven metric/imperial conversion
  extract.py      # object tables + result tables -> pandas DataFrames (+ units)
  excel.py        # workbook writer: one sheet per table + info sheet
  app.py          # Textual TUI + console entry point
examples/
  export_reactions.py   # original single-table proof-of-concept
```

### Extending table coverage

Add a `TableDef` to the `TREE` in [`rfem_export/catalog.py`](rfem_export/catalog.py).
Both extraction and the TUI pick up new entries automatically — inputs via
`get_object_table` for an `ObjectType` (which also supplies per-column units);
results via `get_result_table` across every loading. For a **result** table,
also fill in `result_units` (the SI unit of each value column) so the values can
be converted — result tables carry no units over the API.

## Development

Lint and format with [ruff](https://docs.astral.sh/ruff/) (the same checks CI
runs):

```bash
uvx ruff check .          # lint
uvx ruff format --check . # formatting
uvx ruff format .         # auto-format
```

## Versioning & releases

The package version is **derived automatically from git tags** via
[`hatch-vcs`](https://github.com/ofek/hatch-vcs) — there is no hardcoded
version to bump. Tag `vX.Y.Z` becomes version `X.Y.Z`.

To cut a release:

```bash
git tag v0.2.0
git push origin v0.2.0
```

Pushing the tag triggers the **Release** workflow, which lints, builds the
sdist/wheel, verifies the built version matches the tag, publishes to
[PyPI](https://pypi.org/p/rfem-table-export), and creates a GitHub release.

> **One-time PyPI setup:** publishing uses
> [Trusted Publishing (OIDC)](https://docs.pypi.org/trusted-publishers/), so no
> API token is stored. On PyPI, add a trusted publisher for this project with:
> owner `Mark-Milkis`, repository `rfem-table-export`, workflow
> `release.yml`, and environment `pypi`. (For the very first publish you may
> need to use the "pending publisher" flow since the project doesn't exist on
> PyPI yet.)

## License

[MIT](LICENSE)
