diff --git a/.github/workflows/build_osx.yml b/.github/workflows/build_osx.yml index c758470a79..b090b92774 100644 --- a/.github/workflows/build_osx.yml +++ b/.github/workflows/build_osx.yml @@ -21,12 +21,12 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: submodules: recursive - name: Checkout Build Repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: IfcOpenShell/build-outputs path: ./build diff --git a/.github/workflows/build_pyodide.yml b/.github/workflows/build_pyodide.yml index 050b542cff..d951643ea0 100644 --- a/.github/workflows/build_pyodide.yml +++ b/.github/workflows/build_pyodide.yml @@ -9,13 +9,13 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: submodules: recursive path: IfcOpenShell - name: Checkout Build Repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: IfcOpenShell/build-outputs path: ifcopenshell_build diff --git a/.github/workflows/build_rocky.yml b/.github/workflows/build_rocky.yml index 0de26089f6..1ca0d8695f 100644 --- a/.github/workflows/build_rocky.yml +++ b/.github/workflows/build_rocky.yml @@ -35,12 +35,12 @@ jobs: aws --version - name: Checkout Repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: submodules: recursive - name: Checkout Build Repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: IfcOpenShell/build-outputs path: ./build diff --git a/.github/workflows/build_rocky_arm.yml b/.github/workflows/build_rocky_arm.yml index e7401f1f63..7bdf6af0f8 100644 --- a/.github/workflows/build_rocky_arm.yml +++ b/.github/workflows/build_rocky_arm.yml @@ -35,12 +35,12 @@ jobs: aws --version - name: Checkout Repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: submodules: recursive - name: Checkout Build Repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: IfcOpenShell/build-outputs path: ./build diff --git a/.github/workflows/build_win.yml b/.github/workflows/build_win.yml index 5b976f7886..888b0f3605 100644 --- a/.github/workflows/build_win.yml +++ b/.github/workflows/build_win.yml @@ -27,12 +27,12 @@ jobs: steps: - name: Checkout Repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: submodules: recursive - name: Checkout Build Repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: IfcOpenShell/build-outputs path: ${{ matrix.deps_dir }} diff --git a/.github/workflows/ci-bcf-pypi.yml b/.github/workflows/ci-bcf-pypi.yml index 349b071740..62330db87e 100644 --- a/.github/workflows/ci-bcf-pypi.yml +++ b/.github/workflows/ci-bcf-pypi.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 # https://github.com/actions/checkout + - uses: actions/checkout@v7 # https://github.com/actions/checkout - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax diff --git a/.github/workflows/ci-bonsai-choco.yml b/.github/workflows/ci-bonsai-choco.yml index c4204f7d02..a811dbe195 100644 --- a/.github/workflows/ci-bonsai-choco.yml +++ b/.github/workflows/ci-bonsai-choco.yml @@ -34,7 +34,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: fetch-tags: true fetch-depth: 0 diff --git a/.github/workflows/ci-bonsai-daily.yml b/.github/workflows/ci-bonsai-daily.yml index df7bff1fff..137e2386bc 100644 --- a/.github/workflows/ci-bonsai-daily.yml +++ b/.github/workflows/ci-bonsai-daily.yml @@ -65,7 +65,7 @@ jobs: config: short_name: macos steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified @@ -98,7 +98,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout bonsai_unstable_repo repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: IfcOpenShell/bonsai_unstable_repo token: ${{ secrets.IFCOPENBOT_TOKEN }} diff --git a/.github/workflows/ci-bonsai.yml b/.github/workflows/ci-bonsai.yml index 6fcc61658f..3eeb5b8d5d 100644 --- a/.github/workflows/ci-bonsai.yml +++ b/.github/workflows/ci-bonsai.yml @@ -48,7 +48,7 @@ jobs: config: short_name: macos steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified diff --git a/.github/workflows/ci-bsdd-pypi.yaml b/.github/workflows/ci-bsdd-pypi.yaml index e7c326a59a..683365e3ed 100644 --- a/.github/workflows/ci-bsdd-pypi.yaml +++ b/.github/workflows/ci-bsdd-pypi.yaml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax diff --git a/.github/workflows/ci-ifc4d-pypi.yaml b/.github/workflows/ci-ifc4d-pypi.yaml index c08b26d9c0..8bd1bc206f 100644 --- a/.github/workflows/ci-ifc4d-pypi.yaml +++ b/.github/workflows/ci-ifc4d-pypi.yaml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax diff --git a/.github/workflows/ci-ifc5d-pypi.yaml b/.github/workflows/ci-ifc5d-pypi.yaml index 635f60c985..2ff30152fb 100644 --- a/.github/workflows/ci-ifc5d-pypi.yaml +++ b/.github/workflows/ci-ifc5d-pypi.yaml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax diff --git a/.github/workflows/ci-ifccityjson-pypi.yaml b/.github/workflows/ci-ifccityjson-pypi.yaml index f16a57cffd..ecbae379ce 100644 --- a/.github/workflows/ci-ifccityjson-pypi.yaml +++ b/.github/workflows/ci-ifccityjson-pypi.yaml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax diff --git a/.github/workflows/ci-ifcclash-pypi.yaml b/.github/workflows/ci-ifcclash-pypi.yaml index bbde274804..4a7f2687c3 100644 --- a/.github/workflows/ci-ifcclash-pypi.yaml +++ b/.github/workflows/ci-ifcclash-pypi.yaml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax diff --git a/.github/workflows/ci-ifcconvert.yml b/.github/workflows/ci-ifcconvert.yml index d4cd7fc55a..ab664fa56e 100644 --- a/.github/workflows/ci-ifcconvert.yml +++ b/.github/workflows/ci-ifcconvert.yml @@ -37,7 +37,7 @@ jobs: short_name: macosm164 } steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax diff --git a/.github/workflows/ci-ifccsv-pypi.yaml b/.github/workflows/ci-ifccsv-pypi.yaml index 04d7a76937..1a56708c56 100644 --- a/.github/workflows/ci-ifccsv-pypi.yaml +++ b/.github/workflows/ci-ifccsv-pypi.yaml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax diff --git a/.github/workflows/ci-ifcdiff-pypi.yaml b/.github/workflows/ci-ifcdiff-pypi.yaml index eba7ad237f..76e9aa758d 100644 --- a/.github/workflows/ci-ifcdiff-pypi.yaml +++ b/.github/workflows/ci-ifcdiff-pypi.yaml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax diff --git a/.github/workflows/ci-ifcedit-pypi.yaml b/.github/workflows/ci-ifcedit-pypi.yaml index d961c72f56..967e4fc712 100644 --- a/.github/workflows/ci-ifcedit-pypi.yaml +++ b/.github/workflows/ci-ifcedit-pypi.yaml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 with: python-version: '3.11' diff --git a/.github/workflows/ci-ifcfm-pypi.yaml b/.github/workflows/ci-ifcfm-pypi.yaml index c658bbf3ef..9d7c7d6fca 100644 --- a/.github/workflows/ci-ifcfm-pypi.yaml +++ b/.github/workflows/ci-ifcfm-pypi.yaml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax diff --git a/.github/workflows/ci-ifcmcp-pypi.yaml b/.github/workflows/ci-ifcmcp-pypi.yaml index 388c10217a..f476f98ccb 100644 --- a/.github/workflows/ci-ifcmcp-pypi.yaml +++ b/.github/workflows/ci-ifcmcp-pypi.yaml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 with: python-version: '3.11' diff --git a/.github/workflows/ci-ifcopenshell-conda-daily.yml b/.github/workflows/ci-ifcopenshell-conda-daily.yml index 3d18316963..0e5bd4cbce 100644 --- a/.github/workflows/ci-ifcopenshell-conda-daily.yml +++ b/.github/workflows/ci-ifcopenshell-conda-daily.yml @@ -21,7 +21,7 @@ jobs: date: ${{ steps.date.outputs.date }} verdate: ${{ steps.verdate.outputs.verdate }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Set env run: echo ok go @@ -75,7 +75,7 @@ jobs: echo "ARTIFACTS_DIR=/home/runner/work/artifacts" >> $GITHUB_ENV fi - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: submodules: recursive diff --git a/.github/workflows/ci-ifcopenshell-docker.yml b/.github/workflows/ci-ifcopenshell-docker.yml index 50978c9d6f..94a63d2dbb 100644 --- a/.github/workflows/ci-ifcopenshell-docker.yml +++ b/.github/workflows/ci-ifcopenshell-docker.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-22.04 needs: activate steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: submodules: recursive @@ -86,7 +86,7 @@ jobs: name: Docker Build, Tag, Push steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: lfs: true diff --git a/.github/workflows/ci-ifcopenshell-python-pypi.yml b/.github/workflows/ci-ifcopenshell-python-pypi.yml index 7988a0bc6a..2d4a7218ef 100644 --- a/.github/workflows/ci-ifcopenshell-python-pypi.yml +++ b/.github/workflows/ci-ifcopenshell-python-pypi.yml @@ -47,7 +47,7 @@ jobs: short_name: macosm164 } steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: submodules: recursive - uses: actions/setup-python@v6 # https://github.com/actions/setup-python diff --git a/.github/workflows/ci-ifcopenshell-python.yml b/.github/workflows/ci-ifcopenshell-python.yml index fe99f4857b..a3afa9e734 100644 --- a/.github/workflows/ci-ifcopenshell-python.yml +++ b/.github/workflows/ci-ifcopenshell-python.yml @@ -38,7 +38,7 @@ jobs: short_name: macosm164 } steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: submodules: recursive - uses: actions/setup-python@v6 # https://github.com/actions/setup-python diff --git a/.github/workflows/ci-ifcpatch-pypi.yaml b/.github/workflows/ci-ifcpatch-pypi.yaml index 79692933ce..49ab85a6fb 100644 --- a/.github/workflows/ci-ifcpatch-pypi.yaml +++ b/.github/workflows/ci-ifcpatch-pypi.yaml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax diff --git a/.github/workflows/ci-ifcquery-pypi.yaml b/.github/workflows/ci-ifcquery-pypi.yaml index 9dc39a9304..dd2482cc0b 100644 --- a/.github/workflows/ci-ifcquery-pypi.yaml +++ b/.github/workflows/ci-ifcquery-pypi.yaml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 with: python-version: '3.11' diff --git a/.github/workflows/ci-ifcsverchok-daily.yml b/.github/workflows/ci-ifcsverchok-daily.yml index ac3edc546b..85019fc9ae 100644 --- a/.github/workflows/ci-ifcsverchok-daily.yml +++ b/.github/workflows/ci-ifcsverchok-daily.yml @@ -25,7 +25,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified diff --git a/.github/workflows/ci-ifcsverchok.yml b/.github/workflows/ci-ifcsverchok.yml index f3c8aba0a2..98979c396c 100644 --- a/.github/workflows/ci-ifcsverchok.yml +++ b/.github/workflows/ci-ifcsverchok.yml @@ -19,7 +19,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: architecture: 'x64' # optional x64 or x86. Defaults to x64 if not specified diff --git a/.github/workflows/ci-ifctester-org.yml b/.github/workflows/ci-ifctester-org.yml index 7ec6cbdc46..3ca981e064 100644 --- a/.github/workflows/ci-ifctester-org.yml +++ b/.github/workflows/ci-ifctester-org.yml @@ -10,9 +10,9 @@ jobs: publish_website: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - name: Checkout ifctester_org_static_html - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: IfcOpenShell/ifctester_org_static_html token: ${{ secrets.IFCOPENBOT_TOKEN }} diff --git a/.github/workflows/ci-ifctester-pypi.yml b/.github/workflows/ci-ifctester-pypi.yml index b024d7685d..7dbc627765 100644 --- a/.github/workflows/ci-ifctester-pypi.yml +++ b/.github/workflows/ci-ifctester-pypi.yml @@ -18,7 +18,7 @@ jobs: strategy: fail-fast: false steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: actions/setup-python@v6 # https://github.com/actions/setup-python with: python-version: '3.11' # Version range or exact version of a Python version to use, using SemVer's version range syntax diff --git a/.github/workflows/ci-ifcwrap-standalone.yml b/.github/workflows/ci-ifcwrap-standalone.yml index 9bd901791b..3cfe17e8e1 100644 --- a/.github/workflows/ci-ifcwrap-standalone.yml +++ b/.github/workflows/ci-ifcwrap-standalone.yml @@ -34,7 +34,7 @@ jobs: runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: submodules: recursive diff --git a/.github/workflows/ci-lint.yaml b/.github/workflows/ci-lint.yaml index b1098e8607..d18e125dfe 100644 --- a/.github/workflows/ci-lint.yaml +++ b/.github/workflows/ci-lint.yaml @@ -12,7 +12,7 @@ jobs: MIN_BLENDER_PY_VERSION: "3.11" steps: - name: Action - checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Action - install python uses: actions/setup-python@v6 @@ -55,11 +55,17 @@ jobs: black --diff --check . | black-codeclimate | python .github/workflows/black_to_github_annotations.py continue-on-error: true - - name: ty check - id: ty - run: | - poe ty-venv - poe ty + - name: ty check (venv setup) + run: poe ty-venv + + - name: ty check (bonsai) + id: ty-bonsai + run: poe ty-bonsai + continue-on-error: true + + - name: ty check (ios) + id: ty-ios + run: poe ty-ios continue-on-error: true - name: Ruff check @@ -109,7 +115,10 @@ jobs: if [ "${{ steps.ruff.outcome }}" != "success" ]; then echo "::error::Ruff check failed, see Summary or 'ruff' step for the details." && ERROR=1 fi - if [ "${{ steps.ty.outcome }}" != "success" ]; then - echo "::error::ty check failed, see 'ty check' step for the details." && ERROR=1 + if [ "${{ steps.ty-bonsai.outcome }}" != "success" ]; then + echo "::error::ty check (bonsai) failed, see 'ty check (bonsai)' step for the details." && ERROR=1 + fi + if [ "${{ steps.ty-ios.outcome }}" != "success" ]; then + echo "::error::ty check (ios) failed, see 'ty check (ios)' step for the details." && ERROR=1 fi exit $ERROR diff --git a/.github/workflows/ci-pyodide-wasm-release.yml b/.github/workflows/ci-pyodide-wasm-release.yml index 0e3017f819..fa951c44f8 100644 --- a/.github/workflows/ci-pyodide-wasm-release.yml +++ b/.github/workflows/ci-pyodide-wasm-release.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout IfcOpenShell - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Install uv uses: astral-sh/setup-uv@v7 @@ -25,7 +25,7 @@ jobs: echo "name=$(basename $WHEEL)" >> $GITHUB_OUTPUT - name: Checkout wasm-wheels - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: IfcOpenShell/wasm-wheels path: wasm-wheels diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 255ce63308..c6cdd9b1a7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,7 +43,7 @@ jobs: CLICOLOR_FORCE: "1" CMAKE_COLOR_DIAGNOSTICS: "ON" steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 with: submodules: recursive @@ -257,7 +257,7 @@ jobs: cd ../ifcdiff && make test || ERROR=1 cd ../ifcpatch && make test || ERROR=1 pip install -e ../ifc5d --no-deps - pip install odfpy xlsxwriter + pip install odfpy openpyxl cd ../ifc5d && make test || ERROR=1 pip install -e ../ifcquery --no-deps cd ../ifcquery && make test || ERROR=1 diff --git a/.github/workflows/docs-deployment-unstable.yml b/.github/workflows/docs-deployment-unstable.yml index 55e2fd5518..25cefaff4a 100644 --- a/.github/workflows/docs-deployment-unstable.yml +++ b/.github/workflows/docs-deployment-unstable.yml @@ -11,7 +11,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v7 - name: Set up Python uses: actions/setup-python@v6 diff --git a/.github/workflows/publish-aichat-app.yaml b/.github/workflows/publish-aichat-app.yaml index 20369577f5..06504fff16 100644 --- a/.github/workflows/publish-aichat-app.yaml +++ b/.github/workflows/publish-aichat-app.yaml @@ -27,12 +27,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout (recursive) - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: submodules: recursive fetch-depth: 0 - name: Checkout intermediate Pages repo - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: IfcOpenShell/aichat_ifcopenshell_org_static_html ref: gh-pages diff --git a/.github/workflows/publish-bonsai-releases.yml b/.github/workflows/publish-bonsai-releases.yml index 6d423deadf..b55ed37079 100644 --- a/.github/workflows/publish-bonsai-releases.yml +++ b/.github/workflows/publish-bonsai-releases.yml @@ -7,7 +7,7 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v7 - uses: astral-sh/setup-uv@v7 diff --git a/.github/workflows/publish-pyodide-demo-app.yml b/.github/workflows/publish-pyodide-demo-app.yml index 6b0141fc29..c2fcc6cc33 100644 --- a/.github/workflows/publish-pyodide-demo-app.yml +++ b/.github/workflows/publish-pyodide-demo-app.yml @@ -27,12 +27,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout (recursive) - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: submodules: recursive fetch-depth: 0 - name: Checkout intermediate Pages repo - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: repository: IfcOpenShell/wasm_ifcopenshell_org_static_html ref: gh-pages diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4880ed8177..bda527177e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v7 with: submodules: recursive - name: Install C++ dependencies diff --git a/README.md b/README.md index a44a6ff78c..a6bb653c54 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ and many other libraries, CLI apps, and more. Support is also provided for auxil For more information, see: -* [IfcOpenShell Website](http://ifcopenshell.org) +* [IfcOpenShell Website](https://ifcopenshell.org) * [IfcOpenShell Documentation](https://docs.ifcopenshell.org) * [IfcOpenShell C++ Installation](https://docs.ifcopenshell.org/ifcopenshell/installation.html) * [IfcOpenShell Python Installation](https://docs.ifcopenshell.org/ifcopenshell-python/installation.html) diff --git a/choco/bonsai/blenderbim.nuspec b/choco/bonsai/blenderbim.nuspec index a6ca7b107d..c769b698dd 100644 --- a/choco/bonsai/blenderbim.nuspec +++ b/choco/bonsai/blenderbim.nuspec @@ -3,7 +3,7 @@ blenderbim-nightly - blenderbim_build_version-alpha + blenderbim_build_version https://github.com/IfcOpenShell/IfcOpenShell fbpyr diff --git a/choco/bonsai/choco_release.py b/choco/bonsai/choco_release.py index ea53798c5d..0521ed4d6b 100644 --- a/choco/bonsai/choco_release.py +++ b/choco/bonsai/choco_release.py @@ -3,11 +3,12 @@ apt update && apt install git wget curl ptpython mono-devel micro mkdir -p /home/runner/work/IfcOpenShell && cd /home/runner/work/IfcOpenShell git clone https://github.com/IfcOpenShell/IfcOpenShell -cd /home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/ +cd /home/runner/work/IfcOpenShell/IfcOpenShell/choco/bonsai/ micro choco_release.py # paste this script, comment out push command export CHOCO_TOKEN="secret_choco_release_token" python3 choco_release.py """ + import datetime import hashlib import os @@ -28,7 +29,7 @@ def get_repo_tag_names() -> list[str]: def request_repo_info(url: str): - req = request.Request(url) + req = request.Request(url) resp = request.urlopen(req) if not resp.status == 200: print(f"[ERROR] could not contact server: {url}") @@ -85,13 +86,15 @@ def run(command: str) -> None: start = datetime.datetime.now() -URL_CHOCO_PACKAGE = "https://community.chocolatey.org/packages/blender" -URL_BLENDER_CMAKE = "https://raw.githubusercontent.com/blender/blender/{}/build_files/cmake/Modules/FindPythonLibsUnix.cmake" -RE_BLENDER_VERSION_MIN_MAJ = r"Latest Version.+Blender (\d+\.\d+)\..+" -RE_BLENDER_VERSION_MIN_MAJ_PAT = r"Latest Version.+Blender (\d+\.\d+\.\d+)" +URL_CHOCO_PACKAGE = "https://community.chocolatey.org/packages/blender" +URL_BLENDER_CMAKE = ( + "https://raw.githubusercontent.com/blender/blender/{}/build_files/cmake/Modules/FindPythonLibsUnix.cmake" +) +RE_BLENDER_VERSION_MIN_MAJ = r"Latest Version.+Blender (\d+\.\d+)\..+" +RE_BLENDER_VERSION_MIN_MAJ_PAT = r"Latest Version.+Blender (\d+\.\d+\.\d+)" RE_BLENDER_PYTHON_VERSION_MAJ_MIN = r"\(_PYTHON_VERSION_SUPPORTED (\d+\.\d+)\)" -BLENDERBIM_DIR = pathlib.Path("/home/runner/work/IfcOpenShell/IfcOpenShell/choco/blenderbim/") +BLENDERBIM_DIR = pathlib.Path("/home/runner/work/IfcOpenShell/IfcOpenShell/choco/bonsai/") print("_____ check choco release needed?") @@ -148,7 +151,7 @@ print(f"{blender_python_version_maj_min=}") python_version = f"py{found[0].replace('.', '')}" print(f"{python_version=}") -blenderbim_build_version = target_release_tag.replace("blenderbim-", "") +blenderbim_build_version = target_release_tag.replace("bonsai-", "") # url_blenderbim_py3x_win_zip release_zip_file_name, url_blenderbim_py3x_win_zip = get_release_zip(target_release_tag) @@ -166,15 +169,15 @@ topics = { "path": HERE_DIR / "blenderbim.nuspec", "key_values": { "latest_blender_version_maj_min_pat": latest_blender_release_maj_min_pat, - "blenderbim_build_version" : blenderbim_build_version, + "blenderbim_build_version": blenderbim_build_version, }, }, "install": { "path": HERE_DIR / "tools" / "chocolateyinstall.ps1", "key_values": { - "url_blenderbim_py3x_win_zip" : url_blenderbim_py3x_win_zip, + "url_blenderbim_py3x_win_zip": url_blenderbim_py3x_win_zip, "sha256sum_blenderbim_py3x_win_zip": sha256sum_blenderbim_py3x_win_zip, - "latest_blender_version_maj_min" : blender_version_min_maj, + "latest_blender_version_maj_min": blender_version_min_maj, }, }, "uninstall": { diff --git a/nix/build-all.py b/nix/build-all.py index 7dba287a48..9b39e5c45e 100644 --- a/nix/build-all.py +++ b/nix/build-all.py @@ -121,7 +121,7 @@ import tarfile import threading from datetime import datetime -ssl._create_default_https_context = ssl._create_unverified_context +ssl._create_default_https_context = ssl._create_unverified_context # ty:ignore[invalid-assignment] import time from collections.abc import Generator, Sequence @@ -697,7 +697,10 @@ def build_dependency( compr = "xz" else: raise RuntimeError("fix source for new download type") - download_tarfile = tarfile.open(name=download_tarfile_path, mode=f"r:{compr}") + # ty: false positive bug upstream. + download_tarfile = tarfile.open( + name=download_tarfile_path, mode=f"r:{compr}" + ) # ty:ignore[no-matching-overload] # tarfile seriously doesn't have a function to retrieve the root directory more easily extract_dir_name = os.path.commonprefix([x for x in download_tarfile.getnames() if x != "."]) # run([tar, "--exclude=\"*/*\"", "-tf", download_name], cwd=build_dir).strip() no longer works diff --git a/nix/cache_dependencies.py b/nix/cache_dependencies.py index 7d231779ee..3ee03382ea 100644 --- a/nix/cache_dependencies.py +++ b/nix/cache_dependencies.py @@ -68,6 +68,7 @@ def unpack_dependencies(install_dir: Path) -> None: if __name__ == "__main__": + action = None if len(sys.argv) != 2 or (action := sys.argv[1].lower()) not in ("pack", "unpack"): print(__doc__) sys.exit(1) diff --git a/pyproject.toml b/pyproject.toml index d832213792..fca2913fb9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -82,8 +82,6 @@ ignore = [ all = "error" # Structural rules (no deep type inference needed, easier to adapt). -# Has false positives due to ty walrus operator bug. -possibly-unresolved-reference = "ignore" # Maybe later, requires to specify element types for all generics. missing-type-argument = "ignore" # Conflicts with `bpy` props defined using annotations. @@ -111,7 +109,6 @@ no-matching-overload = "ignore" not-subscriptable = "ignore" unsupported-dynamic-base = "ignore" unsupported-operator = "ignore" -type-assertion-failure = "ignore" [tool.ty.environment] extra-paths = [ @@ -159,7 +156,8 @@ exclude = [ [tool.poe.tasks] dev-setup.sequence = [ - {cmd = "uv sync"}, + # 3.13 is chosen because it's the version used in the latest Bonsai. + {cmd = "uv sync --python 3.13"}, {cmd = "uv pip install -e ./src/bsdd/"}, {cmd = "uv pip install -e ./src/ifcopenshell-python/[advanced,dev]"}, {cmd = "uv pip install -e ./src/ifcedit/"}, @@ -195,7 +193,6 @@ format.sequence = ["black", "ruff"] cmake-format = "gersemi . --in-place" [tool.poe.tasks.ty-ios] -# --ignore unresolved-reference: walrus operator false positives in ty. cmd = """ ty check nix/ @@ -213,7 +210,6 @@ cmd = """ src/ifcpatch src/ifctester --python=src/ifcopenshell-python/.venv - --ignore unresolved-reference """ [tool.poe.tasks.bonsai-deps] diff --git a/requirements-tools.txt b/requirements-tools.txt index 33a2505707..1388c7c518 100644 --- a/requirements-tools.txt +++ b/requirements-tools.txt @@ -1,5 +1,5 @@ black==26.3.1 -ruff==0.15.12 +ruff==0.16.0 poethepoet -ty==0.0.59 -gersemi==0.26.1 +ty==0.0.63 +gersemi==0.28.0 diff --git a/src/bcf/bcf/v3/bcfapi.py b/src/bcf/bcf/v3/bcfapi.py index 368c294cd0..515b2f2fe2 100644 --- a/src/bcf/bcf/v3/bcfapi.py +++ b/src/bcf/bcf/v3/bcfapi.py @@ -188,6 +188,8 @@ class BcfClient: response.raise_for_status() return response.status_code, response.text except requests.exceptions.HTTPError as errh: + response = errh.response + assert response is not None print(f"message: {response.reason}' '{response.status_code}, {errh}") return response.status_code, response.reason @@ -206,6 +208,8 @@ class BcfClient: response.raise_for_status() return response.status_code, response.text except requests.exceptions.HTTPError as errh: + response = errh.response + assert response is not None print(f"message: {response.reason}' '{response.status_code}, {errh}") return response.status_code, response.reason @@ -222,6 +226,8 @@ class BcfClient: response.raise_for_status() return response.status_code, response.text except requests.exceptions.HTTPError as errh: + response = errh.response + assert response is not None print(f"message: {response.reason}' '{response.status_code}, {errh}") return response.status_code, response.reason diff --git a/src/bonsai/bonsai/bim/data/assets/default.css b/src/bonsai/bonsai/bim/data/assets/default.css index 68307f4c3f..d82d97e8f2 100644 --- a/src/bonsai/bonsai/bim/data/assets/default.css +++ b/src/bonsai/bonsai/bim/data/assets/default.css @@ -24,10 +24,28 @@ a text, a tspan { fill: blue !important; text-decoration: underline;} a:hover { cursor: pointer; } .cut { fill: black; stroke: black; stroke-linecap: 'round'; stroke-width: 0.35; fill-rule: evenodd; } .projection { fill: white; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; } -.surface { stroke: none; fill: #fff; fill-rule: evenodd; } -.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; } -.IfcAnnotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.25; } -.IfcGeographicElement { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 1; } +/* SVG edge classification (issue #3668): see edge-classification.md. These select directly on + the element (each classified projection edge carries its own class), so they win over + the inherited .projection rule above regardless of specificity. */ +path.outline { stroke: black; stroke-width: 0.35; stroke-opacity: 1; } +path.boundary { stroke: black; stroke-width: 0.3; stroke-opacity: 0.9; } +path.crease { stroke: black; stroke-width: 0.25; stroke-opacity: 0.85; } +path.sharp { stroke: black; stroke-width: 0.18; stroke-opacity: 0.7; } +path.flush { stroke: black; stroke-width: 0.1; stroke-opacity: 0.4; } + +/* Debug CSS for troubleshooting edge classification */ +/* +path.outline { stroke: black; stroke-width: 0.35; stroke-opacity: 1; } +path.boundary { stroke: orange; stroke-width: 0.3; stroke-opacity: 0.9; } +path.crease { stroke: green; stroke-width: 0.25; stroke-opacity: 0.85; } +path.sharp { stroke: red; stroke-width: 0.18; stroke-opacity: 0.7; } +path.flush { stroke: blue; stroke-width: 0.1; stroke-opacity: 0.4; } +*/ + +.surface {fill: white; stroke-width: 0.1;} +.annotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.3; } +.IfcAnnotation { fill: none; stroke: black; stroke-linecap: 'round'; stroke-width: 0.3; } +/* .IfcGeographicElement { fill: none; stroke: rgb(150, 150, 150); stroke-linecap: 'round'; stroke-dasharray: 1, 2;} */ .PredefinedType-LINEWORK { stroke: black; stroke-width: 0.25; } .PredefinedType-LINEWORK.dashed { stroke-dasharray: 3, 2; } .PredefinedType-LINEWORK.fine { stroke-width: 0.18; stroke: #777777; } diff --git a/src/bonsai/bonsai/bim/data/pset/EPset_Drawing.ifc b/src/bonsai/bonsai/bim/data/pset/EPset_Drawing.ifc index 114b766ff8..aa39cc353b 100644 --- a/src/bonsai/bonsai/bim/data/pset/EPset_Drawing.ifc +++ b/src/bonsai/bonsai/bim/data/pset/EPset_Drawing.ifc @@ -5,7 +5,7 @@ FILE_NAME('EPset_Drawing.ifc','2020-01-01T00:00:00',$,$,'EPset_Drawing','EPset_D FILE_SCHEMA(('IFC4')); ENDSEC; DATA; -#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation/DRAWING',(#23,#22,#27,#24,#29,#30,#19,#12,#26,#9,#8,#7,#6,#4,#18,#11,#5,#20,#25,#14,#10,#17,#28,#16,#3,#21,#13,#15,#2)); +#1=IFCPROPERTYSETTEMPLATE('2JhNIvqZrFnAgxfhK0XVQX',$,'EPset_Drawing','',.PSET_OCCURRENCEDRIVEN.,'IfcAnnotation/DRAWING',(#23,#22,#27,#24,#29,#30,#19,#12,#26,#9,#8,#7,#6,#4,#18,#11,#5,#20,#25,#14,#10,#17,#28,#16,#3,#21,#13,#15,#2,#31,#32,#33,#34,#35,#36,#37)); #2=IFCSIMPLEPROPERTYTEMPLATE('23JavTMk98ZxXhrUEnjAcf',$,'TargetView','',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); #3=IFCSIMPLEPROPERTYTEMPLATE('1yVWUt5H9DAOuu0OaMMLpe',$,'Scale','The scale of this drawing represented as a numerator and denominator, such as 1/100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); #4=IFCSIMPLEPROPERTYTEMPLATE('3gsuPBtU93b8f0gg1pjkq6',$,'HumanScale','The scale of this drawing in human readable format, such as 1:100',.P_SINGLEVALUE.,'IfcLabel',$,$,$,$,$,.READWRITE.); @@ -35,5 +35,12 @@ DATA; #28=IFCSIMPLEPROPERTYTEMPLATE('1YSnFzurrEyRNtoLdmmddP',$,'BringToFront','The objects with these SVG classes will render in front of all other objects.Ex: IfcBeam, IfcColumn',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.); #29=IFCSIMPLEPROPERTYTEMPLATE('0lP6Y8q9v2QhDnR4sT7uVx',$,'PerspectiveShiftX','Horizontal perspective camera shift stored as drawing metadata using Blender camera shift units.',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.); #30=IFCSIMPLEPROPERTYTEMPLATE('2mR8b1NcW5EoFyG7hJ9kLp',$,'PerspectiveShiftY','Vertical perspective camera shift stored as drawing metadata using Blender camera shift units.',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.); +#31=IFCSIMPLEPROPERTYTEMPLATE('1cFVJnqT13m8ItkMHaI1tp',$,'UseEdgeClassification','Enable the boundary/outline/sharp/crease/flush SVG edge classification scheme (issue #3668). When false, drawings use the original unclassified linework.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); +#32=IFCSIMPLEPROPERTYTEMPLATE('2kB$mxBgnBUvhjh0Ti0c4P',$,'RenderCreases','Whether to render ''crease'' (concave) edges. Only relevant when UseEdgeClassification is enabled.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); +#33=IFCSIMPLEPROPERTYTEMPLATE('3MSIJNW$T8r9Hl12kk0BY$',$,'ValleyAngleMinDegrees','Minimum concave dihedral deviation from flat, in degrees, for a projection edge to be classified as ''crease'' rather than ''flush''.',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.); +#34=IFCSIMPLEPROPERTYTEMPLATE('2epSGfC4bFM9gb1X7zBIp4',$,'RenderSharp','Whether to render ''sharp'' (convex) edges. Only relevant when UseEdgeClassification is enabled.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); +#35=IFCSIMPLEPROPERTYTEMPLATE('3TZwsEjkr5WRDKcgrYzSIA',$,'RidgeAngleMinDegrees','Minimum convex dihedral deviation from flat, in degrees, for a projection edge to be classified as ''sharp'' rather than ''flush''.',.P_SINGLEVALUE.,'IfcReal',$,$,$,$,$,.READWRITE.); +#36=IFCSIMPLEPROPERTYTEMPLATE('2Jua$lO754vgZOkBoHM2gA',$,'RenderFlush','Whether to render ''flush'' edges (dihedral deviation below both ridge/valley thresholds). Only relevant when UseEdgeClassification is enabled.',.P_SINGLEVALUE.,'IfcBoolean',$,$,$,$,$,.READWRITE.); +#37=IFCSIMPLEPROPERTYTEMPLATE('1zM9sia2L8RQDnWZxgUwlZ',$,'JoinClasses','Comma separated list of IFC classes whose cut linework will be joined together when they meet (e.g. mitred at a corner).\X2\000A\X0\Defaults to ''IfcWall,IfcSlab'' if not set. Override to also join other classes, such as ''IfcWall,IfcSlab,IfcCovering''.',.P_SINGLEVALUE.,'IfcText',$,$,$,$,$,.READWRITE.); ENDSEC; END-ISO-10303-21; diff --git a/src/bonsai/bonsai/bim/data/webui/sioserver.py b/src/bonsai/bonsai/bim/data/webui/sioserver.py index 91c3c4e8eb..4098778fac 100644 --- a/src/bonsai/bonsai/bim/data/webui/sioserver.py +++ b/src/bonsai/bonsai/bim/data/webui/sioserver.py @@ -10,6 +10,7 @@ if bonsai_lib_path: import argparse import base64 import json +import urllib.parse import xml.etree.ElementTree as ET import pystache @@ -18,8 +19,53 @@ from aiohttp import web sio_port = 8080 # default port + +def get_asset_version() -> str: + """A cache-busting token appended to locally served static asset URLs. + + Browsers otherwise keep serving a stale cached copy of static/js and + static/css after Bonsai ships a code change, until the user does a hard + refresh. Using the Bonsai version (which includes the build's commit + hash) means the token changes on every shipped update. + """ + if bonsai_version: + return urllib.parse.quote(bonsai_version, safe="") + # Fallback for standalone runs without BONSAI_VERSION set (e.g. running + # sioserver.py directly outside of Blender): derive a token from the + # newest mtime among the static assets, so it still changes whenever the + # shipped files change. + static_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "static") + latest_mtime = 0 + for root, _dirs, files in os.walk(static_dir): + for name in files: + latest_mtime = max(latest_mtime, int(os.path.getmtime(os.path.join(root, name)))) + return f"dev-{latest_mtime}" + + +asset_version = get_asset_version() + + +@web.middleware +async def no_cache_static_middleware(request: web.Request, handler): + """Force revalidation of locally served static assets. + + Query-string version stamping (see `asset_version`) busts the cache for + the HTML-referenced entry points, but JS files that statically import + other local modules (e.g. cost.js/gantt.js importing utilities/costui.js) + reference those modules by an un-stamped relative path. Marking all + /static/ and /jsgantt/ responses as no-cache makes browsers always + revalidate with the server (a cheap conditional GET / 304 when nothing + changed), so nested imports also pick up shipped changes without + requiring a hard refresh. + """ + response = await handler(request) + if request.path.startswith("/static/") or request.path.startswith("/jsgantt/"): + response.headers["Cache-Control"] = "no-cache, must-revalidate" + return response + + sio = socketio.AsyncServer(cors_allowed_origins="*", async_mode="aiohttp", max_http_buffer_size=10000000) -app = web.Application() +app = web.Application(middlewares=[no_cache_static_middleware]) sio.attach(app) @@ -199,28 +245,28 @@ class BlenderNamespace(socketio.AsyncNamespace): async def schedules(request): with open("templates/index.html", "r") as f: template = f.read() - html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version}) + html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version, "v": asset_version}) return web.Response(text=html_content, content_type="text/html") async def costing(request): with open("templates/costing.html", "r") as f: template = f.read() - html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version}) + html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version, "v": asset_version}) return web.Response(text=html_content, content_type="text/html") async def sequencing(request): with open("templates/gantt.html", "r") as f: template = f.read() - html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version}) + html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version, "v": asset_version}) return web.Response(text=html_content, content_type="text/html") async def documentation(request): with open("templates/drawings.html", "r") as f: template = f.read() - html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version}) + html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version, "v": asset_version}) return web.Response(text=html_content, content_type="text/html") @@ -229,7 +275,7 @@ async def documentation(request): async def demo(request): with open("templates/demo.html", "r") as f: template = f.read() - html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version}) + html_content = pystache.render(template, {"port": sio_port, "version": bonsai_version, "v": asset_version}) return web.Response(text=html_content, content_type="text/html") diff --git a/src/bonsai/bonsai/bim/data/webui/static/js/gantt.js b/src/bonsai/bonsai/bim/data/webui/static/js/gantt.js index 5eaaffa841..630571e63d 100644 --- a/src/bonsai/bonsai/bim/data/webui/static/js/gantt.js +++ b/src/bonsai/bonsai/bim/data/webui/static/js/gantt.js @@ -244,7 +244,7 @@ function addGanttElement(blenderId, tasks, workSched, filename) { vShowTaskInfoLink: 1, // Show link in tool tip (0/1) vShowEndWeekDate: 0, // Show/Hide the date for the last day of the week in header for daily vUseSingleCell: 10000, // Set the threshold cell per table row (Helps performance for large data. - vFormatArr: ["Day", "Week", "Month", "Quarter"], // Even with setUseSingleCell using Hour format on such a large chart can cause issues in some browsers, + vFormatArr: ["Hour", "Day", "Week", "Month", "Quarter"], // vUseSingleCell keeps Hour usable on large charts. vShowRes: true, // Disable the resource column. vShowComp: false, // Disable the completion column. vShowDur: false, // Disable the duration column, because jsgantt doesn't calculate durations the way we want. diff --git a/src/bonsai/bonsai/bim/data/webui/static/js/utilities/costui.js b/src/bonsai/bonsai/bim/data/webui/static/js/utilities/costui.js index 7c9f5ed1a1..3c10101488 100644 --- a/src/bonsai/bonsai/bim/data/webui/static/js/utilities/costui.js +++ b/src/bonsai/bonsai/bim/data/webui/static/js/utilities/costui.js @@ -250,6 +250,14 @@ export class CostUI { }, }); + CostUI.addRibbonButton({ + text: "Download CSV", + icon: "fa-solid fa-file-csv", + callback: () => { + CostUI.downloadCsv(); + }, + }); + CostUI.addRibbonButton({ text: "Hide Schedules", icon: "fa-regular fa-eye-slash", @@ -523,6 +531,81 @@ export class CostUI { } } + static downloadCsv() { + const tables = document.querySelectorAll("table[id^='cost-items-']"); + if (tables.length === 0) { + alert("No cost schedule loaded to export!"); + return; + } + tables.forEach((table) => { + const scheduleId = table.id.split("-").pop(); + const csv = CostUI.tableToCsv(table); + if (csv === null) { + return; + } + const nameEl = document.querySelector( + "#cost-schedule-container-" + scheduleId + " .form-header span" + ); + const scheduleName = nameEl + ? nameEl.textContent + : "cost_schedule_" + scheduleId; + CostUI.triggerCsvDownload(csv, scheduleName + ".csv"); + }); + } + + static tableToCsv(table) { + const escapeCsvCell = (value) => { + const text = (value === null || value === undefined ? "" : value) + .toString() + .trim(); + if (/[",\n]/.test(text)) { + return '"' + text.replace(/"/g, '""') + '"'; + } + return text; + }; + + const cellText = (cell) => { + const input = cell.querySelector("input"); + return input ? input.value : cell.innerText; + }; + + // The Actions column only holds buttons (edit/delete/etc), not data. + const isDataColumn = (column) => column && column !== "Actions"; + + const headerCells = Array.from(table.querySelectorAll("thead th")).filter( + (th) => isDataColumn(th.getAttribute("data-column")) + ); + if (headerCells.length === 0) { + return null; + } + + const rows = [headerCells.map((th) => escapeCsvCell(th.textContent)).join(",")]; + + table.querySelectorAll("tbody tr").forEach((row) => { + const cells = Array.from(row.children).filter((cell) => + isDataColumn(cell.getAttribute("data-column")) + ); + if (cells.length === 0) { + return; // e.g. the "No cost items found" placeholder row. + } + rows.push(cells.map((cell) => escapeCsvCell(cellText(cell))).join(",")); + }); + + return rows.join("\n"); + } + + static triggerCsvDownload(csvContent, filename) { + const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }); + const url = URL.createObjectURL(blob); + const link = document.createElement("a"); + link.href = url; + link.download = filename; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + URL.revokeObjectURL(url); + } + static createCostTable({ costSchedule, currency, callbacks }) { const preferences = CostUI.getColumnPreferences(); const isScheduleOfRates = costSchedule.PredefinedType === "SCHEDULEOFRATES"; diff --git a/src/bonsai/bonsai/bim/data/webui/templates/costing.html b/src/bonsai/bonsai/bim/data/webui/templates/costing.html index 6e076203c3..948ba4a00e 100644 --- a/src/bonsai/bonsai/bim/data/webui/templates/costing.html +++ b/src/bonsai/bonsai/bim/data/webui/templates/costing.html @@ -7,7 +7,7 @@ - +