Browse Source

Merge branch 'develop' into feature/#612-expose-submission-to-taipy-core-APIs

Toan Quach 1 year ago
parent
commit
11a1b3069f
41 changed files with 1057 additions and 549 deletions
  1. 174 0
      .github/workflows/build-and-release-dev.yml
  2. 171 0
      .github/workflows/build-and-release-prod.yml
  3. 65 11
      .github/workflows/overall-tests.yml
  4. 3 13
      .github/workflows/packaging.yml
  5. 1 4
      .github/workflows/partial-tests.yml
  6. 3 33
      .github/workflows/publish.yml
  7. 0 173
      .github/workflows/release-dev.yml
  8. 0 106
      .github/workflows/release.yml
  9. 1 1
      .github/workflows/trigger-benchmark.yml
  10. 0 0
      .github/workflows/trigger-integration-tests.yml
  11. 14 0
      MANIFEST.in
  12. 202 155
      frontend/taipy-gui/package-lock.json
  13. 7 5
      frontend/taipy-gui/src/components/Taipy/Chart.spec.tsx
  14. 39 14
      frontend/taipy-gui/src/components/Taipy/Chart.tsx
  15. 3 3
      setup.py
  16. 2 2
      taipy/config/MANIFEST.in
  17. 5 4
      taipy/config/setup.py
  18. 2 2
      taipy/core/MANIFEST.in
  19. 7 1
      taipy/core/job/job.py
  20. 3 3
      taipy/core/setup.py
  21. 4 4
      taipy/gui/MANIFEST.in
  22. 10 2
      taipy/gui/_renderers/builder.py
  23. 2 0
      taipy/gui/_renderers/factory.py
  24. 2 1
      taipy/gui/data/data_accessor.py
  25. 3 0
      taipy/gui/gui.py
  26. 2 0
      taipy/gui/gui_types.py
  27. 3 3
      taipy/gui/setup.py
  28. 1 0
      taipy/gui/utils/__init__.py
  29. 24 0
      taipy/gui/utils/types.py
  30. 7 2
      taipy/gui/viselements.json
  31. 1 1
      taipy/rest/MANIFEST.in
  32. 3 3
      taipy/rest/setup.py
  33. 1 0
      taipy/templates/MANIFEST.in
  34. 3 3
      taipy/templates/setup.py
  35. 16 0
      tests/core/job/test_job.py
  36. 20 0
      tools/release/check_releases.py
  37. 15 0
      tools/release/extract_from_setup.py
  38. 115 0
      tools/release/setup_version.py
  39. 32 0
      tools/release/update_setup.py
  40. 35 0
      tools/release/update_setup_requirements.py
  41. 56 0
      tools/validate_taipy_install.py

+ 174 - 0
.github/workflows/build-and-release-dev.yml

