[build-system] requires = ["hatchling>=1.24"] build-backend = "hatchling.build" [project] name = "otko" dynamic = ["version"] description = "OTKO — a modern, SAP2000-style desktop GUI for OpenSeesPy." readme = "README.md" requires-python = ">=3.10" license = { file = "LICENSE" } authors = [{ name = "Ozan" }] keywords = ["opensees", "openseespy", "structural", "earthquake", "fem", "gui"] classifiers = [ "Development Status :: 2 - Pre-Alpha", "Intended Audience :: Science/Research", "Topic :: Scientific/Engineering", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "License :: OSI Approved :: GNU Affero General Public License v3", "Operating System :: OS Independent", ] dependencies = [ # ── Headless base (core + services, no Qt required) ────────────────────── "pydantic>=2.5", "numpy>=1.26", "h5py>=3.10", "openseespy==3.8.0.0", "openseespywin==3.8.0.0 ; sys_platform == 'win32'", ] [project.optional-dependencies] # ── GUI / visualization stack (desktop app only) ────────────────────────── # Install with: pip install -e ".[gui]" (desktop) # pip install -e ".[gui,dev]" (desktop + dev tools) # Headless / web reuse: pip install -e . (base only, no Qt pulled in) gui = [ "PySide6>=6.6", "pyvista>=0.43", "pyvistaqt>=0.11", "vtk>=9.3", "pyqtgraph>=0.13", "imageio[ffmpeg]>=2.34", "plotly>=5.18", ] dev = [ "pytest>=7.4", "pytest-qt>=4.2", "pytest-cov>=4.1", "ruff>=0.4", "mypy>=1.8", "pre-commit>=3.6", "hatch>=1.9", "tomli>=2.0; python_version < '3.11'", ] docs = [ "sphinx>=7.2", "myst-parser>=2.0", "furo>=2024.1.29", ] [project.scripts] otko = "otko.__main__:main" [project.urls] Homepage = "https://github.com/ogunc/otko" Issues = "https://github.com/ogunc/otko/issues" [tool.hatch.build.targets.wheel] packages = ["src/otko"] [tool.hatch.version] path = "src/otko/_const.py" # ─────────── Ruff ─────────── [tool.ruff] line-length = 100 target-version = "py310" src = ["src", "tests"] [tool.ruff.lint] select = [ "E", "W", # pycodestyle "F", # pyflakes "I", # isort "B", # bugbear "UP", # pyupgrade "N", # pep8-naming "SIM", # simplify "RUF", # ruff-specific ] ignore = [ "E501", # line-length handled by the formatter "RUF001", # ambiguous unicode: engineering notation (×, ≈, ²) is deliberate "RUF002", "RUF003", ] [tool.ruff.lint.per-file-ignores] # Engineering symbols (A, I, L, E, P) and `importorskip`-then-import blocks # are normal in tests. "tests/**" = ["E402", "E741", "N802", "N806", "N815"] # Qt requires camelCase signal names and PascalCase event overrides. "src/otko/views/**" = ["N802", "N815"] "src/otko/viewmodels/**" = ["N815"] # Material fields mirror the OpenSees parameter names verbatim (cR1, epsU, ...). "src/otko/core/materials/__init__.py" = ["N815"] # Engineering symbols in numeric code: L (length), Iz/Iy (second moments). "src/otko/services/section_properties.py" = ["N806"] "src/otko/views/canvas3d/model_renderer.py" = ["N802", "N806", "N815"] [tool.ruff.format] quote-style = "double" # ─────────── Mypy ─────────── [tool.mypy] python_version = "3.10" warn_unused_configs = true plugins = [] [[tool.mypy.overrides]] module = ["otko.core.*", "otko.services.*", "otko.viewmodels.*"] strict = true [[tool.mypy.overrides]] module = ["openseespy.*", "pyvista.*", "pyvistaqt.*", "vtk.*", "pyqtgraph.*"] ignore_missing_imports = true # ─────────── Pytest ─────────── [tool.pytest.ini_options] minversion = "7.0" addopts = "-ra -q --strict-markers" testpaths = ["tests"] markers = [ "gui: marks tests that require a Qt event loop", "slow: marks slow tests (deselect with '-m \"not slow\"')", ] [tool.coverage.run] branch = true source = ["src/otko"] omit = ["src/otko/views/*"] # GUI excluded from coverage targets [tool.coverage.report] exclude_lines = ["if TYPE_CHECKING:", "raise NotImplementedError"] # Conservative floor for the headless suite; raise deliberately with evidence. fail_under = 50