2026-09-08 02:12:15 -04:00
|
|
|
|
# Contributing
|
|
|
|
|
|
|
2026-09-16 12:03:22 -04:00
|
|
|
|
Thanks for helping with OTKO — a SAP2000-style desktop GUI for
|
|
|
|
|
|
OpenSeesPy. Early-stage project: the bar is architecture cleanliness, not
|
|
|
|
|
|
feature count. If your change breaks a layering rule below, it won't
|
|
|
|
|
|
merge — no matter how useful the feature.
|
2026-09-08 02:12:15 -04:00
|
|
|
|
|
|
|
|
|
|
## Dev setup
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m venv .venv
|
2026-09-16 12:03:22 -04:00
|
|
|
|
source .venv/bin/activate # Linux / macOS
|
|
|
|
|
|
# .venv\Scripts\activate # Windows
|
|
|
|
|
|
pip install -e ".[gui,dev]"
|
2026-09-08 02:12:15 -04:00
|
|
|
|
pre-commit install
|
|
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-16 12:03:22 -04:00
|
|
|
|
`pip install -e ".[gui,dev]"` pulls the Qt/PyVista desktop stack plus the
|
|
|
|
|
|
dev tools. For a headless checkout (core + services only, no Qt) use
|
|
|
|
|
|
`pip install -e .` instead. Python 3.10+; on Windows use 3.12+.
|
|
|
|
|
|
|
|
|
|
|
|
Launch the GUI with:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
python -m otko
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`pre-commit install` wires ruff + mypy into your local git hooks so
|
|
|
|
|
|
obvious issues are caught before a commit. Run it once per clone.
|
|
|
|
|
|
|
2026-09-08 02:12:15 -04:00
|
|
|
|
## Before opening a PR
|
|
|
|
|
|
|
2026-09-16 12:03:22 -04:00
|
|
|
|
Run the verify commands in this order and make sure they are all clean:
|
|
|
|
|
|
|
2026-09-08 02:12:15 -04:00
|
|
|
|
```bash
|
|
|
|
|
|
ruff check src tests
|
2026-09-16 12:03:22 -04:00
|
|
|
|
ruff format src tests # line-length 100, E501 ignored
|
2026-09-08 02:12:15 -04:00
|
|
|
|
mypy src/otko/core src/otko/services
|
2026-09-16 12:03:22 -04:00
|
|
|
|
pytest -m "not slow" # CI gate: lint → this, on 3.10/3.11/3.12 × ubuntu/windows/macos
|
2026-09-08 02:12:15 -04:00
|
|
|
|
```
|
|
|
|
|
|
|
2026-09-16 12:03:22 -04:00
|
|
|
|
Focused runs are faster while iterating: `pytest tests/unit` (pure logic,
|
|
|
|
|
|
milliseconds), `pytest tests/gui -k <name>` (pytest-qt, needs a display;
|
|
|
|
|
|
Linux GUI tests want `xvfb-run -a pytest ...`), and
|
|
|
|
|
|
`pytest tests/integration -k <name>` (real OpenSeesPy runs). Single test:
|
|
|
|
|
|
`pytest tests/unit/test_project.py::test_name -q`. Markers: `gui`, `slow`.
|
|
|
|
|
|
|
2026-09-08 02:12:15 -04:00
|
|
|
|
## Architectural rules (enforced in review)
|
|
|
|
|
|
|
2026-09-16 12:03:22 -04:00
|
|
|
|
OTKO uses a strict one-way **MVVM + service** architecture. Dependencies
|
|
|
|
|
|
flow outward-in only:
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
views → viewmodels → services → core
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`commands` sits alongside the bridge and owns every model mutation.
|
|
|
|
|
|
|
|
|
|
|
|
1. `core/` is pure Python — stdlib + numpy + pydantic. It may **not**
|
|
|
|
|
|
import Qt or `openseespy`. Period.
|
|
|
|
|
|
2. `services/` may use `core` + `h5py` + `openseespy`, but may **not**
|
|
|
|
|
|
import Qt.
|
|
|
|
|
|
3. `views/` (PySide6/pyvistaqt) may **not** import `openseespy` directly —
|
|
|
|
|
|
go through a service. No business logic in `views`.
|
|
|
|
|
|
4. `viewmodels/` bridges `core` ↔ Qt (signals, `QUndoStack`).
|
|
|
|
|
|
5. `commands/` holds the `QUndoCommand` subclasses; all model mutations
|
|
|
|
|
|
go through `commands`, not ad-hoc edits in `views`.
|
|
|
|
|
|
6. Public functions and methods need type hints and a docstring.
|
|
|
|
|
|
7. New domain entities go through Pydantic validation.
|
|
|
|
|
|
8. Long-running operations (>50 ms) run off the GUI thread (`AnalysisWorker`
|
|
|
|
|
|
in a `QThread`, cancelled via `isInterruptionRequested()`; results cross
|
|
|
|
|
|
threads as a lightweight `ResultsHandle` written to HDF5).
|
|
|
|
|
|
|
|
|
|
|
|
The full package map and the fixed OpenSeesPy command order the runner
|
|
|
|
|
|
emits live in [`docs/architecture.md`](docs/architecture.md). Never
|
|
|
|
|
|
reorder the runner's commands.
|
|
|
|
|
|
|
|
|
|
|
|
## Branch model
|
|
|
|
|
|
|
|
|
|
|
|
`main` is the default and integration branch. Work on short-lived topic
|
|
|
|
|
|
branches cut from `main` — `feat/<topic>`, `fix/<topic>`, or
|
|
|
|
|
|
`docs/<topic>` — and open a pull request against `main`. Do not commit
|
|
|
|
|
|
directly to `main` from an agent session. There is no `develop` branch.
|
2026-09-08 02:12:15 -04:00
|
|
|
|
|
|
|
|
|
|
## Commit style
|
|
|
|
|
|
|
|
|
|
|
|
Conventional Commits — `feat:`, `fix:`, `refactor:`, `docs:`, `test:`,
|
2026-09-16 12:03:22 -04:00
|
|
|
|
`chore:`, `ci:`. Keep each commit focused; a `feat:` commit should add a
|
|
|
|
|
|
feature, not mix one in with unrelated refactors.
|
|
|
|
|
|
|
|
|
|
|
|
## Documentation
|
|
|
|
|
|
|
|
|
|
|
|
If your change is user-visible, update
|
|
|
|
|
|
[`docs/QUICK_GUIDE.md`](docs/QUICK_GUIDE.md) and the relevant roadmap or
|
|
|
|
|
|
ADR entry. Project model files are `.osmodel` (Pydantic-validated JSON);
|
|
|
|
|
|
regenerate the checked-in `examples/*.osmodel` from their scripts with
|
|
|
|
|
|
`python examples/<name>.py` rather than hand-editing them.
|