@@ -0,0 +1,174 @@
+name: Build a dev version for all packages and release them
+
+on:
+  workflow_dispatch:
+
+jobs:
+  fetch-versions:
+    runs-on: ubuntu-latest
+    outputs:
+        config_VERSION: ${{ steps.version-setup.outputs.config_VERSION }}
+        core_VERSION: ${{ steps.version-setup.outputs.core_VERSION }}
+        gui_VERSION: ${{ steps.version-setup.outputs.gui_VERSION }}
+        rest_VERSION: ${{ steps.version-setup.outputs.rest_VERSION }}
+        templates_VERSION: ${{ steps.version-setup.outputs.templates_VERSION }}
+        VERSION: ${{ steps.version-setup.outputs.VERSION }}
+        NEW_VERSION: ${{ steps.version-setup.outputs.NEW_VERSION }}
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Dev Version
+        id: version-setup
+        run: |
+          python tools/release/setup_version.py ALL dev >> $GITHUB_OUTPUT
+
+  build-and-release-taipy-dev-packages:
+    needs: [fetch-versions]
+    timeout-minutes: 20
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        package: [config, core, gui, rest, templates]
+      max-parallel: 1
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          ssh-key: ${{secrets.DEPLOY_KEY}}
+      - uses: actions/setup-python@v4
+        with:
+          python-version: 3.9
+      - uses: actions/setup-node@v4
+        with:
+          node-version: '20'
+
+      - name: Extract commit hash
+        shell: bash
+        run: echo "HASH=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
+        id: extract_hash
+
+      - name: Set Build Variables
+        id: set-variables
+        run: |
+          if [ "${{ matrix.package }}" == "config" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.config_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/config" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.config_VERSION}}-config" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-config-${{needs.fetch-versions.outputs.config_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          elif [ "${{ matrix.package }}" == "core" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.core_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/core" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.core_VERSION}}-core" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-core-${{needs.fetch-versions.outputs.core_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          elif [ "${{ matrix.package }}" == "gui" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.gui_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/gui" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.gui_VERSION}}-gui" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-gui-${{needs.fetch-versions.outputs.gui_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          elif [ "${{ matrix.package }}" == "rest" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.rest_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/rest" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.rest_VERSION}}-rest" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-rest-${{needs.fetch-versions.outputs.rest_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          elif [ "${{ matrix.package }}" == "templates" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.templates_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/templates" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.templates_VERSION}}-templates" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-templates-${{needs.fetch-versions.outputs.templates_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          fi
+        shell: bash
+
+      - name: Update setup.requirements.txt
+        run: |
+          python tools/release/update_setup_requirements.py taipy-${{ matrix.package }} \
+            ${{needs.fetch-versions.outputs.config_VERSION}} \
+            ${{needs.fetch-versions.outputs.core_VERSION}} \
+            ${{needs.fetch-versions.outputs.gui_VERSION}} \
+            ${{needs.fetch-versions.outputs.rest_VERSION}} \
+            ${{needs.fetch-versions.outputs.templates_VERSION}}
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install build wheel
+
+      - name: Build package
+        working-directory: ${{ steps.set-variables.outputs.package_dir }}
+        run: python setup.py build_py && python -m build
+
+      - name: Create tag and release
+        working-directory: ${{ steps.set-variables.outputs.package_dir }}
+        run: |
+          gh release create ${{ steps.set-variables.outputs.release_name }} ${{ steps.set-variables.outputs.tar_path }} --target ${{ steps.extract_hash.outputs.HASH }} --prerelease --title ${{ steps.set-variables.outputs.release_name }} --notes "Release Draft ${{ steps.set-variables.outputs.release_name }}"
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Install Package
+        working-directory: ${{ steps.set-variables.outputs.package_dir }}
+        run: |
+          pip install ${{ steps.set-variables.outputs.tar_path }}
+
+  build-and-release-taipy-dev:
+    runs-on: ubuntu-latest
+    needs: [ build-and-release-taipy-dev-packages, fetch-versions ]
+    timeout-minutes: 20
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          ssh-key: ${{secrets.DEPLOY_KEY}}
+      - name: Extract commit hash
+        shell: bash
+        run: echo "HASH=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
+        id: extract_hash
+
+      - name: Set Build Variables
+        id: set-variables
+        run: |
+          echo "package_version=${{needs.fetch-versions.outputs.VERSION}}" >> $GITHUB_OUTPUT
+          echo "release_name=${{needs.fetch-versions.outputs.VERSION}}" >> $GITHUB_OUTPUT
+          echo "tar_path=./dist/${{ github.event.repository.name }}-${{needs.fetch-versions.outputs.VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install build wheel
+
+      - name: Build Taipy package
+        run: python setup.py build_py && python -m build
+
+      - name: Create tag and release Taipy
+        run: |
+          gh release create ${{ steps.set-variables.outputs.release_name }} ${{ steps.set-variables.outputs.tar_path }} --target ${{ steps.extract_hash.outputs.HASH }} --prerelease --title ${{ steps.set-variables.outputs.release_name }} --notes "Release Draft ${{ steps.set-variables.outputs.release_name }}"
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Install Taipy
+        run: |
+          pip install ${{ steps.set-variables.outputs.tar_path }}
+
+      - name: Check Taipy Installation
+        run: |
+          python tools/validate_taipy_install.py
+
+      - name: Download packages
+        run: |
+          gh release download ${{ needs.fetch-versions.outputs.config_VERSION }}-config --skip-existing --dir dist
+          gh release download ${{ needs.fetch-versions.outputs.core_VERSION }}-core --skip-existing --dir dist
+          gh release download ${{ needs.fetch-versions.outputs.gui_VERSION }}-gui --skip-existing --dir dist
+          gh release download ${{ needs.fetch-versions.outputs.rest_VERSION }}-rest --skip-existing --dir dist
+          gh release download ${{ needs.fetch-versions.outputs.templates_VERSION }}-templates --skip-existing --dir dist
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Bundle all packages in main release tag
+        run: |
+          find dist -type f -print0 | xargs -r0 gh release upload ${{ needs.fetch-versions.outputs.VERSION }} --clobber
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Reset changes
+        run: |
+          git reset --hard HEAD
+          git clean -fdx
+
+      - uses: stefanzweifel/git-auto-commit-action@v4
+        with:
+          commit_message: Update version to ${{ needs.fetch-versions.outputs.NEW_VERSION }}

+ 171 - 0
.github/workflows/build-and-release-prod.yml

@@ -0,0 +1,171 @@
+name: Build a dev version for all packages and release them
+
+on:
+  workflow_dispatch:
+
+jobs:
+  fetch-versions:
+    runs-on: ubuntu-latest
+    outputs:
+        config_VERSION: ${{ steps.version-setup.outputs.config_VERSION }}
+        core_VERSION: ${{ steps.version-setup.outputs.core_VERSION }}
+        gui_VERSION: ${{ steps.version-setup.outputs.gui_VERSION }}
+        rest_VERSION: ${{ steps.version-setup.outputs.rest_VERSION }}
+        templates_VERSION: ${{ steps.version-setup.outputs.templates_VERSION }}
+        VERSION: ${{ steps.version-setup.outputs.VERSION }}
+        NEW_VERSION: ${{ steps.version-setup.outputs.NEW_VERSION }}
+    steps:
+      - uses: actions/checkout@v4
+      - name: Setup Dev Version
+        id: version-setup
+        run: |
+          python tools/release/setup_version.py ALL production >> $GITHUB_OUTPUT
+
+  build-and-release-taipy-packages:
+    needs: [fetch-versions]
+    timeout-minutes: 20
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        package: [config, core, gui, rest, templates]
+      max-parallel: 1
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          ssh-key: ${{secrets.DEPLOY_KEY}}
+      - uses: actions/setup-python@v4
+        with:
+          python-version: 3.9
+      - uses: actions/setup-node@v4
+        with:
+          node-version: '20'
+
+      - name: Extract commit hash
+        shell: bash
+        run: echo "HASH=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
+        id: extract_hash
+
+      - name: Set Build Variables
+        id: set-variables
+        run: |
+          if [ "${{ matrix.package }}" == "config" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.config_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/config" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.config_VERSION}}-config" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-config-${{needs.fetch-versions.outputs.config_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          elif [ "${{ matrix.package }}" == "core" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.core_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/core" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.core_VERSION}}-core" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-core-${{needs.fetch-versions.outputs.core_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          elif [ "${{ matrix.package }}" == "gui" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.gui_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/gui" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.gui_VERSION}}-gui" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-gui-${{needs.fetch-versions.outputs.gui_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          elif [ "${{ matrix.package }}" == "rest" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.rest_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/rest" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.rest_VERSION}}-rest" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-rest-${{needs.fetch-versions.outputs.rest_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          elif [ "${{ matrix.package }}" == "templates" ]; then
+            echo "package_version=${{needs.fetch-versions.outputs.templates_VERSION}}" >> $GITHUB_OUTPUT
+            echo "package_dir=./taipy/templates" >> $GITHUB_OUTPUT
+            echo "release_name=${{needs.fetch-versions.outputs.templates_VERSION}}-templates" >> $GITHUB_OUTPUT
+            echo "tar_path=./dist/${{ github.event.repository.name }}-templates-${{needs.fetch-versions.outputs.templates_VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+          fi
+        shell: bash
+
+      - name: Update setup.requirements.txt
+        run: |
+          python tools/release/update_setup_requirements.py taipy-${{ matrix.package }} \
+            ${{needs.fetch-versions.outputs.config_VERSION}} \
+            ${{needs.fetch-versions.outputs.core_VERSION}} \
+            ${{needs.fetch-versions.outputs.gui_VERSION}} \
+            ${{needs.fetch-versions.outputs.rest_VERSION}} \
+            ${{needs.fetch-versions.outputs.templates_VERSION}} \
+            dev
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install build wheel
+
+      - name: Build package
+        working-directory: ${{ steps.set-variables.outputs.package_dir }}
+        run: python setup.py build_py && python -m build
+
+      - name: Create tag and release
+        working-directory: ${{ steps.set-variables.outputs.package_dir }}
+        run: |
+          gh release create ${{ steps.set-variables.outputs.release_name }} ${{ steps.set-variables.outputs.tar_path }} --target ${{ steps.extract_hash.outputs.HASH }} --title ${{ steps.set-variables.outputs.release_name }} --notes "Release ${{ steps.set-variables.outputs.release_name }}"
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Install Package
+        working-directory: ${{ steps.set-variables.outputs.package_dir }}
+        run: |
+          pip install ${{ steps.set-variables.outputs.tar_path }}
+
+  build-and-release-taipy:
+    runs-on: ubuntu-latest
+    needs: [ build-and-release-taipy-dev-packages, fetch-versions ]
+    timeout-minutes: 20
+    steps:
+      - uses: actions/checkout@v4
+        with:
+          ssh-key: ${{secrets.DEPLOY_KEY}}
+      - name: Extract commit hash
+        shell: bash
+        run: echo "HASH=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
+        id: extract_hash
+
+      - name: Set Build Variables
+        id: set-variables
+        run: |
+          echo "package_version=${{needs.fetch-versions.outputs.VERSION}}" >> $GITHUB_OUTPUT
+          echo "release_name=${{needs.fetch-versions.outputs.VERSION}}" >> $GITHUB_OUTPUT
+          echo "tar_path=./dist/${{ github.event.repository.name }}-${{needs.fetch-versions.outputs.VERSION}}.tar.gz" >> $GITHUB_OUTPUT
+
+      - name: Install dependencies
+        run: |
+          python -m pip install --upgrade pip
+          pip install build wheel
+
+      - name: Build Taipy package
+        run: python setup.py build_py && python -m build
+
+      - name: Create tag and release Taipy
+        run: |
+          gh release create ${{ steps.set-variables.outputs.release_name }} ${{ steps.set-variables.outputs.tar_path }} --target ${{ steps.extract_hash.outputs.HASH }} --title ${{ steps.set-variables.outputs.release_name }} --notes "Release ${{ steps.set-variables.outputs.release_name }}"
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Install Taipy
+        run: |
+          pip install ${{ steps.set-variables.outputs.tar_path }}
+
+      - name: Check Taipy Installation
+        run: |
+          python tools/validate_taipy_install.py
+
+      - name: Download packages
+        run: |
+          gh release download ${{ needs.fetch-versions.outputs.config_VERSION }}-config --skip-existing --dir dist
+          gh release download ${{ needs.fetch-versions.outputs.core_VERSION }}-core --skip-existing --dir dist
+          gh release download ${{ needs.fetch-versions.outputs.gui_VERSION }}-gui --skip-existing --dir dist
+          gh release download ${{ needs.fetch-versions.outputs.rest_VERSION }}-rest --skip-existing --dir dist
+          gh release download ${{ needs.fetch-versions.outputs.templates_VERSION }}-templates --skip-existing --dir dist
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Bundle all packages in main release tag
+        run: |
+          find dist -type f -print0 | xargs -r0 gh release upload ${{ needs.fetch-versions.outputs.VERSION }} --clobber
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Reset changes
+        run: |
+          git reset --hard HEAD
+          git clean -fdx

+ 65 - 11
.github/workflows/overall-tests.yml

@@ -1,12 +1,17 @@
 name: Overall Test Workflow
 
 on:
-  pull_request_review:
-    types: [submitted]
+  push:
+    branches: [ develop, dev/*, release/* ]
+  pull_request:
+    branches: [ develop, dev/*, release/* ]
 
 jobs:
-  tests:
-    if: github.event.review.state == 'approved'
+  partial-tests:
+    uses: ./.github/workflows/partial-tests.yml
+
+  overall-tests:
+    needs: [partial-tests]
     timeout-minutes: 40
     strategy:
       fail-fast: false
@@ -50,10 +55,59 @@ jobs:
           pytest-xml-coverage-path: ./overall-coverage.xml
           title: Taipy Overall Coverage Report
 
-      - name: Notify user if failed
-        if: failure() && github.event_name == 'workflow_dispatch'
-        run: |
-          if [[ -n "${{ github.event.inputs.user-to-notify }}" ]]; then
-            curl "${{ secrets.notify_endpoint }}" -d '{"username": "${{ github.event.inputs.user-to-notify }}", "url": "${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" }' -H "Content-Type: application/json"
-          fi
-        shell: bash
+  submit_tests:
+    needs: [partial-tests]
+    timeout-minutes: 20
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: ['3.8', '3.9', '3.10', '3.11']
+        os: [ubuntu-latest, windows-latest, macos-latest]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v4
+      - uses: actions/setup-python@v5
+        with:
+          python-version: ${{matrix.python-version}}
+
+      - name: Install pipenv
+        run: curl https://raw.githubusercontent.com/pypa/pipenv/master/get-pipenv.py | python
+
+      - name: Install Dependencies
+        run: pipenv install --dev --python=${{ matrix.python-version }}
+
+      - name: Setup LibMagic (MacOS)
+        if: matrix.os == 'macos-latest'
+        run: brew install libmagic
+
+      - name: Pytest Core orchestrator_dispatcher
+        run: pipenv run pytest -m "orchestrator_dispatcher" tests/core
+
+  standalone_tests:
+    needs: [partial-tests]
+    timeout-minutes: 20
+    strategy:
+      fail-fast: false
+      matrix:
+        python-version: ['3.8', '3.9', '3.10', '3.11']
+        os: [ubuntu-latest, windows-latest, macos-latest]
+    runs-on: ${{ matrix.os }}
+    steps:
+      - uses: actions/checkout@v4
+
+      - uses: actions/setup-python@v5
+        with:
+          python-version: ${{matrix.python-version}}
+
+      - name: Install pipenv
+        run: curl https://raw.githubusercontent.com/pypa/pipenv/master/get-pipenv.py | python
+
+      - name: Install Dependencies
+        run: pipenv install --dev --python=${{ matrix.python-version }}
+
+      - name: Setup LibMagic (MacOS)
+        if: matrix.os == 'macos-latest'
+        run: brew install libmagic
+
+      - name: Pytest Core standalone
+        run: pipenv run pytest -m "standalone" tests/core

+ 3 - 13
.github/workflows/packaging.yml

@@ -36,19 +36,9 @@ jobs:
         run: |
           pip install .
           rm -rf taipy
-
-          python -c "import taipy as tp; tp.Scenario"
-          python -c "import taipy as tp; tp.gui"
-          python -c "import taipy as tp; tp.rest"
-
-          echo """
-          import taipy
-          from pathlib import Path
-          taipy_gui_core_path = Path(taipy.__file__).absolute().parent / 'gui_core' / 'lib' / 'taipy-gui-core.js'
-          if not taipy_gui_core_path.exists():
-              raise FileNotFoundError(f'taipy-gui-core.js not found in {taipy_gui_core_path}')
-          """ > ${{ runner.temp }}/verify_gui_core.py
-          python ${{ runner.temp }}/verify_gui_core.py
+      - name: Check Taipy Installation
+        run: |
+          python tools/validate_taipy_install.py
 
       - name: Notify user if failed
         if: failure()

+ 1 - 4
.github/workflows/partial-tests.yml

@@ -1,10 +1,7 @@
 name: Partial Tests Workflow
 
 on:
-  push:
-    branches: [ develop, dev/*, release/* ]
-  pull_request:
-    branches: [ develop, dev/*, release/* ]
+  workflow_call:
 
 jobs:
   linter:

+ 3 - 33
.github/workflows/publish.yml

@@ -21,34 +21,15 @@ jobs:
         id: vars
         run: echo "tag=${GITHUB_REF#refs/*/}" >> $GITHUB_OUTPUT
 
-      - name: Ensure package version is properly set
-        run: |
-          echo """
-          import json, sys, os
-          with open(f\"taipy{os.sep}version.json\") as version_file:
-            version_o = json.load(version_file)
-          version = f'{version_o.get(\"major\")}.{version_o.get(\"minor\")}.{version_o.get(\"patch\")}'
-          if vext := version_o.get(\"ext\"):
-            version = f'{version}.{vext}'
-          if version != sys.argv[1]:
-            raise ValueError(f\"Invalid version {version} / {sys.argv[1]}\")
-          if sys.argv[1] != sys.argv[2]:
-            raise ValueError(f\"Invalid tag version {sys.argv[2]} with package version {sys.argv[1]}\")
-          """ > ${{ runner.temp }}/check.py
-          python ${{ runner.temp }}/check.py "${{ github.event.inputs.version }}" "${{ steps.vars.outputs.tag }}"
-
       - name: Download assets from github release tag
         run: |
           gh release download ${{ github.event.inputs.version }} --dir dist
         env:
           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
 
-      - name: Verify there is a release asset
+      - name: Verify if all releases exist
         run: |
-          if [ ! -f dist/${{ github.event.repository.name }}-${{ github.event.inputs.version }}.tar.gz ]; then
-            echo "No release asset found"
-            exit 1
-          fi
+          python tools/release/check_releases.py
 
   publish-to-pypi:
     needs: [test-package]
@@ -98,15 +79,4 @@ jobs:
         run: |
           pip install --upgrade pip
           pip install --no-cache-dir ${{ github.event.repository.name }}==${{ github.event.inputs.version }}
-          python -c "import taipy as tp; tp.Scenario"
-          python -c "import taipy as tp; tp.gui"
-          python -c "import taipy as tp; tp.rest"
-
-          echo """
-          import taipy
-          from pathlib import Path
-          taipy_gui_core_path = Path(taipy.__file__).absolute().parent / 'gui_core' / 'lib' / 'taipy-gui-core.js'
-          if not taipy_gui_core_path.exists():
-              raise FileNotFoundError(f'taipy-gui-core.js not found in {taipy_gui_core_path}')
-          """ > ${{ runner.temp }}/verify_gui_core.py
-          python ${{ runner.temp }}/verify_gui_core.py
+          python tools/validate_taipy_install.py

+ 0 - 173
.github/workflows/release-dev.yml

@@ -1,173 +0,0 @@
-name: Create Github Dev Release
-
-on:
-  workflow_dispatch:
-    inputs:
-      taipy-gui-version:
-        description: "The taipy-gui version to use (ex: 2.3.0.dev0)"
-        required: true
-      taipy-rest-version:
-        description: "The taipy-rest version to use (ex: 2.3.0.dev0)"
-      taipy-templates-version:
-        description: "The taipy-templates version to use (ex: 2.3.0.dev0)"
-
-jobs:
-  release-dev-package:
-    timeout-minutes: 20
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v3
-        with:
-          ssh-key: ${{secrets.DEPLOY_KEY}}
-      - uses: actions/setup-python@v4
-        with:
-          python-version: 3.11
-      - uses: actions/setup-node@v4
-        with:
-          node-version: '20'
-
-      - name: Ensure package version has 'dev' suffix
-        run: |
-          echo """
-          import json, sys, os
-          SUFFIX = 'dev'
-          with open(f\"taipy{os.sep}version.json\") as version_file:
-              version_o = json.load(version_file)
-          version = f'{version_o.get(\"major\")}.{version_o.get(\"minor\")}.{version_o.get(\"patch\")}'
-          if vext := version_o.get(\"ext\"):
-              version = f'{version}.{vext}'
-          if SUFFIX not in version:
-              raise ValueError(f\"version {version} does not contain suffix {SUFFIX}\")
-          """ > ${{ runner.temp }}/check1.py
-          python ${{ runner.temp }}/check1.py
-
-      - name: Extract package version
-        id: current-version
-        run: |
-          echo """
-          import json, os
-          with open(f\"taipy{os.sep}version.json\") as version_file:
-              version_o = json.load(version_file)
-          version = f'{version_o.get(\"major\")}.{version_o.get(\"minor\")}.{version_o.get(\"patch\")}'
-          if vext := version_o.get(\"ext\"):
-              version = f'{version}.{vext}'
-          print(f'VERSION={version}')
-          """ > ${{ runner.temp }}/check2.py
-          python ${{ runner.temp }}/check2.py >> $GITHUB_OUTPUT
-
-      - name: Check taipy-gui dependencies
-        run: |
-          curl https://pypi.org/simple/taipy-gui/ | grep -o ">taipy-gui-${{ github.event.inputs.taipy-gui-version }}\.tar\.gz<"
-
-      - name: Install dependencies
-        run: |
-          python -m pip install --upgrade pip
-          pip install build wheel
-          pip install "taipy-gui==${{ github.event.inputs.taipy-gui-version }}"
-
-      - name: Check dependencies are available
-        if: github.event.inputs.taipy-rest-version != '' && github.event.inputs.taipy-templates-version != ''
-        run: |
-          curl https://pypi.org/simple/taipy-rest/ | grep -o ">taipy-rest-${{ github.event.inputs.taipy-rest-version }}\.tar\.gz<"
-          curl https://pypi.org/simple/taipy-templates/ | grep -o ">taipy-templates-${{ github.event.inputs.taipy-templates-version }}\.tar\.gz<"
-
-      - name: Update setup.py locally
-        if: github.event.inputs.taipy-gui-version != '' && github.event.inputs.taipy-rest-version != '' && github.event.inputs.taipy-templates-version != ''
-        run: |
-          mv setup.py setup.taipy.py
-          echo """
-          import sys
-          with open('setup.taipy.py', mode='r') as setup_r, open('setup.py', mode='w') as setup_w:
-              in_requirements = False
-              looking = True
-              for line in setup_r:
-                  if looking:
-                      if line.lstrip().startswith('requirements') and line.rstrip().endswith('['):
-                          in_requirements = True
-                      elif in_requirements:
-                          if line.strip() == ']':
-                              looking = False
-                          else:
-                              if line.lstrip().startswith('\"taipy-gui@git+https'):
-                                  start = line.find('\"taipy-gui')
-                                  end = line.rstrip().find(',')
-                                  line = f'{line[:start]}\"taipy-gui=={sys.argv[1]}\"{line[end:]}'
-                              elif line.lstrip().startswith('\"taipy-rest@git+https'):
-                                  start = line.find('\"taipy-rest')
-                                  end = line.rstrip().find(',')
-                                  line = f'{line[:start]}\"taipy-rest=={sys.argv[2]}\"{line[end:]}'
-                              elif line.lstrip().startswith('\"taipy-templates@git+https'):
-                                  start = line.find('\"taipy-templates')
-                                  end = line.rstrip().find(',')
-                                  line = f'{line[:start]}\"taipy-templates=={sys.argv[3]}\"{line[end:]}'
-                  setup_w.write(line)
-          """ > ${{ runner.temp }}/write_setup_taipy.py
-          python ${{ runner.temp }}/write_setup_taipy.py "${{ github.event.inputs.taipy-gui-version }}" "${{ github.event.inputs.taipy-rest-version }}" "${{ github.event.inputs.taipy-templates-version }}"
-
-      - name: Build package
-        run: python setup.py build_py && python -m build
-
-      - name: Install the package and test it
-        run: |
-          # Install package
-          echo "Installing package..."
-          pip install ./dist/${{ github.event.repository.name }}-${{ steps.current-version.outputs.VERSION }}.tar.gz
-
-          # Run tests
-          python -c "import taipy as tp; tp.Scenario"
-          python -c "import taipy as tp; tp.gui"
-          python -c "import taipy as tp; tp.rest"
-
-          echo """
-          import taipy
-          from pathlib import Path
-          taipy_gui_core_path = Path(taipy.__file__).absolute().parent / 'gui_core' / 'lib' / 'taipy-gui-core.js'
-          if not taipy_gui_core_path.exists():
-              raise FileNotFoundError(f'taipy-gui-core.js not found in {taipy_gui_core_path}')
-          """ > ${{ runner.temp }}/verify_gui_core.py
-          python ${{ runner.temp }}/verify_gui_core.py
-
-      - name: Extract commit hash
-        shell: bash
-        run: echo "HASH=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
-        id: extract_hash
-
-      - name: Create/update release and tag
-        run: |
-          echo "Creating release ${{ steps.current-version.outputs.VERSION }}"
-          gh release create ${{ steps.current-version.outputs.VERSION }} ./dist/${{ github.event.repository.name }}-${{ steps.current-version.outputs.VERSION }}.tar.gz --target ${{ steps.extract_hash.outputs.HASH }} --prerelease --title ${{ steps.current-version.outputs.VERSION }} --notes "Release Draft ${{ steps.current-version.outputs.VERSION }}"
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
-      - name: Reset changes
-        run: |
-          git reset --hard HEAD
-          git clean -fdx
-
-      - name: Increase dev version
-        id: new-version
-        run: |
-          echo """
-          import json, os
-          with open(f'taipy{os.sep}version.json') as version_file:
-              version_o = json.load(version_file)
-              if version_o is None or 'dev' not in version_o['ext']:
-                  raise ValueError('Invalid version file. Version must contain dev suffix.')
-              prev_version = version_o['ext']
-              new_version = 'dev' + str(int(version_o['ext'].replace('dev', '')) + 1)
-              with open(f'taipy{os.sep}version.json') as r:
-                  text = r.read().replace(prev_version, new_version)
-              with open(f'taipy{os.sep}version.json', mode='w') as w:
-                  w.write(text)
-              with open(f\"taipy{os.sep}version.json\") as version_file:
-                  version_o = json.load(version_file)
-              version = f'{version_o.get(\"major\")}.{version_o.get(\"minor\")}.{version_o.get(\"patch\")}'
-              if vext := version_o.get(\"ext\"):
-                  version = f'{version}.{vext}'
-              print(f'VERSION={version}')
-          """ > ${{ runner.temp }}/increase_dev_version.py
-          python ${{ runner.temp }}/increase_dev_version.py >> $GITHUB_OUTPUT
-
-      - uses: stefanzweifel/git-auto-commit-action@v4
-        with:
-          commit_message: Update version to ${{ steps.new-version.outputs.VERSION }}

+ 0 - 106
.github/workflows/release.yml

@@ -1,106 +0,0 @@
-name: Create Github Release
-
-on:
-  workflow_dispatch:
-    inputs:
-      version:
-        description: "The release/package version to create (ex: 1.0.0)"
-        required: true
-
-jobs:
-  release-package:
-    timeout-minutes: 20
-    runs-on: ubuntu-latest
-    steps:
-      - uses: actions/checkout@v3
-      - uses: actions/setup-python@v4
-        with:
-          python-version: 3.11
-      - uses: actions/setup-node@v4
-        with:
-          node-version: '20'
-
-      - name: Extract branch name
-        shell: bash
-        run: echo "branch=${GITHUB_REF#refs/heads/}" >> $GITHUB_OUTPUT
-        id: extract_branch
-
-      - name: Ensure package version is properly set
-        run: |
-          echo """
-          import json, sys, os
-          with open(f\"taipy{os.sep}version.json\") as version_file:
-              version_o = json.load(version_file)
-          version = f'{version_o.get(\"major\")}.{version_o.get(\"minor\")}.{version_o.get(\"patch\")}'
-          if vext := version_o.get(\"ext\"):
-              version = f'{version}.{vext}'
-          if version != sys.argv[1]:
-              raise ValueError(f\"Invalid version {version} / {sys.argv[1]}\")
-          """ > ${{ runner.temp }}/check1.py
-          python ${{ runner.temp }}/check1.py "${{ github.event.inputs.version }}"
-
-      - name: Validate branch name
-        run: |
-          echo """
-          import json, sys, os
-          with open(f\"taipy{os.sep}version.json\") as version_file:
-              version = json.load(version_file)
-          if f'release/{version.get(\"major\")}.{version.get(\"minor\")}' != sys.argv[1]:
-              raise ValueError(f'Branch name mismatch: release/{version.get(\"major\")}.{version.get(\"minor\")} != {sys.argv[1]}')
-          """ > ${{ runner.temp }}/check2.py
-          python ${{ runner.temp }}/check2.py "${{ steps.extract_branch.outputs.branch }}"
-
-      - name: Modify README image file path
-        run: |
-          cp tools/modify_readme.py ${{ runner.temp }}
-          python ${{ runner.temp }}/modify_readme.py "${{ github.event.repository.name }}" "${{ steps.extract_branch.outputs.branch }}"
-
-      - name: Get taipy-gui version from setup.py
-        id: taipy_gui_version
-        run: |
-          echo """
-          with open('setup.py') as f:
-              for line in f:
-                  if 'taipy-gui' in line:
-                      start = line.find('taipy-gui')
-                      end = line.rstrip().find('\",')
-                      print(f'VERSION={line[start:end]}')
-                      break
-          """ > ${{ runner.temp }}/get_gui_version.py
-          python ${{ runner.temp }}/get_gui_version.py >> $GITHUB_OUTPUT
-
-      - name: Install dependencies
-        run: |
-          python -m pip install --upgrade pip
-          pip install build wheel
-          # install taipy-gui from based on setup.py version
-          pip install "${{ steps.taipy_gui_version.outputs.VERSION }}"
-
-      - name: Build and test the package
-        run: |
-          python setup.py build_py && python -m build
-          rm -rf taipy
-          pip install dist/*.tar.gz
-          python -c "import taipy as tp; tp.Scenario"
-          python -c "import taipy as tp; tp.gui"
-          python -c "import taipy as tp; tp.rest"
-
-          echo """
-          import taipy
-          from pathlib import Path
-          taipy_gui_core_path = Path(taipy.__file__).absolute().parent / 'gui_core' / 'lib' / 'taipy-gui-core.js'
-          if not taipy_gui_core_path.exists():
-              raise FileNotFoundError(f'taipy-gui-core.js not found in {taipy_gui_core_path}')
-          """ > ${{ runner.temp }}/verify_gui_core.py
-          python ${{ runner.temp }}/verify_gui_core.py
-
-      - name: Extract commit hash
-        shell: bash
-        run: echo "HASH=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
-        id: extract_hash
-
-      - name: Create/update release and tag
-        run: |
-            gh release create ${{ github.event.inputs.version }} ./dist/${{ github.event.repository.name }}-${{ github.event.inputs.version }}.tar.gz --target ${{ steps.extract_hash.outputs.HASH }} --title ${{ github.event.inputs.version }}
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

+ 1 - 1
taipy/core/.github/workflows/trigger-benchmark.yml → .github/workflows/trigger-benchmark.yml

@@ -11,6 +11,6 @@ jobs:
         uses: peter-evans/repository-dispatch@v1
         with:
           token: ${{secrets.TAIPY_INTEGRATION_TESTING_ACCESS_TOKEN}}
-          repository: avaiga/taipy-integration-testing
+          repository: avaiga/taipy-benchmark
           event-type: benchmark
           client-payload: '{"repo": "taipy-core", "commitSHA": "${{ github.sha }}"}'

+ 0 - 0
taipy/core/.github/workflows/trigger-integration-tests.yml → .github/workflows/trigger-integration-tests.yml


+ 14 - 0
MANIFEST.in

@@ -0,0 +1,14 @@
+recursive-include tools *
+include taipy/core/*.json
+include taipy/core/config/*.json
+include taipy/*.json
+include taipy/gui_core/*.json
+include taipy/gui_core/lib/*.js
+recursive-include taipy/gui/webapp *
+include taipy/gui/version.json
+include taipy/gui/viselements.json
+include taipy/gui/*.pyi
+include taipy/config/*.pyi
+include taipy/config/*.json
+recursive-include taipy/templates *
+include taipy/rest/*.json

+ 202 - 155
frontend/taipy-gui/package-lock.json

@@ -406,9 +406,9 @@
       }
     },
     "node_modules/@babel/helpers": {
-      "version": "7.23.7",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.7.tgz",
-      "integrity": "sha512-6AMnjCoC8wjqBzDHkuqpa7jAKwvMo4dC+lr/TFBz+ucfulO1XMpDnwWPGBNwClOKZ8h6xn5N81W/R5OrcKtCbQ==",
+      "version": "7.23.8",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.23.8.tgz",
+      "integrity": "sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==",
       "dev": true,
       "dependencies": {
         "@babel/template": "^7.22.15",
@@ -686,9 +686,9 @@
       }
     },
     "node_modules/@babel/runtime": {
-      "version": "7.23.7",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.7.tgz",
-      "integrity": "sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==",
+      "version": "7.23.8",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.8.tgz",
+      "integrity": "sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==",
       "dependencies": {
         "regenerator-runtime": "^0.14.0"
       },
@@ -996,28 +996,28 @@
       }
     },
     "node_modules/@floating-ui/core": {
-      "version": "1.5.2",
-      "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.2.tgz",
-      "integrity": "sha512-Ii3MrfY/GAIN3OhXNzpCKaLxHQfJF9qvwq/kEJYdqDxeIHa01K8sldugal6TmeeXl+WMvhv9cnVzUTaFFJF09A==",
+      "version": "1.5.3",
+      "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.3.tgz",
+      "integrity": "sha512-O0WKDOo0yhJuugCx6trZQj5jVJ9yR0ystG2JaNAemYUWce+pmM6WUEFIibnWyEJKdrDxhm75NoSRME35FNaM/Q==",
       "dependencies": {
-        "@floating-ui/utils": "^0.1.3"
+        "@floating-ui/utils": "^0.2.0"
       }
     },
     "node_modules/@floating-ui/dom": {
-      "version": "1.5.3",
-      "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz",
-      "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==",
+      "version": "1.5.4",
+      "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.4.tgz",
+      "integrity": "sha512-jByEsHIY+eEdCjnTVu+E3ephzTOzkQ8hgUfGwos+bg7NlH33Zc5uO+QHz1mrQUOgIKKDD1RtS201P9NvAfq3XQ==",
       "dependencies": {
-        "@floating-ui/core": "^1.4.2",
-        "@floating-ui/utils": "^0.1.3"
+        "@floating-ui/core": "^1.5.3",
+        "@floating-ui/utils": "^0.2.0"
       }
     },
     "node_modules/@floating-ui/react-dom": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.4.tgz",
-      "integrity": "sha512-CF8k2rgKeh/49UrnIBs4BdxPUV6vize/Db1d/YbCLyp9GiVZ0BEwf5AiDSxJRCr6yOkGqTFHtmrULxkEfYZ7dQ==",
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.5.tgz",
+      "integrity": "sha512-UsBK30Bg+s6+nsgblXtZmwHhgS2vmbuQK22qgt2pTQM6M3X6H1+cQcLXqgRY3ihVLcZJE6IvqDQozhsnIVqK/Q==",
       "dependencies": {
-        "@floating-ui/dom": "^1.5.1"
+        "@floating-ui/dom": "^1.5.4"
       },
       "peerDependencies": {
         "react": ">=16.8.0",
@@ -1025,9 +1025,9 @@
       }
     },
     "node_modules/@floating-ui/utils": {
-      "version": "0.1.6",
-      "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz",
-      "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz",
+      "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q=="
     },
     "node_modules/@humanwhocodes/config-array": {
       "version": "0.11.13",
@@ -1662,14 +1662,14 @@
       }
     },
     "node_modules/@mui/base": {
-      "version": "5.0.0-beta.29",
-      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.29.tgz",
-      "integrity": "sha512-OXfUssYrB6ch/xpBVHMKAjThPlI9VyGGKdvQLMXef2j39wXfcxPlUVQlwia/lmE3rxWIGvbwkZsDtNYzLMsDUg==",
+      "version": "5.0.0-beta.30",
+      "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.30.tgz",
+      "integrity": "sha512-dc38W4W3K42atE9nSaOeoJ7/x9wGIfawdwC/UmMxMLlZ1iSsITQ8dQJaTATCbn98YvYPINK/EH541YA5enQIPQ==",
       "dependencies": {
         "@babel/runtime": "^7.23.6",
         "@floating-ui/react-dom": "^2.0.4",
-        "@mui/types": "^7.2.11",
-        "@mui/utils": "^5.15.2",
+        "@mui/types": "^7.2.12",
+        "@mui/utils": "^5.15.3",
         "@popperjs/core": "^2.11.8",
         "clsx": "^2.0.0",
         "prop-types": "^15.8.1"
@@ -1693,18 +1693,18 @@
       }
     },
     "node_modules/@mui/core-downloads-tracker": {
-      "version": "5.15.2",
-      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.2.tgz",
-      "integrity": "sha512-0vk4ckS2w1F5PmkSXSd7F/QuRlNcPqWTJ8CPl+HQRLTIhJVS/VKEI+3dQufOdKfn2wS+ecnvlvXerbugs+xZ8Q==",
+      "version": "5.15.3",
+      "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.3.tgz",
+      "integrity": "sha512-sWeihiVyxdJjpLkp8SHkTy9kt2M/o11M60G1MzwljGL2BXdM3Ktzqv5QaQHdi00y7Y1ulvtI3GOSxP2xU8mQJw==",
       "funding": {
         "type": "opencollective",
         "url": "https://opencollective.com/mui-org"
       }
     },
     "node_modules/@mui/icons-material": {
-      "version": "5.15.2",
-      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.2.tgz",
-      "integrity": "sha512-Vs0Z6cd6ieTavMjqPvIJJfwsKaCLdRSErk5LjKdZlBqk7r2SR6roDyhVTQuZOeCzjEFj0qZ4iVPp2DJZRwuYbw==",
+      "version": "5.15.3",
+      "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.3.tgz",
+      "integrity": "sha512-7LEs8AnO2Se/XYH+CcJndRsGAE+M8KAExiiQHf0V11poqmPVGcbbY82Ry2IUYf9+rOilCVnWI18ErghZ625BPQ==",
       "dependencies": {
         "@babel/runtime": "^7.23.6"
       },
@@ -1727,16 +1727,16 @@
       }
     },
     "node_modules/@mui/material": {
-      "version": "5.15.2",
-      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.2.tgz",
-      "integrity": "sha512-JnoIrpNmEHG5uC1IyEdgsnDiaiuCZnUIh7f9oeAr87AvBmNiEJPbo7XrD7kBTFWwp+b97rQ12QdSs9CLhT2n/A==",
+      "version": "5.15.3",
+      "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.3.tgz",
+      "integrity": "sha512-DODBBMouyq1B5f3YkEWL9vO8pGCxuEGqtfpltF6peMJzz/78tJFyLQsDas9MNLC/8AdFu2BQdkK7wox5UBPTAA==",
       "dependencies": {
         "@babel/runtime": "^7.23.6",
-        "@mui/base": "5.0.0-beta.29",
-        "@mui/core-downloads-tracker": "^5.15.2",
-        "@mui/system": "^5.15.2",
-        "@mui/types": "^7.2.11",
-        "@mui/utils": "^5.15.2",
+        "@mui/base": "5.0.0-beta.30",
+        "@mui/core-downloads-tracker": "^5.15.3",
+        "@mui/system": "^5.15.3",
+        "@mui/types": "^7.2.12",
+        "@mui/utils": "^5.15.3",
         "@types/react-transition-group": "^4.4.10",
         "clsx": "^2.0.0",
         "csstype": "^3.1.2",
@@ -1771,12 +1771,12 @@
       }
     },
     "node_modules/@mui/private-theming": {
-      "version": "5.15.2",
-      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.2.tgz",
-      "integrity": "sha512-KlXx5TH1Mw9omSY+Q6rz5TA/P71meSYaAOeopiW8s6o433+fnOxS17rZbmd1RnDZGCo+j24TfCavQuCMBAZnQA==",
+      "version": "5.15.3",
+      "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.3.tgz",
+      "integrity": "sha512-Q79MhVMmywC1l5bMsMZq5PsIudr1MNPJnx9/EqdMP0vpz5iNvFpnLmxsD7d8/hqTWgFAljI+LH3jX8MxlZH9Gw==",
       "dependencies": {
         "@babel/runtime": "^7.23.6",
-        "@mui/utils": "^5.15.2",
+        "@mui/utils": "^5.15.3",
         "prop-types": "^15.8.1"
       },
       "engines": {
@@ -1797,9 +1797,9 @@
       }
     },
     "node_modules/@mui/styled-engine": {
-      "version": "5.15.2",
-      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.2.tgz",
-      "integrity": "sha512-fYEN3IZzbebeHwAmQHhxwruiOIi8W74709qXg/7tgtHV4byQSmPgnnKsZkg0hFlzjEbcJIRZyZI0qEecgpR2cg==",
+      "version": "5.15.3",
+      "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.3.tgz",
+      "integrity": "sha512-+d5XZCTeemOO/vBfWGEeHgTm8fjU1Psdgm+xAw+uegycO2EnoA/EfGSaG5UwZ6g3b66y48Mkxi35AggShMr88w==",
       "dependencies": {
         "@babel/runtime": "^7.23.6",
         "@emotion/cache": "^11.11.0",
@@ -1828,15 +1828,15 @@
       }
     },
     "node_modules/@mui/system": {
-      "version": "5.15.2",
-      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.2.tgz",
-      "integrity": "sha512-I7CzLiHDtU/BTobJgSk+wPGGWG95K8lYfdFEnq//wOgSrLDAdOVvl2gleDxJWO+yAbGz4RKEOnR9KuD+xQZH4A==",
+      "version": "5.15.3",
+      "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.3.tgz",
+      "integrity": "sha512-ewVU4eRgo4VfNMGpO61cKlfWmH7l9s6rA8EknRzuMX3DbSLfmtW2WJJg6qPwragvpPIir0Pp/AdWVSDhyNy5Tw==",
       "dependencies": {
         "@babel/runtime": "^7.23.6",
-        "@mui/private-theming": "^5.15.2",
-        "@mui/styled-engine": "^5.15.2",
-        "@mui/types": "^7.2.11",
-        "@mui/utils": "^5.15.2",
+        "@mui/private-theming": "^5.15.3",
+        "@mui/styled-engine": "^5.15.3",
+        "@mui/types": "^7.2.12",
+        "@mui/utils": "^5.15.3",
         "clsx": "^2.0.0",
         "csstype": "^3.1.2",
         "prop-types": "^15.8.1"
@@ -1867,9 +1867,9 @@
       }
     },
     "node_modules/@mui/types": {
-      "version": "7.2.11",
-      "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.11.tgz",
-      "integrity": "sha512-KWe/QTEsFFlFSH+qRYf3zoFEj3z67s+qAuSnMMg+gFwbxG7P96Hm6g300inQL1Wy///gSRb8juX7Wafvp93m3w==",
+      "version": "7.2.12",
+      "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.12.tgz",
+      "integrity": "sha512-3kaHiNm9khCAo0pVe0RenketDSFoZGAlVZ4zDjB/QNZV0XiCj+sh1zkX0VVhQPgYJDlBEzAag+MHJ1tU3vf0Zw==",
       "peerDependencies": {
         "@types/react": "^17.0.0 || ^18.0.0"
       },
@@ -1880,9 +1880,9 @@
       }
     },
     "node_modules/@mui/utils": {
-      "version": "5.15.2",
-      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.2.tgz",
-      "integrity": "sha512-6dGM9/guFKBlFRHA7/mbM+E7wE7CYDy9Ny4JLtD3J+NTyhi8nd8YxlzgAgTaTVqY0BpdQ2zdfB/q6+p2EdGM0w==",
+      "version": "5.15.3",
+      "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.3.tgz",
+      "integrity": "sha512-mT3LiSt9tZWCdx1pl7q4Q5tNo6gdZbvJel286ZHGuj6LQQXjWNAh8qiF9d+LogvNUI+D7eLkTnj605d1zoazfg==",
       "dependencies": {
         "@babel/runtime": "^7.23.6",
         "@types/prop-types": "^15.7.11",
@@ -1907,9 +1907,9 @@
       }
     },
     "node_modules/@mui/x-date-pickers": {
-      "version": "6.18.6",
-      "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.18.6.tgz",
-      "integrity": "sha512-pqOrGPUDVY/1xXrM1hofqwgquno/SB9aG9CVS1m2Rs8hKF1VWRC+jYlEa1Qk08xKmvkia5g7NsdV/BBb+tHUZw==",
+      "version": "6.18.7",
+      "resolved": "https://registry.npmjs.org/@mui/x-date-pickers/-/x-date-pickers-6.18.7.tgz",
+      "integrity": "sha512-4NoapaCT3jvEk2cuAUjG0ReZvTEk1i4dGDz94Gt1Oc08GuC1AuzYRwCR1/1tdmbDynwkR8ilkKL6AyS3NL1H4A==",
       "dependencies": {
         "@babel/runtime": "^7.23.2",
         "@mui/base": "^5.0.0-beta.22",
@@ -2061,6 +2061,38 @@
         "elementary-circuits-directed-graph": "^1.0.4"
       }
     },
+    "node_modules/@plotly/mapbox-gl": {
+      "version": "1.13.4",
+      "resolved": "https://registry.npmjs.org/@plotly/mapbox-gl/-/mapbox-gl-1.13.4.tgz",
+      "integrity": "sha512-sR3/Pe5LqT/fhYgp4rT4aSFf1rTsxMbGiH6Hojc7PH36ny5Bn17iVFUjpzycafETURuFbLZUfjODO8LvSI+5zQ==",
+      "dependencies": {
+        "@mapbox/geojson-rewind": "^0.5.2",
+        "@mapbox/geojson-types": "^1.0.2",
+        "@mapbox/jsonlint-lines-primitives": "^2.0.2",
+        "@mapbox/mapbox-gl-supported": "^1.5.0",
+        "@mapbox/point-geometry": "^0.1.0",
+        "@mapbox/tiny-sdf": "^1.1.1",
+        "@mapbox/unitbezier": "^0.0.0",
+        "@mapbox/vector-tile": "^1.3.1",
+        "@mapbox/whoots-js": "^3.1.0",
+        "csscolorparser": "~1.0.3",
+        "earcut": "^2.2.2",
+        "geojson-vt": "^3.2.1",
+        "gl-matrix": "^3.2.1",
+        "grid-index": "^1.1.0",
+        "murmurhash-js": "^1.0.0",
+        "pbf": "^3.2.1",
+        "potpack": "^1.0.1",
+        "quickselect": "^2.0.0",
+        "rw": "^1.3.3",
+        "supercluster": "^7.1.0",
+        "tinyqueue": "^2.0.3",
+        "vt-pbf": "^3.1.1"
+      },
+      "engines": {
+        "node": ">=6.4.0"
+      }
+    },
     "node_modules/@plotly/point-cluster": {
       "version": "3.1.9",
       "resolved": "https://registry.npmjs.org/@plotly/point-cluster/-/point-cluster-3.1.9.tgz",
@@ -2125,9 +2157,9 @@
       "integrity": "sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg=="
     },
     "node_modules/@testing-library/dom": {
-      "version": "9.3.3",
-      "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.3.tgz",
-      "integrity": "sha512-fB0R+fa3AUqbLHWyxXa2kGVtf1Fe1ZZFr0Zp6AIbIAzXb2mKbEXl+PCQNUOaq5lbTab5tfctfXRNsWXxa2f7Aw==",
+      "version": "9.3.4",
+      "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.3.4.tgz",
+      "integrity": "sha512-FlS4ZWlp97iiNWig0Muq8p+3rVDjRiYE+YKGbAqXOu9nwJFFOdL00kFpz42M+4huzYi86vAK1sOOfyOG45muIQ==",
       "dev": true,
       "dependencies": {
         "@babel/code-frame": "^7.10.4",
@@ -2144,9 +2176,9 @@
       }
     },
     "node_modules/@testing-library/jest-dom": {
-      "version": "6.1.6",
-      "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.1.6.tgz",
-      "integrity": "sha512-YwuiOdYEcxhfC2u5iNKlvg2Q5MgbutovP6drq7J1HrCbvR+G58BbtoCoq+L/kNlrNFsu2Kt3jaFAviLVxYHJZg==",
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.2.0.tgz",
+      "integrity": "sha512-+BVQlJ9cmEn5RDMUS8c2+TU6giLvzaHZ8sU/x0Jj7fk+6/46wPdwlgOPcpxS17CjcanBi/3VmGMqVr2rmbUmNw==",
       "dev": true,
       "dependencies": {
         "@adobe/css-tools": "^4.3.2",
@@ -2154,7 +2186,7 @@
         "aria-query": "^5.0.0",
         "chalk": "^3.0.0",
         "css.escape": "^1.5.1",
-        "dom-accessibility-api": "^0.5.6",
+        "dom-accessibility-api": "^0.6.3",
         "lodash": "^4.17.15",
         "redent": "^3.0.0"
       },
@@ -2197,6 +2229,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": {
+      "version": "0.6.3",
+      "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz",
+      "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==",
+      "dev": true
+    },
     "node_modules/@testing-library/react": {
       "version": "14.1.2",
       "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-14.1.2.tgz",
@@ -2558,9 +2596,9 @@
       "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng=="
     },
     "node_modules/@types/react": {
-      "version": "18.2.46",
-      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.46.tgz",
-      "integrity": "sha512-nNCvVBcZlvX4NU1nRRNV/mFl1nNRuTuslAJglQsq+8ldXe5Xv0Wd2f7WTE3jOxhLH2BFfiZGC6GCp+kHQbgG+w==",
+      "version": "18.2.47",
+      "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.47.tgz",
+      "integrity": "sha512-xquNkkOirwyCgoClNk85BjP+aqnIS+ckAJ8i37gAbDs14jfW/J23f2GItAf33oiUPQnqNMALiFeoM9Y5mbjpVQ==",
       "dependencies": {
         "@types/prop-types": "*",
         "@types/scheduler": "*",
@@ -2698,16 +2736,16 @@
       "dev": true
     },
     "node_modules/@typescript-eslint/eslint-plugin": {
-      "version": "6.17.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.17.0.tgz",
-      "integrity": "sha512-Vih/4xLXmY7V490dGwBQJTpIZxH4ZFH6eCVmQ4RFkB+wmaCTDAx4dtgoWwMNGKLkqRY1L6rPqzEbjorRnDo4rQ==",
+      "version": "6.18.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.18.1.tgz",
+      "integrity": "sha512-nISDRYnnIpk7VCFrGcu1rnZfM1Dh9LRHnfgdkjcbi/l7g16VYRri3TjXi9Ir4lOZSw5N/gnV/3H7jIPQ8Q4daA==",
       "dev": true,
       "dependencies": {
         "@eslint-community/regexpp": "^4.5.1",
-        "@typescript-eslint/scope-manager": "6.17.0",
-        "@typescript-eslint/type-utils": "6.17.0",
-        "@typescript-eslint/utils": "6.17.0",
-        "@typescript-eslint/visitor-keys": "6.17.0",
+        "@typescript-eslint/scope-manager": "6.18.1",
+        "@typescript-eslint/type-utils": "6.18.1",
+        "@typescript-eslint/utils": "6.18.1",
+        "@typescript-eslint/visitor-keys": "6.18.1",
         "debug": "^4.3.4",
         "graphemer": "^1.4.0",
         "ignore": "^5.2.4",
@@ -2733,15 +2771,15 @@
       }
     },
     "node_modules/@typescript-eslint/parser": {
-      "version": "6.17.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.17.0.tgz",
-      "integrity": "sha512-C4bBaX2orvhK+LlwrY8oWGmSl4WolCfYm513gEccdWZj0CwGadbIADb0FtVEcI+WzUyjyoBj2JRP8g25E6IB8A==",
+      "version": "6.18.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.18.1.tgz",
+      "integrity": "sha512-zct/MdJnVaRRNy9e84XnVtRv9Vf91/qqe+hZJtKanjojud4wAVy/7lXxJmMyX6X6J+xc6c//YEWvpeif8cAhWA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/scope-manager": "6.17.0",
-        "@typescript-eslint/types": "6.17.0",
-        "@typescript-eslint/typescript-estree": "6.17.0",
-        "@typescript-eslint/visitor-keys": "6.17.0",
+        "@typescript-eslint/scope-manager": "6.18.1",
+        "@typescript-eslint/types": "6.18.1",
+        "@typescript-eslint/typescript-estree": "6.18.1",
+        "@typescript-eslint/visitor-keys": "6.18.1",
         "debug": "^4.3.4"
       },
       "engines": {
@@ -2761,13 +2799,13 @@
       }
     },
     "node_modules/@typescript-eslint/scope-manager": {
-      "version": "6.17.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.17.0.tgz",
-      "integrity": "sha512-RX7a8lwgOi7am0k17NUO0+ZmMOX4PpjLtLRgLmT1d3lBYdWH4ssBUbwdmc5pdRX8rXon8v9x8vaoOSpkHfcXGA==",
+      "version": "6.18.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.18.1.tgz",
+      "integrity": "sha512-BgdBwXPFmZzaZUuw6wKiHKIovms97a7eTImjkXCZE04TGHysG+0hDQPmygyvgtkoB/aOQwSM/nWv3LzrOIQOBw==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.17.0",
-        "@typescript-eslint/visitor-keys": "6.17.0"
+        "@typescript-eslint/types": "6.18.1",
+        "@typescript-eslint/visitor-keys": "6.18.1"
       },
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
@@ -2778,13 +2816,13 @@
       }
     },
     "node_modules/@typescript-eslint/type-utils": {
-      "version": "6.17.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.17.0.tgz",
-      "integrity": "sha512-hDXcWmnbtn4P2B37ka3nil3yi3VCQO2QEB9gBiHJmQp5wmyQWqnjA85+ZcE8c4FqnaB6lBwMrPkgd4aBYz3iNg==",
+      "version": "6.18.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.18.1.tgz",
+      "integrity": "sha512-wyOSKhuzHeU/5pcRDP2G2Ndci+4g653V43gXTpt4nbyoIOAASkGDA9JIAgbQCdCkcr1MvpSYWzxTz0olCn8+/Q==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/typescript-estree": "6.17.0",
-        "@typescript-eslint/utils": "6.17.0",
+        "@typescript-eslint/typescript-estree": "6.18.1",
+        "@typescript-eslint/utils": "6.18.1",
         "debug": "^4.3.4",
         "ts-api-utils": "^1.0.1"
       },
@@ -2805,9 +2843,9 @@
       }
     },
     "node_modules/@typescript-eslint/types": {
-      "version": "6.17.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.17.0.tgz",
-      "integrity": "sha512-qRKs9tvc3a4RBcL/9PXtKSehI/q8wuU9xYJxe97WFxnzH8NWWtcW3ffNS+EWg8uPvIerhjsEZ+rHtDqOCiH57A==",
+      "version": "6.18.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.18.1.tgz",
+      "integrity": "sha512-4TuMAe+tc5oA7wwfqMtB0Y5OrREPF1GeJBAjqwgZh1lEMH5PJQgWgHGfYufVB51LtjD+peZylmeyxUXPfENLCw==",
       "dev": true,
       "engines": {
         "node": "^16.0.0 || >=18.0.0"
@@ -2818,13 +2856,13 @@
       }
     },
     "node_modules/@typescript-eslint/typescript-estree": {
-      "version": "6.17.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.17.0.tgz",
-      "integrity": "sha512-gVQe+SLdNPfjlJn5VNGhlOhrXz4cajwFd5kAgWtZ9dCZf4XJf8xmgCTLIqec7aha3JwgLI2CK6GY1043FRxZwg==",
+      "version": "6.18.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.18.1.tgz",
+      "integrity": "sha512-fv9B94UAhywPRhUeeV/v+3SBDvcPiLxRZJw/xZeeGgRLQZ6rLMG+8krrJUyIf6s1ecWTzlsbp0rlw7n9sjufHA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.17.0",
-        "@typescript-eslint/visitor-keys": "6.17.0",
+        "@typescript-eslint/types": "6.18.1",
+        "@typescript-eslint/visitor-keys": "6.18.1",
         "debug": "^4.3.4",
         "globby": "^11.1.0",
         "is-glob": "^4.0.3",
@@ -2846,17 +2884,17 @@
       }
     },
     "node_modules/@typescript-eslint/utils": {
-      "version": "6.17.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.17.0.tgz",
-      "integrity": "sha512-LofsSPjN/ITNkzV47hxas2JCsNCEnGhVvocfyOcLzT9c/tSZE7SfhS/iWtzP1lKNOEfLhRTZz6xqI8N2RzweSQ==",
+      "version": "6.18.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.18.1.tgz",
+      "integrity": "sha512-zZmTuVZvD1wpoceHvoQpOiewmWu3uP9FuTWo8vqpy2ffsmfCE8mklRPi+vmnIYAIk9t/4kOThri2QCDgor+OpQ==",
       "dev": true,
       "dependencies": {
         "@eslint-community/eslint-utils": "^4.4.0",
         "@types/json-schema": "^7.0.12",
         "@types/semver": "^7.5.0",
-        "@typescript-eslint/scope-manager": "6.17.0",
-        "@typescript-eslint/types": "6.17.0",
-        "@typescript-eslint/typescript-estree": "6.17.0",
+        "@typescript-eslint/scope-manager": "6.18.1",
+        "@typescript-eslint/types": "6.18.1",
+        "@typescript-eslint/typescript-estree": "6.18.1",
         "semver": "^7.5.4"
       },
       "engines": {
@@ -2871,12 +2909,12 @@
       }
     },
     "node_modules/@typescript-eslint/visitor-keys": {
-      "version": "6.17.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.17.0.tgz",
-      "integrity": "sha512-H6VwB/k3IuIeQOyYczyyKN8wH6ed8EwliaYHLxOIhyF0dYEIsN8+Bk3GE19qafeMKyZJJHP8+O1HiFhFLUNKSg==",
+      "version": "6.18.1",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.18.1.tgz",
+      "integrity": "sha512-/kvt0C5lRqGoCfsbmm7/CwMqoSkY3zzHLIjdhHZQW3VFrnz7ATecOHR7nb7V+xn4286MBxfnQfQhAmCI0u+bJA==",
       "dev": true,
       "dependencies": {
-        "@typescript-eslint/types": "6.17.0",
+        "@typescript-eslint/types": "6.18.1",
         "eslint-visitor-keys": "^3.4.1"
       },
       "engines": {
@@ -3572,11 +3610,11 @@
       }
     },
     "node_modules/axios": {
-      "version": "1.6.3",
-      "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.3.tgz",
-      "integrity": "sha512-fWyNdeawGam70jXSVlKl+SUNVcL6j6W79CuSIPfi6HnDUmSCH6gyUys/HrqHeA/wU0Az41rRgean494d0Jb+ww==",
+      "version": "1.6.5",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz",
+      "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==",
       "dependencies": {
-        "follow-redirects": "^1.15.0",
+        "follow-redirects": "^1.15.4",
         "form-data": "^4.0.0",
         "proxy-from-env": "^1.1.0"
       }
@@ -3726,6 +3764,14 @@
       "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
       "dev": true
     },
+    "node_modules/base64-arraybuffer": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz",
+      "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
     "node_modules/binary-search-bounds": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/binary-search-bounds/-/binary-search-bounds-2.0.5.tgz",
@@ -3876,9 +3922,9 @@
       }
     },
     "node_modules/caniuse-lite": {
-      "version": "1.0.30001572",
-      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001572.tgz",
-      "integrity": "sha512-1Pbh5FLmn5y4+QhNyJE9j3/7dK44dGB83/ZMjv/qJk86TvDbjk0LosiZo0i0WB0Vx607qMX9jYrn1VLHCkN4rw==",
+      "version": "1.0.30001576",
+      "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001576.tgz",
+      "integrity": "sha512-ff5BdakGe2P3SQsMsiqmt1Lc8221NR1VzHj5jXN5vBny9A6fpze94HiVV/n7XRosOlsShJcvMv5mdnpjOGCEgg==",
       "funding": [
         {
           "type": "opencollective",
@@ -4582,19 +4628,19 @@
       "integrity": "sha512-X1xgQhkZ9n94WDwntqst5D/FKkmiU0GlJSFZSV3kLvyJ1WC5VeyoXDOuleUD+SIuH9C7W05is++0Woh0CGfKjQ=="
     },
     "node_modules/css-loader": {
-      "version": "6.8.1",
-      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.8.1.tgz",
-      "integrity": "sha512-xDAXtEVGlD0gJ07iclwWVkLoZOpEvAWaSyf6W18S2pOC//K8+qUDIx8IIT3D+HjnmkJPQeesOPv5aiUaJsCM2g==",
+      "version": "6.9.0",
+      "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.9.0.tgz",
+      "integrity": "sha512-3I5Nu4ytWlHvOP6zItjiHlefBNtrH+oehq8tnQa2kO305qpVyx9XNIT1CXIj5bgCJs7qICBCkgCYxQLKPANoLA==",
       "dev": true,
       "dependencies": {
         "icss-utils": "^5.1.0",
-        "postcss": "^8.4.21",
+        "postcss": "^8.4.31",
         "postcss-modules-extract-imports": "^3.0.0",
         "postcss-modules-local-by-default": "^4.0.3",
-        "postcss-modules-scope": "^3.0.0",
+        "postcss-modules-scope": "^3.1.0",
         "postcss-modules-values": "^4.0.0",
         "postcss-value-parser": "^4.2.0",
-        "semver": "^7.3.8"
+        "semver": "^7.5.4"
       },
       "engines": {
         "node": ">= 12.13.0"
@@ -5282,9 +5328,9 @@
       "integrity": "sha512-/pjZsA1b4RPHbeWZQn66SWS8nZZWLQQ23oE3Eam7aroEFGEvwKAsJfZ9ytiEMycfzXWpca4FA9QIOehf7PocBQ=="
     },
     "node_modules/electron-to-chromium": {
-      "version": "1.4.617",
-      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.617.tgz",
-      "integrity": "sha512-sYNE3QxcDS4ANW1k4S/wWYMXjCVcFSOX3Bg8jpuMFaXt/x8JCmp0R1Xe1ZXDX4WXnSRBf+GJ/3eGWicUuQq5cg=="
+      "version": "1.4.625",
+      "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.625.tgz",
+      "integrity": "sha512-DENMhh3MFgaPDoXWrVIqSPInQoLImywfCwrSmVl3cf9QHzoZSiutHwGaB/Ql3VkqcQV30rzgdM+BjKqBAJxo5Q=="
     },
     "node_modules/element-size": {
       "version": "1.1.1",
@@ -9457,11 +9503,12 @@
       }
     },
     "node_modules/mapbox-gl": {
-      "version": "1.10.1",
-      "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.10.1.tgz",
-      "integrity": "sha512-0aHt+lFUpYfvh0kMIqXqNXqoYMuhuAsMlw87TbhWrw78Tx2zfuPI0Lx31/YPUgJ+Ire0tzQ4JnuBL7acDNXmMg==",
+      "version": "1.13.3",
+      "resolved": "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-1.13.3.tgz",
+      "integrity": "sha512-p8lJFEiqmEQlyv+DQxFAOG/XPWN0Wp7j/Psq93Zywz7qt9CcUKFYDBOoOEKzqe6gudHVJY8/Bhqw6VDpX2lSBg==",
+      "peer": true,
       "dependencies": {
-        "@mapbox/geojson-rewind": "^0.5.0",
+        "@mapbox/geojson-rewind": "^0.5.2",
         "@mapbox/geojson-types": "^1.0.2",
         "@mapbox/jsonlint-lines-primitives": "^2.0.2",
         "@mapbox/mapbox-gl-supported": "^1.5.0",
@@ -9475,13 +9522,12 @@
         "geojson-vt": "^3.2.1",
         "gl-matrix": "^3.2.1",
         "grid-index": "^1.1.0",
-        "minimist": "^1.2.5",
         "murmurhash-js": "^1.0.0",
         "pbf": "^3.2.1",
         "potpack": "^1.0.1",
         "quickselect": "^2.0.0",
         "rw": "^1.3.3",
-        "supercluster": "^7.0.0",
+        "supercluster": "^7.1.0",
         "tinyqueue": "^2.0.3",
         "vt-pbf": "^3.1.1"
       },
@@ -10893,16 +10939,18 @@
       }
     },
     "node_modules/plotly.js": {
-      "version": "2.27.1",
-      "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-2.27.1.tgz",
-      "integrity": "sha512-XeE0zTJWTxURYrUJqzf73l8lTb+HnyRvvhHkoSIEvWf58ins4saopo9l25kCm+xHAGz8E/2EOncE4DyXsJ34kA==",
+      "version": "2.28.0",
+      "resolved": "https://registry.npmjs.org/plotly.js/-/plotly.js-2.28.0.tgz",
+      "integrity": "sha512-fEvAapXhFTFO/PM5LnUvoG+tgOxRjjW7C7nHwVaDLKfq0F+SF/p3zRgo7vKwk9588WfgEHvuk6yRnp1mxZ+YSw==",
       "dependencies": {
         "@plotly/d3": "3.8.1",
         "@plotly/d3-sankey": "0.7.2",
         "@plotly/d3-sankey-circular": "0.33.1",
+        "@plotly/mapbox-gl": "v1.13.4",
         "@turf/area": "^6.4.0",
         "@turf/bbox": "^6.4.0",
         "@turf/centroid": "^6.0.2",
+        "base64-arraybuffer": "^1.0.2",
         "canvas-fit": "^1.5.0",
         "color-alpha": "1.0.4",
         "color-normalize": "1.5.0",
@@ -10924,7 +10972,6 @@
         "has-hover": "^1.0.1",
         "has-passive-events": "^1.0.0",
         "is-mobile": "^4.0.0",
-        "mapbox-gl": "1.10.1",
         "mouse-change": "^1.4.0",
         "mouse-event-offset": "^3.0.2",
         "mouse-wheel": "^1.2.0",
@@ -10936,7 +10983,7 @@
         "regl": "npm:@plotly/regl@^2.1.2",
         "regl-error2d": "^2.0.12",
         "regl-line2d": "^3.1.2",
-        "regl-scatter2d": "^3.2.9",
+        "regl-scatter2d": "^3.3.1",
         "regl-splom": "^1.0.14",
         "strongly-connected-components": "^1.0.1",
         "superscript-text": "^1.0.0",
@@ -10959,9 +11006,9 @@
       "integrity": "sha512-mKjR5nolISvF+q2BtC1fi/llpxBPTQ3wLWN8+ldzdw2Hocpc8C72ZqnamCM4Z6z+68GVVjkeM01WJegQmZ8MEQ=="
     },
     "node_modules/postcss": {
-      "version": "8.4.32",
-      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.32.tgz",
-      "integrity": "sha512-D/kj5JNu6oo2EIy+XL/26JEDTlIbB8hw85G8StOE6L74RQAVVP5rej6wxCNqyMbR4RkPfqvezVbPw81Ngd6Kcw==",
+      "version": "8.4.33",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz",
+      "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==",
       "dev": true,
       "funding": [
         {
@@ -12059,9 +12106,9 @@
       "dev": true
     },
     "node_modules/serialize-javascript": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.1.tgz",
-      "integrity": "sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==",
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+      "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
       "dev": true,
       "dependencies": {
         "randombytes": "^2.1.0"
@@ -12220,9 +12267,9 @@
       }
     },
     "node_modules/socket.io-client": {
-      "version": "4.7.2",
-      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.2.tgz",
-      "integrity": "sha512-vtA0uD4ibrYD793SOIAwlo8cj6haOeMHrGvwPxJsxH7CeIksqJ+3Zc06RvWTIFgiSqx4A3sOnTXpfAEE2Zyz6w==",
+      "version": "4.7.3",
+      "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.3.tgz",
+      "integrity": "sha512-nU+ywttCyBitXIl9Xe0RSEfek4LneYkJxCeNnKCuhwoH4jGXO1ipIUw/VA/+Vvv2G1MTym11fzFC0SxkrcfXDw==",
       "dependencies": {
         "@socket.io/component-emitter": "~3.1.0",
         "debug": "~4.3.2",
@@ -13218,9 +13265,9 @@
       }
     },
     "node_modules/typedoc": {
-      "version": "0.25.6",
-      "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.6.tgz",
-      "integrity": "sha512-1rdionQMpOkpA58qfym1J+YD+ukyA1IEIa4VZahQI2ZORez7dhOvEyUotQL/8rSoMBopdzOS+vAIsORpQO4cTA==",
+      "version": "0.25.7",
+      "resolved": "https://registry.npmjs.org/typedoc/-/typedoc-0.25.7.tgz",
+      "integrity": "sha512-m6A6JjQRg39p2ZVRIN3NKXgrN8vzlHhOS+r9ymUYtcUP/TIQPvWSq7YgE5ZjASfv5Vd5BW5xrir6Gm2XNNcOow==",
       "dev": true,
       "dependencies": {
         "lunr": "^2.3.9",

+ 7 - 5
frontend/taipy-gui/src/components/Taipy/Chart.spec.tsx

@@ -126,7 +126,7 @@ describe("Chart Component", () => {
         const selProps = { selected0: JSON.stringify([2, 4, 6]) };
         render(
             <TaipyContext.Provider value={{ state, dispatch }}>
-                <Chart id="chart" data={undefined} defaultConfig={chartConfig} updateVars="varname=varname" {...selProps} />
+                <Chart id="chart" data={undefined} updateVarName="data_var" defaultConfig={chartConfig} updateVars="varname=varname" {...selProps} />
             </TaipyContext.Provider>
         );
         expect(dispatch).toHaveBeenCalledWith({
@@ -135,7 +135,7 @@ describe("Chart Component", () => {
             type: "REQUEST_UPDATE",
         });
         expect(dispatch).toHaveBeenCalledWith({
-            name: "",
+            name: "data_var",
             payload: {
                 alldata: true,
                 pagekey: "Day-Daily hospital occupancy",
@@ -151,7 +151,7 @@ describe("Chart Component", () => {
         const state: TaipyState = INITIAL_STATE;
         const { getByTestId } = render(
             <TaipyContext.Provider value={{ state, dispatch }}>
-                <Chart data={undefined} defaultConfig={chartConfig} testId="test" />
+                <Chart data={undefined} updateVarName="data_var" defaultConfig={chartConfig} testId="test" />
             </TaipyContext.Provider>
         );
         const elt = getByTestId("test");
@@ -159,7 +159,7 @@ describe("Chart Component", () => {
         const modebar = elt.querySelector(".modebar");
         modebar && await userEvent.click(modebar);
         expect(dispatch).toHaveBeenCalledWith({
-            name: "",
+            name: "data_var",
             payload: {
                 alldata: true,
                 columns: ["Day","Daily hospital occupancy"],
@@ -176,6 +176,7 @@ describe("Chart Component", () => {
             <TaipyContext.Provider value={{ state, dispatch }}>
                 <Chart
                     id="table"
+                    updateVarName="data_var"
                     data={state.data.table as undefined}
                     defaultConfig={chartConfig}
                     updateVars="varname=varname"
@@ -187,6 +188,7 @@ describe("Chart Component", () => {
             <TaipyContext.Provider value={{ state: newState, dispatch }}>
                 <Chart
                     id="table"
+                    updateVarName="data_var"
                     data={newState.data.table as Record<string, TraceValueType>}
                     defaultConfig={chartConfig}
                     updateVars="varname=varname"
@@ -196,7 +198,7 @@ describe("Chart Component", () => {
         const elt = getByLabelText("Go to next page");
         await userEvent.click(elt);
         expect(dispatch).toHaveBeenCalledWith({
-            name: "",
+            name: "data_var",
             payload: {
                 columns: ["Entity"],
                 end: 200,

+ 39 - 14
frontend/taipy-gui/src/components/Taipy/Chart.tsx

@@ -54,6 +54,7 @@ interface ChartProp extends TaipyActiveProps, TaipyChangeProps {
     template_Dark_?: string;
     template_Light_?: string;
     //[key: `selected_${number}`]: number[];
+    figure?: Array<Record<string, unknown>>;
 }
 
 interface ChartConfig {
@@ -261,7 +262,7 @@ const Chart = (props: ChartProp) => {
     const config = useDynamicJsonProperty(props.config, props.defaultConfig, defaultConfig);
 
     useEffect(() => {
-        if (refresh || !data[dataKey]) {
+        if (updateVarName && (refresh || !data[dataKey])) {
             const backCols = Object.values(config.columns).map((col) => col.dfid);
             const dtKey = backCols.join("-") + (config.decimators ? `--${config.decimators.join("")}` : "");
             setDataKey(dtKey);
@@ -306,6 +307,14 @@ const Chart = (props: ChartProp) => {
         if (template) {
             baseLayout.template = template;
         }
+        if (props.figure) {
+            return {
+                ...(props.figure[0].layout as Partial<Layout>),
+                ...baseLayout,
+                title: title || baseLayout.title || (props.figure[0].layout as Partial<Layout>).title,
+                clickmode: "event+select",
+            } as Layout;
+        }
         return {
             ...baseLayout,
             title: title || baseLayout.title,
@@ -334,6 +343,7 @@ const Chart = (props: ChartProp) => {
         props.template,
         props.template_Dark_,
         props.template_Light_,
+        props.figure,
     ]);
 
     const style = useMemo(
@@ -506,11 +516,13 @@ const Chart = (props: ChartProp) => {
     const getRealIndex = useCallback(
         (index?: number) =>
             typeof index === "number"
-                ? data[dataKey].tp_index
+                ? props.figure
+                    ? index
+                    : data[dataKey].tp_index
                     ? (data[dataKey].tp_index[index] as number)
                     : index
                 : 0,
-        [data, dataKey]
+        [data, dataKey, props.figure]
     );
 
     const onSelect = useCallback(
@@ -543,17 +555,30 @@ const Chart = (props: ChartProp) => {
         <Box id={id} key="div" data-testid={props.testId} className={className} ref={plotRef}>
             <Tooltip title={hover || ""}>
                 <Suspense fallback={<Skeleton key="skeleton" sx={skelStyle} />}>
-                    <Plot
-                        data={dataPl}
-                        layout={layout}
-                        style={style}
-                        onRelayout={onRelayout}
-                        onAfterPlot={onAfterPlot}
-                        onSelected={isOnClick(config.types) ? undefined : onSelect}
-                        onDeselect={isOnClick(config.types) ? undefined : onSelect}
-                        onClick={isOnClick(config.types) ? onSelect : undefined}
-                        config={plotConfig}
-                    />
+                    {Array.isArray(props.figure) && props.figure.length && props.figure[0].data !== undefined ? (
+                        <Plot
+                            data={props.figure[0].data as Data[]}
+                            layout={layout}
+                            style={style}
+                            onRelayout={onRelayout}
+                            onAfterPlot={onAfterPlot}
+                            onSelected={onSelect}
+                            onDeselect={onSelect}
+                            config={plotConfig}
+                        />
+                    ) : (
+                        <Plot
+                            data={dataPl}
+                            layout={layout}
+                            style={style}
+                            onRelayout={onRelayout}
+                            onAfterPlot={onAfterPlot}
+                            onSelected={isOnClick(config.types) ? undefined : onSelect}
+                            onDeselect={isOnClick(config.types) ? undefined : onSelect}
+                            onClick={isOnClick(config.types) ? onSelect : undefined}
+                            config={plotConfig}
+                        />
+                    )}
                 </Suspense>
             </Tooltip>
         </Box>

+ 3 - 3
setup.py

@@ -11,7 +11,7 @@
 
 """The setup script."""
 
-
+import os
 import json
 import platform
 import subprocess
@@ -25,14 +25,14 @@ root_folder = Path(__file__).parent
 readme = (root_folder / "README.md").read_text("UTF-8")
 
 # get current version
-with open(root_folder / "taipy" / "version.json") as version_file:
+with open(os.path.join("taipy", "version.json")) as version_file:
     version = json.load(version_file)
     version_string = f'{version.get("major", 0)}.{version.get("minor", 0)}.{version.get("patch", 0)}'
     if vext := version.get("ext"):
         version_string = f"{version_string}.{vext}"
 
 # build MANIFEST.in from tools/packages/taipy*/MANIFEST.in
-with open(root_folder / "MANIFEST.in", "w") as man:
+with open(os.path.join(root_folder, "MANIFEST.in"), "a") as man:
     for pman in [
         dir / "MANIFEST.in"
         for dir in (root_folder / "tools" / "packages").iterdir()

+ 2 - 2
taipy/config/MANIFEST.in

@@ -1,2 +1,2 @@
-include taipy/config/*.pyi
-include taipy/config/*.json
+include *.pyi
+include *.json

+ 5 - 4
taipy/config/setup.py

@@ -20,7 +20,9 @@ from setuptools import find_namespace_packages, find_packages, setup
 with open("README.md") as readme_file:
     readme = readme_file.read()
 
-with open(f"src{os.sep}taipy{os.sep}config{os.sep}version.json") as version_file:
+version_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "version.json")
+
+with open(version_path) as version_file:
     version = json.load(version_file)
     version_string = f'{version.get("major", 0)}.{version.get("minor", 0)}.{version.get("patch", 0)}'
     if vext := version.get("ext"):
@@ -47,13 +49,12 @@ setup(
     install_requires=requirements,
     long_description=readme,
     long_description_content_type="text/markdown",
-    include_package_data=True,
     license="Apache License 2.0",
     keywords="taipy-config",
     name="taipy-config",
-    package_dir={"": "src"},
-    packages=find_namespace_packages(where="src")
+    packages=find_namespace_packages(where=".")
     + find_packages(include=["taipy", "taipy.config", "taipy.config.*", "taipy.logger", "taipy.logger.*"]),
+    include_package_data=True,
     test_suite="tests",
     tests_require=test_requirements,
     url="https://github.com/avaiga/taipy-config",

+ 2 - 2
taipy/core/MANIFEST.in

@@ -1,2 +1,2 @@
-include taipy/core/*.json
-include taipy/core/config/*.json
+include *.json
+include config/*.json

+ 7 - 1
taipy/core/job/job.py

@@ -132,10 +132,16 @@ class Job(_Entity, _Labeled):
     def creation_date(self, val):
         self._creation_date = val
 
-    @property
+    @property  # type: ignore
+    @_self_reload(_MANAGER_NAME)
     def stacktrace(self) -> List[str]:
         return self._stacktrace
 
+    @stacktrace.setter  # type: ignore
+    @_self_setter(_MANAGER_NAME)
+    def stacktrace(self, val):
+        self._stacktrace = val
+
     @property
     def version(self):
         return self._version

+ 3 - 3
taipy/core/setup.py

@@ -20,7 +20,8 @@ from setuptools import find_namespace_packages, find_packages, setup
 with open("README.md") as readme_file:
     readme = readme_file.read()
 
-with open(f"src{os.sep}taipy{os.sep}core{os.sep}version.json") as version_file:
+version_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "version.json")
+with open(version_path) as version_file:
     version = json.load(version_file)
     version_string = f'{version.get("major", 0)}.{version.get("minor", 0)}.{version.get("patch", 0)}'
     if vext := version.get("ext"):
@@ -67,8 +68,7 @@ setup(
     license="Apache License 2.0",
     keywords="taipy-core",
     name="taipy-core",
-    package_dir={"": "src"},
-    packages=find_namespace_packages(where="src") + find_packages(include=["taipy", "taipy.core", "taipy.core.*"]),
+    packages=find_namespace_packages(where=".") + find_packages(include=["taipy", "taipy.core", "taipy.core.*"]),
     include_package_data=True,
     test_suite="tests",
     tests_require=test_requirements,

+ 4 - 4
taipy/gui/MANIFEST.in

@@ -1,4 +1,4 @@
-recursive-include taipy/gui/webapp *
-include taipy/gui/version.json
-include taipy/gui/viselements.json
-include taipy/gui/*.pyi
+recursive-include webapp *
+include version.json
+include viselements.json
+include *.pyi

+ 10 - 2
taipy/gui/_renderers/builder.py

@@ -37,7 +37,7 @@ from ..utils import (
 )
 from ..utils.chart_config_builder import _CHART_NAMES, _build_chart_config
 from ..utils.table_col_builder import _enhance_columns, _get_name_indexed_property
-from ..utils.types import _TaipyBase, _TaipyData
+from ..utils.types import _TaipyBase, _TaipyData, _TaipyToJson
 from .json import _TaipyJsonEncoder
 from .utils import _add_to_dict_and_get, _get_columns_dict, _get_tuple_val
 
@@ -569,10 +569,14 @@ class _Builder:
     def _set_chart_selected(self, max=0):
         name = "selected"
         default_sel = self.__attributes.get(name)
+        if not isinstance(default_sel, list) and name in self.__attributes:
+            default_sel = []
         idx = 1
         name_idx = f"{name}[{idx}]"
         sel = self.__attributes.get(name_idx)
-        while idx <= max:
+        if not isinstance(sel, list) and name_idx in self.__attributes:
+            sel = []
+        while idx <= max or name_idx in self.__attributes:
             if sel is not None or default_sel is not None:
                 self.__update_vars.extend(
                     self.__set_list_attribute(
@@ -585,6 +589,8 @@ class _Builder:
             idx += 1
             name_idx = f"{name}[{idx}]"
             sel = self.__attributes.get(name_idx)
+            if not isinstance(sel, list) and name_idx in self.__attributes:
+                sel = []
 
     def _get_list_attribute(self, name: str, list_type: PropertyType):
         varname = self.__hashes.get(name)
@@ -893,6 +899,8 @@ class _Builder:
             if not isinstance(attr, tuple):
                 attr = (attr,)
             var_type = _get_tuple_val(attr, 1, PropertyType.string)
+            if var_type == PropertyType.to_json:
+                var_type = _TaipyToJson
             if var_type == PropertyType.boolean:
                 def_val = _get_tuple_val(attr, 2, False)
                 val = self.__get_boolean_attribute(attr[0], def_val)

+ 2 - 0
taipy/gui/_renderers/factory.py

@@ -85,6 +85,7 @@ class _Factory:
             control_type=control_type,
             element_name="Chart",
             attributes=attrs,
+            default_value=None
         )
         .set_value_and_default(with_default=False, var_type=PropertyType.data)
         .set_attributes(
@@ -103,6 +104,7 @@ class _Factory:
                 ("template", PropertyType.dict),
                 ("template[dark]", PropertyType.dict, gui._get_config("chart_dark_template", None)),
                 ("template[light]", PropertyType.dict),
+                ("figure", PropertyType.to_json)
             ]
         )
         ._get_chart_config("scatter", "lines+markers")

+ 2 - 1
taipy/gui/data/data_accessor.py

@@ -94,7 +94,8 @@ class _DataAccessors(object):
         value = value.get()
         access = self.__access_4_type.get(type(value).__name__)
         if access is None:
-            _warn(f"Can't find Data Accessor for type {type(value).__name__}.")
+            if value:
+                _warn(f"Can't find Data Accessor for type {type(value).__name__}.")
             return self.__invalid_data_accessor
         return access
 

+ 3 - 0
taipy/gui/gui.py

@@ -84,6 +84,7 @@ from .utils import (
     _TaipyData,
     _TaipyLov,
     _TaipyLovValue,
+    _TaipyToJson,
     _to_camel_case,
     _variable_decode,
     is_debugging,
@@ -965,6 +966,8 @@ class Gui:
                         ]
                     else:
                         newvalue = self.__adapter._run_for_var(newvalue.get_name(), newvalue.get(), id_only=True)
+                elif isinstance(newvalue, _TaipyToJson):
+                    newvalue = newvalue.get()
                 if isinstance(newvalue, (dict, _MapDict)):
                     continue  # this var has no transformer
                 debug_warnings: t.List[warnings.WarningMessage] = []

+ 2 - 0
taipy/gui/gui_types.py

@@ -28,6 +28,7 @@ from .utils import (
     _TaipyLov,
     _TaipyLovValue,
     _TaipyNumber,
+    _TaipyToJson,
 )
 
 
@@ -135,6 +136,7 @@ class PropertyType(Enum):
     The property holds an inner attributes that is defined by a library and cannot be overridden by the user.
     """
     inner = "inner"
+    to_json = _TaipyToJson
 
 
 @t.overload  # noqa: F811

+ 3 - 3
taipy/gui/setup.py

@@ -22,7 +22,8 @@ from setuptools.command.build_py import build_py
 
 readme = Path("README.md").read_text()
 
-with open(f"src{os.sep}taipy{os.sep}gui{os.sep}version.json") as version_file:
+version_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "version.json")
+with open(version_path) as version_file:
     version = json.load(version_file)
     version_string = f'{version.get("major", 0)}.{version.get("minor", 0)}.{version.get("patch", 0)}'
     if vext := version.get("ext"):
@@ -94,8 +95,7 @@ setup(
     include_package_data=True,
     keywords="taipy-gui",
     name="taipy-gui",
-    package_dir={"": "src"},
-    packages=find_namespace_packages(where="src") + find_packages(include=["taipy", "taipy.gui", "taipy.gui.*"]),
+    packages=find_namespace_packages(where=".") + find_packages(include=["taipy", "taipy.gui", "taipy.gui.*"]),
     test_suite="tests",
     tests_require=test_requirements,
     url="https://github.com/avaiga/taipy-gui",

+ 1 - 0
taipy/gui/utils/__init__.py

@@ -50,5 +50,6 @@ from .types import (
     _TaipyLov,
     _TaipyLovValue,
     _TaipyNumber,
+    _TaipyToJson,
 )
 from .varnamefromcontent import _varname_from_content

+ 24 - 0
taipy/gui/utils/types.py

@@ -9,10 +9,12 @@
 # an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 # specific language governing permissions and limitations under the License.
 
+
 import json
 import typing as t
 from abc import ABC
 from datetime import datetime
+from importlib.util import find_spec
 
 from .._warnings import _warn
 from . import _date_to_string, _MapDict, _string_to_date, _variable_decode
@@ -186,3 +188,25 @@ class _TaipyDict(_TaipyBase):
     @staticmethod
     def get_hash():
         return _TaipyBase._HOLDER_PREFIX + "Di"
+
+
+class _TaipyToJson(_TaipyBase):
+    def get(self):
+        val = super().get()
+        if not val:
+            return None
+        if find_spec("plotly") and find_spec("plotly.graph_objs"):
+            from plotly.graph_objs import Figure as PlotlyFigure
+
+            if isinstance(val, PlotlyFigure):
+                try:
+                    return [json.loads(val.to_json())]
+                except Exception as e:
+                    _warn("Issue while serializing Plotly Figure", e)
+                    return None
+        _warn("'figure' property value must be a plotly.graph_objects.Figure.")
+        return None
+
+    @staticmethod
+    def get_hash():
+        return _TaipyBase._HOLDER_PREFIX + "Tj"

+ 7 - 2
taipy/gui/viselements.json

@@ -554,6 +554,11 @@
             "type": "dynamic(bool)",
             "default_value": "False",
             "doc": "Allows dynamic config refresh if set to True."
+          },
+          {
+            "name": "figure",
+            "type": "plotly.graph_objects.Figure",
+            "doc": "A figure as produced by plotly."
           }
         ]
       }
@@ -984,12 +989,12 @@
           {
             "name": "tooltip",
             "type": "str",
-            "doc": "The name of the function that must return a tooltip text for a cell.<br/>See <a href=\"#cell%20tooltip\">below</a> for details."
+            "doc": "The name of the function that must return a tooltip text for a cell.<br/>See <a href=\"#cell-tooltips\">below</a> for details."
           },
           {
             "name": "tooltip[<i>column_name</i>]",
             "type": "str",
-            "doc": "The name of the function that must return a tooltip text for a cell.<br/>See <a href=\"#cell%20tooltip\">below</a> for details."
+            "doc": "The name of the function that must return a tooltip text for a cell.<br/>See <a href=\"#cell-tooltips\">below</a> for details."
           },
           {
             "name": "width",

+ 1 - 1
taipy/rest/MANIFEST.in

@@ -1 +1 @@
-include src/taipy/rest/*.json
+include *.json

+ 3 - 3
taipy/rest/setup.py

@@ -16,7 +16,8 @@ from setuptools import find_namespace_packages, find_packages, setup
 with open("README.md") as readme_file:
     readme = readme_file.read()
 
-with open(f"src{os.sep}taipy{os.sep}rest{os.sep}version.json") as version_file:
+version_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "version.json")
+with open(version_path) as version_file:
     version = json.load(version_file)
     version_string = f'{version.get("major", 0)}.{version.get("minor", 0)}.{version.get("patch", 0)}'
     if vext := version.get("ext"):
@@ -29,8 +30,7 @@ setup(
     python_requires=">=3.8",
     version=version_string,
     author_email="dev@taipy.io",
-    packages=find_namespace_packages(where="src") + find_packages(include=["taipy", "taipy.rest"]),
-    package_dir={"": "src"},
+    packages=find_namespace_packages(where=".") + find_packages(include=["taipy", "taipy.rest"]),
     include_package_data=True,
     long_description=readme,
     long_description_content_type="text/markdown",

+ 1 - 0
taipy/templates/MANIFEST.in

@@ -1 +1,2 @@
 recursive-include taipy/templates *
+include *.json

+ 3 - 3
taipy/templates/setup.py

@@ -19,7 +19,8 @@ from setuptools import find_namespace_packages, find_packages, setup
 with open("README.md", "rb") as readme_file:
     readme = readme_file.read().decode("UTF-8")
 
-with open(f"src{os.sep}taipy{os.sep}templates{os.sep}version.json") as version_file:
+version_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "version.json")
+with open(version_path) as version_file:
     version = json.load(version_file)
     version_string = f'{version.get("major", 0)}.{version.get("minor", 0)}.{version.get("patch", 0)}'
     if vext := version.get("ext"):
@@ -47,8 +48,7 @@ setup(
     long_description_content_type="text/markdown",
     keywords="taipy-templates",
     name="taipy-templates",
-    package_dir={"": "src"},
-    packages=find_namespace_packages(where="src") + find_packages(include=["taipy"]),
+    packages=find_namespace_packages(where=".") + find_packages(include=["taipy"]),
     include_package_data=True,
     test_suite="tests",
     url="https://github.com/avaiga/taipy-templates",

+ 16 - 0
tests/core/job/test_job.py

@@ -147,6 +147,22 @@ def test_status_job(task):
     assert job.is_skipped()
 
 
+def test_stacktrace_job(task):
+    submission = _SubmissionManagerFactory._build_manager()._create(task.id, task._ID_PREFIX, task.config_id)
+    job = Job("job_id", task, submission.id, "SCENARIO_scenario_config")
+
+    fake_stacktraces = [
+        """Traceback (most recent call last):
+File "<stdin>", line 1, in <module>
+ZeroDivisionError: division by zero""",
+        "Another error",
+        "yet\nAnother\nError",
+    ]
+
+    job.stacktrace = fake_stacktraces
+    assert job.stacktrace == fake_stacktraces
+
+
 def test_notification_job(task):
     subscribe = MagicMock()
     submission = _SubmissionManagerFactory._build_manager()._create(task.id, task._ID_PREFIX, task.config_id)

+ 20 - 0
tools/release/check_releases.py

@@ -0,0 +1,20 @@
+import os
+import sys
+
+if __name__ == "__main__":
+    _path = sys.argv[1]
+    _version = sys.argv[2]
+
+    packages = [
+        f"taipy-{_version}.tar.gz",
+        f"taipy-config-{_version}.tar.gz",
+        f"taipy-core-{_version}.tar.gz",
+        f"taipy-rest-{_version}.tar.gz",
+        f"taipy-gui-{_version}.tar.gz",
+        f"taipy-templates-{_version}.tar.gz",
+    ]
+
+    for package in packages:
+        if not os.path.exists(os.path.join(_path, package)):
+            print(f"Package {package} does not exist")  # noqa: T201
+            sys.exit(1)

+ 15 - 0
tools/release/extract_from_setup.py

@@ -0,0 +1,15 @@
+import sys
+
+
+def extract_gui_version(base_path: str) -> None:
+    with open("setup.py") as f:
+        for line in f:
+            if "taipy-gui" in line:
+                start = line.find("taipy-gui")
+                end = line.rstrip().find('",')
+                print(f"VERSION={line[start:end]}")  # noqa: T201
+                break
+
+
+if __name__ == "__main__":
+    extract_gui_version(sys.argv[1])

+ 115 - 0
tools/release/setup_version.py

@@ -0,0 +1,115 @@
+import json
+import os
+import re
+import sys
+from dataclasses import asdict, dataclass
+from typing import Optional
+
+
+@dataclass
+class Version:
+    major: str
+    minor: str
+    patch: str
+    ext: str
+
+    def bump_ext_version(self) -> None:
+        if not self.ext:
+            return
+        reg = re.compile(r"[0-9]+$")
+        num = reg.findall(self.ext)[0]
+
+        self.ext = self.ext.replace(num, str(int(num) + 1))
+
+    def validate_suffix(self, suffix="dev"):
+        if suffix not in self.ext:
+            raise Exception(f"Version does not contain suffix {suffix}")
+
+    @property
+    def name(self) -> str:
+        """returns a string representation of a version"""
+        return f"{self.major}.{self.minor}.{self.patch}"
+
+    @property
+    def dev_name(self) -> str:
+        """returns a string representation of a version"""
+        return f"{self.name}.{self.ext}"
+
+    def __str__(self) -> str:
+        """returns a string representation of a version"""
+        version_str = f"{self.major}.{self.minor}.{self.patch}"
+        if self.ext:
+            version_str = f"{version_str}.{self.ext}"
+        return version_str
+
+
+def __load_version_from_path(base_path: str) -> Version:
+    """Load version.json file from base path."""
+    with open(os.path.join(base_path, "version.json")) as version_file:
+        data = json.load(version_file)
+        return Version(**data)
+
+
+def __write_version_to_path(base_path: str, version: Version) -> None:
+    with open(os.path.join(base_path, "version.json"), "w") as version_file:
+        json.dump(asdict(version), version_file)
+
+
+def extract_version(base_path: str) -> Version:
+    """
+    Load version.json file from base path and return the version string.
+    """
+    return __load_version_from_path(base_path)
+
+
+def __setup_dev_version(
+    version: Version, _base_path: str, name: Optional[str] = None, bump_dev_version: bool = False
+) -> None:
+    version.validate_suffix()
+
+    name = f"{name}_VERSION" if name else "VERSION"
+    print(f"{name}={version.dev_name}")  # noqa: T201
+
+    version.bump_ext_version()
+
+    __write_version_to_path(_base_path, version)
+    print(f"NEW_{name}={version.dev_name}")  # noqa: T201
+
+
+def __setup_prod_version(version: Version, target_version: str, branch_name: str, name: str = None) -> None:
+    if str(version) != target_version:
+        raise ValueError(f"Current version={version} does not match target version={target_version}")
+
+    if target_branch_name := f"release/{version.major}.{version.minor}" != branch_name:
+        raise ValueError(
+            f"Branch name mismatch branch={branch_name} does not match target branch name={target_branch_name}"
+        )
+
+    name = f"{name}_VERSION" if name else "VERSION"
+    print(f"{name}={version.name}")  # noqa: T201
+
+
+if __name__ == "__main__":
+    paths = (
+        [sys.argv[1]]
+        if sys.argv[1] != "ALL"
+        else [
+            f"taipy{os.sep}config",
+            f"taipy{os.sep}core",
+            f"taipy{os.sep}rest",
+            f"taipy{os.sep}gui",
+            f"taipy{os.sep}templates",
+            "taipy",
+        ]
+    )
+    _environment = sys.argv[2]
+
+    for _path in paths:
+        _version = extract_version(_path)
+        _name = None if _path == "taipy" else _path.split(os.sep)[-1]
+
+        if _environment == "dev":
+            __setup_dev_version(_version, _path, _name)
+
+        if _environment == "production":
+            __setup_prod_version(_version, sys.argv[3], sys.argv[4], _name)

+ 32 - 0
tools/release/update_setup.py

@@ -0,0 +1,32 @@
+import sys
+
+
+def update_setup() -> None:
+    with open("setup.taipy.py", mode="r") as setup_r, open("setup.py", mode="w") as setup_w:
+        in_requirements = False
+        looking = True
+        for line in setup_r:
+            if looking:
+                if line.lstrip().startswith("requirements") and line.rstrip().endswith("["):
+                    in_requirements = True
+                elif in_requirements:
+                    if line.strip() == "]":
+                        looking = False
+                    else:
+                        if line.lstrip().startswith('"taipy-gui@git+https'):
+                            start = line.find('"taipy-gui')
+                            end = line.rstrip().find(",")
+                            line = f'{line[:start]}"taipy-gui=={sys.argv[1]}"{line[end:]}'
+                        elif line.lstrip().startswith('"taipy-rest@git+https'):
+                            start = line.find('"taipy-rest')
+                            end = line.rstrip().find(",")
+                            line = f'{line[:start]}"taipy-rest=={sys.argv[2]}"{line[end:]}'
+                        elif line.lstrip().startswith('"taipy-templates@git+https'):
+                            start = line.find('"taipy-templates')
+                            end = line.rstrip().find(",")
+                            line = f'{line[:start]}"taipy-templates=={sys.argv[3]}"{line[end:]}'
+            setup_w.write(line)
+
+
+if __name__ == "__main__":
+    update_setup()

+ 35 - 0
tools/release/update_setup_requirements.py

@@ -0,0 +1,35 @@
+import os
+import sys
+from typing import Dict
+
+BASE_PATH = "./tools/packages"
+
+
+def __build_taipy_package_line(line: str, version: str) -> str:
+    return f"{line.strip()} @ https://github.com/Avaiga/taipy/releases/download/{version}/{version}.tar.gz\n"
+
+
+def update_setup_requirements(package: str, versions: Dict) -> None:
+    _path = os.path.join(BASE_PATH, package, "setup.requirements.txt")
+    lines = []
+    with open(_path, mode="r") as req:
+        for line in req:
+            if v := versions.get(line.strip()):
+                line = __build_taipy_package_line(line, v)
+            lines.append(line)
+
+    with open(_path, 'w') as file:
+        file.writelines(lines)
+
+
+if __name__ == "__main__":
+    _package = sys.argv[1]
+    _versions = {
+        "taipy-config": sys.argv[2],
+        "taipy-core": sys.argv[3],
+        "taipy-gui": sys.argv[4],
+        "taipy-rest": sys.argv[5],
+        "taipy-templates": sys.argv[6],
+    }
+
+    update_setup_requirements(_package, _versions)

+ 56 - 0
tools/validate_taipy_install.py

@@ -0,0 +1,56 @@
+import logging
+import os
+import sys
+
+
+def test_import_taipy_packages() -> bool:
+    """
+    Import taipy package and call gui, Scenario and rest attributes.
+    """
+    import taipy as tp
+
+    valid_install = True
+    if not hasattr(tp, "gui"):
+        logging.error("Taipy installation has no attribute gui")
+        valid_install = False
+    if not hasattr(tp, "Scenario"):
+        logging.error("Taipy installation has no attribute Scenario")
+        valid_install = False
+    if not hasattr(tp, "rest"):
+        logging.error("Taipy installation has no attribute rest")
+        valid_install = False
+
+    return valid_install
+
+
+def is_taipy_gui_install_valid() -> bool:
+    from pathlib import Path
+
+    import taipy
+
+    taipy_gui_core_path = Path(taipy.__file__).absolute().parent / "gui_core" / "lib" / "taipy-gui-core.js"
+    if not taipy_gui_core_path.exists():
+        logging.error("File taipy-gui-core.js not found in taipy installation path")
+        return False
+
+    taipy_gui_webapp_path = Path(taipy.__file__).absolute().parent / "gui" / "webapp"
+
+    if not os.path.exists(taipy_gui_webapp_path):
+        return False
+
+    if not any(fname.endswith('.js') for fname in os.listdir(taipy_gui_webapp_path)):
+        logging.error("Missing js files inside taipy gui webapp folder")
+        return False
+
+    return True
+
+
+if __name__ == "__main__":
+    logging.basicConfig(level=logging.INFO)
+
+    logging.info("Trying to import taipy and verify it's main attributes")
+    if not test_import_taipy_packages() or not is_taipy_gui_install_valid():
+        sys.exit(1)
+
+    logging.info("Taipy installation Validated")
+    sys.exit(0)