From 743446766b97122e0ca56198d5349cbdce700ed2 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 29 Apr 2026 15:42:25 +0200 Subject: [PATCH 1/7] Apply templates --- .badgery.yaml | 72 ++ .copier-answers.yml | 20 + .github/actions/download-artifact/action.yml | 50 + .github/actions/github-script/action.yml | 19 + .../actions/setup-easyscience-bot/action.yml | 40 + .github/actions/setup-pixi/action.yml | 44 + .github/actions/upload-artifact/action.yml | 49 + .github/actions/upload-codecov/action.yml | 42 + .github/configs/pages-deployment.json | 6 + .github/configs/rulesets-develop.json | 37 + .github/configs/rulesets-gh-pages.json | 19 + .github/configs/rulesets-master.json | 30 + .github/scripts/backmerge-conflict-issue.js | 69 + .github/workflows/backmerge.yml | 114 ++ .github/workflows/cleanup.yml | 89 ++ .github/workflows/coverage.yml | 99 ++ .github/workflows/dashboard.yml | 107 ++ .github/workflows/docs.yml | 187 +++ .github/workflows/issues-labels.yml | 56 + .github/workflows/lint-format.yml | 127 ++ .github/workflows/pr-labels.yml | 56 + .github/workflows/pypi-publish.yml | 51 + .github/workflows/pypi-test.yml | 83 ++ .github/workflows/release-notes.yml | 76 ++ .github/workflows/release-pr.yml | 58 + .github/workflows/security.yml | 98 ++ .github/workflows/test-trigger.yml | 45 + .github/workflows/test.yml | 305 +++++ .github/workflows/tutorial-tests-trigger.yml | 45 + .github/workflows/tutorial-tests.yml | 63 + .gitignore | 64 +- .pre-commit-config.yaml | 61 + .prettierignore | 32 + CONTRIBUTING.md | 445 +++++++ LICENSE | 3 +- README.md | 50 +- codecov.yml | 13 + docs/docs/api-reference/index.md | 8 + docs/docs/assets/javascripts/extra.js | 27 + docs/docs/assets/javascripts/mathjax.js | 33 + docs/docs/assets/stylesheets/extra.css | 359 ++++++ docs/docs/index.md | 20 + docs/docs/installation-and-setup/index.md | 281 +++++ docs/docs/introduction/index.md | 68 + docs/docs/tutorials/index.md | 21 + docs/docs/tutorials/tutorial.py | 20 + docs/docs/user-guide/index.md | 24 + docs/includes/abbreviations.md | 15 + docs/mkdocs.yml | 180 +++ docs/overrides/.icons/app.svg | 4 + docs/overrides/.icons/google-colab.svg | 7 + docs/overrides/main.html | 39 + docs/overrides/partials/logo.html | 15 + pixi.lock | 1118 ++++++++++++++++- pixi.toml | 228 +++- prettierrc.toml | 22 + pyproject.toml | 345 ++++- src/easyapplication/__init__.py | 3 + tests/functional/test_dummy.py | 7 + tests/integration/fitting/test_dummy.py | 17 + .../integration/scipp-analysis/test_dummy.py | 17 + tests/unit/test_dummy.py | 7 + tools/license_headers.py | 321 +++++ tools/test_scripts.py | 57 + tools/update_docs_assets.py | 91 ++ tools/update_github_labels.py | 341 +++++ 66 files changed, 6434 insertions(+), 85 deletions(-) create mode 100644 .badgery.yaml create mode 100644 .copier-answers.yml create mode 100644 .github/actions/download-artifact/action.yml create mode 100644 .github/actions/github-script/action.yml create mode 100644 .github/actions/setup-easyscience-bot/action.yml create mode 100644 .github/actions/setup-pixi/action.yml create mode 100644 .github/actions/upload-artifact/action.yml create mode 100644 .github/actions/upload-codecov/action.yml create mode 100644 .github/configs/pages-deployment.json create mode 100644 .github/configs/rulesets-develop.json create mode 100644 .github/configs/rulesets-gh-pages.json create mode 100644 .github/configs/rulesets-master.json create mode 100644 .github/scripts/backmerge-conflict-issue.js create mode 100644 .github/workflows/backmerge.yml create mode 100644 .github/workflows/cleanup.yml create mode 100644 .github/workflows/coverage.yml create mode 100644 .github/workflows/dashboard.yml create mode 100644 .github/workflows/docs.yml create mode 100644 .github/workflows/issues-labels.yml create mode 100644 .github/workflows/lint-format.yml create mode 100644 .github/workflows/pr-labels.yml create mode 100644 .github/workflows/pypi-publish.yml create mode 100644 .github/workflows/pypi-test.yml create mode 100644 .github/workflows/release-notes.yml create mode 100644 .github/workflows/release-pr.yml create mode 100644 .github/workflows/security.yml create mode 100644 .github/workflows/test-trigger.yml create mode 100644 .github/workflows/test.yml create mode 100644 .github/workflows/tutorial-tests-trigger.yml create mode 100644 .github/workflows/tutorial-tests.yml create mode 100644 .pre-commit-config.yaml create mode 100644 .prettierignore create mode 100644 CONTRIBUTING.md create mode 100644 codecov.yml create mode 100644 docs/docs/api-reference/index.md create mode 100644 docs/docs/assets/javascripts/extra.js create mode 100644 docs/docs/assets/javascripts/mathjax.js create mode 100644 docs/docs/assets/stylesheets/extra.css create mode 100644 docs/docs/index.md create mode 100644 docs/docs/installation-and-setup/index.md create mode 100644 docs/docs/introduction/index.md create mode 100644 docs/docs/tutorials/index.md create mode 100644 docs/docs/tutorials/tutorial.py create mode 100644 docs/docs/user-guide/index.md create mode 100644 docs/includes/abbreviations.md create mode 100644 docs/mkdocs.yml create mode 100644 docs/overrides/.icons/app.svg create mode 100644 docs/overrides/.icons/google-colab.svg create mode 100644 docs/overrides/main.html create mode 100644 docs/overrides/partials/logo.html create mode 100644 prettierrc.toml create mode 100644 src/easyapplication/__init__.py create mode 100644 tests/functional/test_dummy.py create mode 100644 tests/integration/fitting/test_dummy.py create mode 100644 tests/integration/scipp-analysis/test_dummy.py create mode 100644 tests/unit/test_dummy.py create mode 100644 tools/license_headers.py create mode 100644 tools/test_scripts.py create mode 100644 tools/update_docs_assets.py create mode 100644 tools/update_github_labels.py diff --git a/.badgery.yaml b/.badgery.yaml new file mode 100644 index 00000000..99bcdc1c --- /dev/null +++ b/.badgery.yaml @@ -0,0 +1,72 @@ +default_branch: master +develop_branch: develop + +cards: + - group: Tests + type: gh_action + title: Code/package tests (GitHub) + file: test.yml + enabled: true + - group: Tests + type: gh_action + title: Tutorial tests (GitHub) + file: tutorial-tests.yml + enabled: true + + - group: Tests + type: gh_action + title: Package tests (PyPI) + file: pypi-test.yml + enabled: true + + - group: Code Quality + type: codefactor + title: Code quality (CodeFactor) + enabled: true + + - group: Code Quality + type: radon_mi + title: Maintainability index (radon) + report: reports/{branch}/maintainability-index.json + enabled: true + + - group: Code Quality + type: radon_cc + title: Cyclomatic complexity (radon) + report: reports/{branch}/cyclomatic-complexity.json + enabled: true + + - group: Size + type: radon_loc + title: Source/Logical lines of code (radon) + report: reports/{branch}/raw-metrics.json + enabled: true + + - group: Size + type: radon_ff + title: Functions/Files count (radon) + report: reports/{branch}/cyclomatic-complexity.json + enabled: true + + - group: Coverage + type: codecov + title: Unit test coverage (Codecov) + flag: unittests + enabled: true + + - group: Coverage + type: interrogate + title: Docstring coverage (interrogate) + report: reports/{branch}/coverage-docstring.txt + enabled: true + - group: Build & Release + type: gh_action + title: Publishing (PyPI) + workflow: pypi-publish.yml + enabled: true + + - group: Build & Release + type: gh_action + title: Docs build/deployment + workflow: docs.yml + enabled: true diff --git a/.copier-answers.yml b/.copier-answers.yml new file mode 100644 index 00000000..a2c2a231 --- /dev/null +++ b/.copier-answers.yml @@ -0,0 +1,20 @@ +# WARNING: Do not edit this file manually. +# Any changes will be overwritten by Copier. +_commit: v0.11.0 +_src_path: gh:easyscience/templates +lib_docs_url: https://easyscience.github.io/easyapp +lib_doi: 10.5281/zenodo.18163581 +lib_package_name: easyapplication +lib_python_max: '3.14' +lib_python_min: '3.11' +lib_repo_name: easyapp +project_contact_email: support@easyscience.org +project_copyright_years: 2021-2026 +project_extended_description: A collection of Qt/QML graphical components to create + cross-platform applications with intuitive graphical interface based on the EasyScience + framework +project_name: EasyApplication +project_short_description: Qt/QML components for building graphical applications +project_shortcut: EA +project_type: lib +template_type: lib diff --git a/.github/actions/download-artifact/action.yml b/.github/actions/download-artifact/action.yml new file mode 100644 index 00000000..e4fd62f5 --- /dev/null +++ b/.github/actions/download-artifact/action.yml @@ -0,0 +1,50 @@ +name: 'Download artifact' +description: 'Generic wrapper for actions/download-artifact' +inputs: + name: + description: 'Name of the artifact to download' + required: true + + path: + description: 'Destination path' + required: false + default: '.' + + pattern: + description: 'Glob pattern to match artifact names (optional)' + required: false + default: '' + + merge-multiple: + description: 'Merge multiple artifacts into the same directory' + required: false + default: 'false' + + github-token: + description: 'GitHub token for cross-repo download (optional)' + required: false + default: '' + + repository: + description: 'owner/repo for cross-repo download (optional)' + required: false + default: '' + + run-id: + description: 'Workflow run ID for cross-run download (optional)' + required: false + default: '' + +runs: + using: 'composite' + steps: + - name: Download artifact + uses: actions/download-artifact@v4 + with: + name: ${{ inputs.name }} + path: ${{ inputs.path }} + pattern: ${{ inputs.pattern }} + merge-multiple: ${{ inputs.merge-multiple }} + github-token: ${{ inputs.github-token }} + repository: ${{ inputs.repository }} + run-id: ${{ inputs.run-id }} diff --git a/.github/actions/github-script/action.yml b/.github/actions/github-script/action.yml new file mode 100644 index 00000000..ab32da56 --- /dev/null +++ b/.github/actions/github-script/action.yml @@ -0,0 +1,19 @@ +name: 'GitHub Script' +description: 'Wrapper for actions/github-script' +inputs: + script: + description: 'JavaScript to run' + required: true + + github-token: + description: 'GitHub token (defaults to github.token)' + required: false + default: ${{ github.token }} + +runs: + using: 'composite' + steps: + - uses: actions/github-script@v8 + with: + script: ${{ inputs.script }} + github-token: ${{ inputs.github-token }} diff --git a/.github/actions/setup-easyscience-bot/action.yml b/.github/actions/setup-easyscience-bot/action.yml new file mode 100644 index 00000000..4b28eaf8 --- /dev/null +++ b/.github/actions/setup-easyscience-bot/action.yml @@ -0,0 +1,40 @@ +name: 'Setup EasyScience bot for pushing' +description: 'Create GitHub App token and configure git identity + origin remote' +inputs: + app-id: + description: 'GitHub App ID' + required: true + private-key: + description: 'GitHub App private key (PEM)' + required: true + repositories: + description: 'Additional repositories to grant access to (newline-separated)' + required: false + default: '' + +outputs: + token: + description: 'Installation access token' + value: ${{ steps.app-token.outputs.token }} + +runs: + using: 'composite' + steps: + - name: Create GitHub App installation token + id: app-token + uses: actions/create-github-app-token@v2 + with: + app-id: ${{ inputs.app-id }} + private-key: ${{ inputs.private-key }} + repositories: ${{ inputs.repositories }} + + - name: Configure git for pushing + shell: bash + run: | + git config user.name "easyscience[bot]" + git config user.email "${{ inputs.app-id }}+easyscience[bot]@users.noreply.github.com" + + - name: Configure origin remote to use the bot token + shell: bash + run: | + git remote set-url origin https://x-access-token:${{ steps.app-token.outputs.token }}@github.com/${{ github.repository }}.git diff --git a/.github/actions/setup-pixi/action.yml b/.github/actions/setup-pixi/action.yml new file mode 100644 index 00000000..167ee623 --- /dev/null +++ b/.github/actions/setup-pixi/action.yml @@ -0,0 +1,44 @@ +name: 'Setup Pixi Environment' +description: 'Sets up pixi with common configuration' +inputs: + environments: + description: 'Pixi environments to setup' + required: false + default: 'default' + activate-environment: + description: 'Environment to activate' + required: false + default: 'default' + run-install: + description: 'Whether to run pixi install' + required: false + default: 'true' + locked: + description: 'Whether to run pixi install --locked' + required: false + default: 'false' + frozen: + description: 'Whether to run pixi install --frozen' + required: false + default: 'true' + cache: + description: 'Whether to use cache' + required: false + default: 'false' + post-cleanup: + description: 'Whether to run post cleanup' + required: false + default: 'false' + +runs: + using: 'composite' + steps: + - uses: prefix-dev/setup-pixi@v0.9.4 + with: + environments: ${{ inputs.environments }} + activate-environment: ${{ inputs.activate-environment }} + run-install: ${{ inputs.run-install }} + locked: ${{ inputs.locked }} + frozen: ${{ inputs.frozen }} + cache: ${{ inputs.cache }} + post-cleanup: ${{ inputs.post-cleanup }} diff --git a/.github/actions/upload-artifact/action.yml b/.github/actions/upload-artifact/action.yml new file mode 100644 index 00000000..825ac396 --- /dev/null +++ b/.github/actions/upload-artifact/action.yml @@ -0,0 +1,49 @@ +name: 'Upload artifact' +description: 'Generic wrapper for actions/upload-artifact' +inputs: + name: + description: 'Artifact name' + required: true + + path: + description: 'File(s)/dir(s)/glob(s) to upload (newline-separated)' + required: true + + include-hidden-files: + description: 'Include hidden files' + required: false + default: 'true' + + if-no-files-found: + description: 'warn | error | ignore' + required: false + default: 'error' + + compression-level: + description: '0-9 (0 = no compression)' + required: false + default: '0' + + retention-days: + description: 'Retention in days (optional)' + required: false + default: '' + + overwrite: + description: 'Overwrite an existing artifact with the same name' + required: false + default: 'false' + +runs: + using: 'composite' + steps: + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.name }} + path: ${{ inputs.path }} + include-hidden-files: ${{ inputs.include-hidden-files }} + if-no-files-found: ${{ inputs.if-no-files-found }} + compression-level: ${{ inputs.compression-level }} + retention-days: ${{ inputs.retention-days }} + overwrite: ${{ inputs.overwrite }} diff --git a/.github/actions/upload-codecov/action.yml b/.github/actions/upload-codecov/action.yml new file mode 100644 index 00000000..37d6298a --- /dev/null +++ b/.github/actions/upload-codecov/action.yml @@ -0,0 +1,42 @@ +name: 'Upload coverage to Codecov' +description: 'Generic wrapper for codecov/codecov-action@v5' + +inputs: + name: + description: 'Codecov upload name' + required: true + + flags: + description: 'Codecov flags' + required: false + default: '' + + files: + description: 'Coverage report files' + required: true + + fail_ci_if_error: + description: 'Fail CI if upload fails' + required: false + default: 'true' + + verbose: + description: 'Enable verbose output' + required: false + default: 'true' + + token: + description: 'Codecov token' + required: true + +runs: + using: composite + steps: + - uses: codecov/codecov-action@v5 + with: + name: ${{ inputs.name }} + flags: ${{ inputs.flags }} + files: ${{ inputs.files }} + fail_ci_if_error: ${{ inputs.fail_ci_if_error }} + verbose: ${{ inputs.verbose }} + token: ${{ inputs.token }} diff --git a/.github/configs/pages-deployment.json b/.github/configs/pages-deployment.json new file mode 100644 index 00000000..c0d3fbee --- /dev/null +++ b/.github/configs/pages-deployment.json @@ -0,0 +1,6 @@ +{ + "source": { + "branch": "gh-pages", + "path": "/" + } +} diff --git a/.github/configs/rulesets-develop.json b/.github/configs/rulesets-develop.json new file mode 100644 index 00000000..04489e52 --- /dev/null +++ b/.github/configs/rulesets-develop.json @@ -0,0 +1,37 @@ +{ + "name": "develop branch", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": ["refs/heads/develop"], + "exclude": [] + } + }, + "bypass_actors": [ + { + "actor_id": 2476259, + "actor_type": "Integration", + "bypass_mode": "always" + } + ], + "rules": [ + { + "type": "non_fast_forward" + }, + { + "type": "deletion" + }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["squash"], + "dismiss_stale_reviews_on_push": false, + "require_code_owner_review": false, + "require_last_push_approval": false, + "required_approving_review_count": 0, + "required_review_thread_resolution": false + } + } + ] +} diff --git a/.github/configs/rulesets-gh-pages.json b/.github/configs/rulesets-gh-pages.json new file mode 100644 index 00000000..ebf38928 --- /dev/null +++ b/.github/configs/rulesets-gh-pages.json @@ -0,0 +1,19 @@ +{ + "name": "gh-pages branch", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": ["refs/heads/gh-pages"], + "exclude": [] + } + }, + "rules": [ + { + "type": "non_fast_forward" + }, + { + "type": "deletion" + } + ] +} diff --git a/.github/configs/rulesets-master.json b/.github/configs/rulesets-master.json new file mode 100644 index 00000000..f658a5c6 --- /dev/null +++ b/.github/configs/rulesets-master.json @@ -0,0 +1,30 @@ +{ + "name": "master branch", + "target": "branch", + "enforcement": "active", + "conditions": { + "ref_name": { + "include": ["~DEFAULT_BRANCH"], + "exclude": [] + } + }, + "rules": [ + { + "type": "non_fast_forward" + }, + { + "type": "deletion" + }, + { + "type": "pull_request", + "parameters": { + "allowed_merge_methods": ["merge"], + "dismiss_stale_reviews_on_push": false, + "require_code_owner_review": false, + "require_last_push_approval": false, + "required_approving_review_count": 0, + "required_review_thread_resolution": false + } + } + ] +} diff --git a/.github/scripts/backmerge-conflict-issue.js b/.github/scripts/backmerge-conflict-issue.js new file mode 100644 index 00000000..f6bd98b5 --- /dev/null +++ b/.github/scripts/backmerge-conflict-issue.js @@ -0,0 +1,69 @@ +module.exports = async ({ github, context, core }) => { + // Repo context + const owner = context.repo.owner + const repo = context.repo.repo + + // Link to the exact workflow run that detected the conflict + const runUrl = `${context.serverUrl}/${owner}/${repo}/actions/runs/${context.runId}` + + // We use a *stable title* so we can find/reuse the same "conflict tracker" issue + // instead of creating a new issue on every failed run. + const title = 'Backmerge conflict: master → develop' + + // Comment/issue body includes the run URL so maintainers can jump straight to logs. + const body = [ + 'Automatic backmerge failed due to merge conflicts.', + '', + `Workflow run: ${runUrl}`, + '', + 'Manual resolution required.', + ].join('\n') + + // Label applied to the tracker issue (assumed to already exist in the repo). + const label = '[bot] backmerge' + + // Search issues by title across *open and closed* issues. + // Why: if the conflict was resolved previously and the issue was closed, + // we prefer to reopen it and append a new comment instead of creating duplicates. + const q = `repo:${owner}/${repo} is:issue in:title "${title}"` + const search = await github.rest.search.issuesAndPullRequests({ + q, + per_page: 10, + }) + + // Pick the first exact-title match (search can return partial matches). + const existing = search.data.items.find((i) => i.title === title) + + if (existing) { + // If a tracker issue exists, reuse it: + // - reopen it if needed + // - add a comment with the new run URL + if (existing.state === 'closed') { + await github.rest.issues.update({ + owner, + repo, + issue_number: existing.number, + state: 'open', + }) + } + + await github.rest.issues.createComment({ + owner, + repo, + issue_number: existing.number, + body, + }) + + core.notice(`Conflict issue updated: #${existing.number}`) + return + } + + // No tracker issue exists yet -> create the first one. + await github.rest.issues.create({ + owner, + repo, + title, + body, + labels: [label], + }) +} diff --git a/.github/workflows/backmerge.yml b/.github/workflows/backmerge.yml new file mode 100644 index 00000000..d8569f05 --- /dev/null +++ b/.github/workflows/backmerge.yml @@ -0,0 +1,114 @@ +# This workflow automatically merges `master` into `develop` whenever a +# new version release with a tag is published. It can also be triggered +# manually via workflow_dispatch for cases where an automatic backmerge +# is needed outside of the standard release process. +# If a merge conflict occurs, the workflow creates an issue to notify +# maintainers for manual resolution. + +name: Backmerge (master → develop) + +on: + release: + types: [published, prereleased] + workflow_dispatch: + +permissions: + contents: write + issues: write + +concurrency: + group: backmerge-master-into-develop + cancel-in-progress: false + +# Opt into Node.js 24 for all JavaScript actions. +# Remove once all referenced actions natively target Node 24. +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + backmerge: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout repository (for local actions) + uses: actions/checkout@v5 + + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + repositories: ${{ github.event.repository.name }} + + - name: Checkout repository (with bot token) + uses: actions/checkout@v5 + with: + fetch-depth: 0 + token: ${{ steps.bot.outputs.token }} + + - name: Configure git identity + run: | + git config user.name "easyscience[bot]" + git config user.email "${{ vars.EASYSCIENCE_APP_ID }}+easyscience[bot]@users.noreply.github.com" + + - name: Set merge message + run: | + if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then + MESSAGE="Backmerge: master into develop (manual) [skip ci]" + else + TAG="${{ github.event.release.tag_name }}" + MESSAGE="Backmerge: master (${TAG}) into develop [skip ci]" + fi + + echo "MESSAGE=$MESSAGE" >> "$GITHUB_ENV" + echo "message=$MESSAGE" >> "$GITHUB_OUTPUT" + echo "📝 Merge message: $MESSAGE" | tee -a "$GITHUB_STEP_SUMMARY" + + - name: Prepare branches + run: | + git fetch origin master develop + git checkout -B develop origin/develop + + - name: Check if develop is already up-to-date + id: up_to_date + run: | + if git merge-base --is-ancestor origin/master develop; then + echo "value=true" >> "$GITHUB_OUTPUT" + echo "ℹ️ Develop is already up-to-date with master" | tee -a "$GITHUB_STEP_SUMMARY" + else + echo "value=false" >> "$GITHUB_OUTPUT" + fi + + - name: Try merge master into develop + id: merge + if: steps.up_to_date.outputs.value == 'false' + continue-on-error: true + run: | + if ! git merge origin/master --no-ff -m "${MESSAGE}"; then + echo "conflict=true" >> "$GITHUB_OUTPUT" + echo "❌ Backmerge conflict detected." | tee -a "$GITHUB_STEP_SUMMARY" + git status --porcelain || true + exit 0 + fi + + echo "conflict=false" >> "$GITHUB_OUTPUT" + echo "✅ Merge commit created." | tee -a "$GITHUB_STEP_SUMMARY" + + - name: Push to develop (if merge succeeded) + if: + steps.up_to_date.outputs.value == 'false' && steps.merge.outputs.conflict == + 'false' + run: | + git push origin develop + echo "🚀 Backmerge successful: master → develop" | tee -a "$GITHUB_STEP_SUMMARY" + + - name: Create issue (if merge failed with conflicts) + if: steps.merge.outputs.conflict == 'true' + uses: ./.github/actions/github-script + with: + github-token: ${{ steps.bot.outputs.token }} + script: | + const run = require('./.github/scripts/backmerge-conflict-issue.js') + await run({ github, context, core }) diff --git a/.github/workflows/cleanup.yml b/.github/workflows/cleanup.yml new file mode 100644 index 00000000..d3ebf3a2 --- /dev/null +++ b/.github/workflows/cleanup.yml @@ -0,0 +1,89 @@ +# This workflow will delete old workflow runs based on the input +# parameters. +# https://github.com/Mattraks/delete-workflow-runs + +name: Old workflow runs cleanup + +on: + # Run monthly, at 00:00 on the 1st day of month. + schedule: + - cron: '0 0 1 * *' + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + inputs: + days: + description: 'Number of days.' + required: true + default: 30 + minimum_runs: + description: 'The minimum runs to keep for each workflow.' + required: true + default: 6 + delete_workflow_pattern: + description: + 'The name or filename of the workflow. if not set then it will target all + workflows.' + required: false + delete_workflow_by_state_pattern: + description: + 'Remove workflow by state: active, deleted, disabled_fork, + disabled_inactivity, disabled_manually' + required: true + default: 'All' + type: choice + options: + - 'All' + - active + - deleted + - disabled_inactivity + - disabled_manually + delete_run_by_conclusion_pattern: + description: + 'Remove workflow by conclusion: action_required, cancelled, failure, skipped, + success' + required: true + default: 'All' + type: choice + options: + - 'All' + - action_required + - cancelled + - failure + - skipped + - success + dry_run: + description: 'Only log actions, do not perform any delete operations (dry run).' + required: false + default: 'false' + type: choice + options: + - 'false' + - 'true' + +# Opt into Node.js 24 for all JavaScript actions. +# Remove once all referenced actions natively target Node 24. +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + del-runs: + runs-on: ubuntu-latest + + permissions: + actions: write + + steps: + - name: Delete workflow runs + uses: Mattraks/delete-workflow-runs@v2 + with: + token: ${{ github.token }} + repository: ${{ github.repository }} + retain_days: ${{ github.event.inputs.days }} + keep_minimum_runs: ${{ github.event.inputs.minimum_runs }} + delete_workflow_pattern: ${{ github.event.inputs.delete_workflow_pattern }} + delete_workflow_by_state_pattern: + ${{ github.event.inputs.delete_workflow_by_state_pattern }} + delete_run_by_conclusion_pattern: + ${{ github.event.inputs.delete_run_by_conclusion_pattern }} + dry_run: ${{ github.event.inputs.dry_run }} diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml new file mode 100644 index 00000000..1ad93139 --- /dev/null +++ b/.github/workflows/coverage.yml @@ -0,0 +1,99 @@ +name: Coverage checks + +on: + # Trigger the workflow on push to develop + push: + branches: + - develop + # Do not run on version tags (those are handled by other workflows) + tags-ignore: ['v*'] + # Trigger the workflow on pull request + pull_request: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Need permissions to trigger the dashboard build workflow +permissions: + actions: write + contents: read + +# Allow only one concurrent workflow per PR or branch ref. +# Cancel in-progress runs only for pull requests, but let branch push runs finish. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + +# Set the environment variables to be used in all jobs defined in this workflow +env: + CI_BRANCH: ${{ github.head_ref || github.ref_name }} + # Opt into Node.js 24 for all JavaScript actions. + # Remove once all referenced actions natively target Node 24. + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + # Job 1: Run docstring coverage + docstring-coverage: + runs-on: ubuntu-latest + + steps: + - name: Check-out repository + uses: actions/checkout@v5 + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + + - name: Run docstring coverage + run: pixi run docstring-coverage + + # Job 2: Run unit tests with coverage and upload to Codecov + unit-tests-coverage: + runs-on: ubuntu-latest + + steps: + - name: Check-out repository + uses: actions/checkout@v5 + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + + - name: Run unit tests with coverage + run: pixi run unit-tests-coverage --cov-report=xml:coverage-unit.xml + + - name: Upload unit tests coverage to Codecov + if: ${{ !cancelled() }} + uses: ./.github/actions/upload-codecov + with: + name: unit-tests-job + flags: unittests + files: ./coverage-unit.xml + token: ${{ secrets.CODECOV_TOKEN }} + + # Job 2: Run integration tests with coverage and upload to Codecov + integration-tests-coverage: + runs-on: ubuntu-latest + + steps: + - name: Check-out repository + uses: actions/checkout@v5 + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + + - name: Run integration tests with coverage + run: + pixi run integration-tests-coverage --cov-report=xml:coverage-integration.xml + + - name: Upload integration tests coverage to Codecov + if: ${{ !cancelled() }} + uses: ./.github/actions/upload-codecov + with: + name: integration-tests-job + flags: integration + files: ./coverage-integration.xml + token: ${{ secrets.CODECOV_TOKEN }} + + # Job 4: Build and publish dashboard (reusable workflow) + run-reusable-workflows: + needs: [docstring-coverage, unit-tests-coverage, integration-tests-coverage] # depend on the previous jobs + uses: ./.github/workflows/dashboard.yml + secrets: inherit diff --git a/.github/workflows/dashboard.yml b/.github/workflows/dashboard.yml new file mode 100644 index 00000000..68513427 --- /dev/null +++ b/.github/workflows/dashboard.yml @@ -0,0 +1,107 @@ +name: Dashboard build and publish + +on: + workflow_dispatch: + workflow_call: + +permissions: + contents: read + +# Set the environment variables to be used in all jobs defined in this workflow +env: + CI_BRANCH: ${{ github.head_ref || github.ref_name }} + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + DEVELOP_BRANCH: develop + REPO_OWNER: ${{ github.repository_owner }} + REPO_NAME: ${{ github.event.repository.name }} + # Opt into Node.js 24 for all JavaScript actions. + # Remove once all referenced actions natively target Node 24. + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + dashboard: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + + - name: Install badgery + shell: bash + run: pixi add --pypi --git https://github.com/enhantica/badgery badgery + + - name: Run docstring coverage and code complexity/maintainability checks + run: | + for BRANCH in $DEFAULT_BRANCH $DEVELOP_BRANCH $CI_BRANCH; do + echo + echo "🔹🔸🔹🔸🔹 Processing branch $BRANCH 🔹🔸🔹🔸🔹" + if [ -d "../$BRANCH" ]; then + echo "Branch $BRANCH already processed, skipping" + continue + fi + + git worktree add ../$BRANCH origin/$BRANCH + mkdir -p reports/$BRANCH + + echo "Docstring coverage for branch $BRANCH" + pixi run interrogate -c pyproject.toml --fail-under=0 ../$BRANCH/src > reports/$BRANCH/coverage-docstring.txt + + echo "Cyclomatic complexity for branch $BRANCH" + pixi run radon cc -s -j ../$BRANCH/src > reports/$BRANCH/cyclomatic-complexity.json + + echo "Maintainability index for branch $BRANCH" + pixi run radon mi -j ../$BRANCH/src > reports/$BRANCH/maintainability-index.json + + echo "Raw metrics for branch $BRANCH" + pixi run radon raw -s -j ../$BRANCH/src > reports/$BRANCH/raw-metrics.json + done + + - name: Generate dashboard HTML + run: > + pixi run python -m badgery --config .badgery.yaml --repo ${{ github.repository + }} --branch ${{ env.CI_BRANCH }} --output index.html + + - name: Prepare publish directory + run: | + mkdir -p _dashboard_publish/${{ env.REPO_NAME }}/${{ env.CI_BRANCH }} + cp index.html _dashboard_publish/${{ env.REPO_NAME }}/${{ env.CI_BRANCH }} + + # Create GitHub App token for pushing to external dashboard repo. + # The 'repositories' parameter is required to grant access to repos + # other than the one where the workflow is running. + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + repositories: | + ${{ github.event.repository.name }} + dashboard + + # Publish to external dashboard repository with retry logic. + # Retry is needed to handle transient GitHub API/authentication issues + # that occasionally cause 403 errors when multiple workflows push concurrently. + # Uses personal_token (not github_token) as GITHUB_TOKEN cannot access external repos. + - name: Publish to main branch of ${{ github.repository }} + uses: Wandalen/wretry.action@v3.8.0 + with: + attempt_limit: 3 + attempt_delay: 15000 # 15 seconds between retries + action: peaceiris/actions-gh-pages@v4 + with: | + publish_dir: ./_dashboard_publish + keep_files: true + external_repository: ${{ env.REPO_OWNER }}/dashboard + publish_branch: master + personal_token: ${{ steps.bot.outputs.token }} + + - name: Add dashboard link to summary + run: | + URL="https://${{ env.REPO_OWNER }}.github.io/dashboard/${{ env.REPO_NAME }}/${{ env.CI_BRANCH }}" + echo "Dashboard link: [$URL]($URL)" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 00000000..642f8757 --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,187 @@ +# This workflow builds and deploys documentation for the project. +# +# Overview: +# - Converts tutorial Python scripts to Jupyter notebooks and executes them. +# - Builds the documentation site using MkDocs with the Material theme. +# - Uploads the built site as an artifact for local inspection. +# - Deploys versioned documentation to the gh-pages branch using Mike: +# - For release tags (v*): deploys to a versioned folder (e.g., /0.9.1/) and updates /latest/. +# - For branches: deploys to /dev/. +# +# The action summary page will contain a link to the built artifact for downloading +# and inspecting, as well as a link to the deployed documentation site. + +name: Docs build and deployment + +on: + # Trigger the workflow on push + push: + # Selected branches + branches: [develop] # master and main are already verified in PR + # Runs on creating a new tag starting with 'v', e.g. 'v1.0.3' + tags: ['v*'] + # Trigger the workflow on pull request + pull_request: + # Selected branches + branches: [master, main, develop] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Allow only one concurrent deployment to gh-pages at a time. +# All docs workflows share the same concurrency group to prevent race conditions +# when multiple branches/tags trigger simultaneous deployments. +concurrency: + group: docs-gh-pages-deploy + cancel-in-progress: false + +# Set the environment variables to be used in all jobs defined in this workflow +env: + # CI_BRANCH - the branch name (used in mkdocs.yml) + # For PRs: github.head_ref is the source branch + # For pushes: github.ref_name is the branch + # For tags: github.ref_name is the tag name + # GITHUB_REPOSITORY - the repository name (used in mkdocs.yml) + # NOTEBOOKS_DIR - the directory containing the Jupyter notebooks (used in mkdocs.yml) + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + DEVELOP_BRANCH: develop + CI_BRANCH: ${{ github.head_ref || github.ref_name }} + IS_RELEASE_TAG: ${{ startsWith(github.ref, 'refs/tags/v') }} + GITHUB_REPOSITORY: ${{ github.repository }} + NOTEBOOKS_DIR: tutorials + # Opt into Node.js 24 for all JavaScript actions. + # Remove once all referenced actions natively target Node 24. + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + # Single job that builds and deploys documentation. + # Uses macOS runner for consistent Plotly chart rendering. + build-deploy-docs: + runs-on: ubuntu-latest # macos-latest + + permissions: + contents: write # Required for pushing to the gh-pages branch + + steps: + # Setting DOCS_VERSION to be used in mkdocs.yml, and then in the + # main.html template. It defines the versioned docs subfolder name + # in the gh-pages branch. If it's a release tag, use the version + # number without the 'v' prefix, otherwise use 'dev'. + # Setting RELEASE_VERSION to be used in mkdocs.yml to show + # the latest release version in the index.md file. If it's a + # release tag, use the tag name, otherwise use the branch name + # for development builds. + - name: Set extra env variables + shell: bash + run: | + if [[ "${IS_RELEASE_TAG}" == "true" ]]; then + RELEASE_VERSION="${GITHUB_REF_NAME}" + DOCS_VERSION="${RELEASE_VERSION#v}" + else + RELEASE_VERSION="${CI_BRANCH}" + DOCS_VERSION="dev" + fi + echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "$GITHUB_ENV" + echo "DOCS_VERSION=${DOCS_VERSION}" >> "$GITHUB_ENV" + + # Check out the repository source code. + # Note: The gh-pages branch is fetched separately later for mike deployment. + - name: Checkout repository + uses: actions/checkout@v5 + + # Activate dark mode to create documentation with Plotly charts in dark mode + # Need a better solution to automatically switch the chart colour theme based on the mkdocs material switcher + # Something similar to mkdocs_plotly_plugin https://haoda-li.github.io/mkdocs-plotly-plugin/, + # but for generating documentation from notepads + #- name: Activate dark mode + # run: | + # brew install dark-mode + # dark-mode status + # dark-mode on + # dark-mode status + + # Set up the pixi package manager and install dependencies from pixi.toml. + # Uses frozen lockfile to ensure reproducible builds. + - name: Set up pixi + uses: ./.github/actions/setup-pixi + # Pre-import the main package to exclude info messages from the docs + # E.g., Matplotlib may print messages to stdout/stderr when first + # imported. This step allows to avoid "Matplotlib is building the font + # cache" messages during notebook execution. + - name: Pre-build site step + run: pixi run python -c "import easyapplication" + + # Prepare the Jupyter notebooks for documentation (strip output, etc.). + - name: Prepare notebooks + run: pixi run notebook-prepare + + # Execute all Jupyter notebooks to generate output cells (plots, tables, etc.). + # Uses multiple cores for parallel execution to speed up the process. + - name: Run notebooks + # if: false # Temporarily disabled to speed up the docs build + run: pixi run notebook-exec + + # Build the static files for the documentation site for local inspection + # Input: docs/ directory containing the Markdown files + # Output: site/ directory containing the generated HTML files + - name: Build site for local check + run: pixi run docs-build-local + + # Upload the static files from the site/ directory to be used for + # local check + - name: Upload built site as artifact + uses: ./.github/actions/upload-artifact + with: + name: site-local_easyapplication-lib-${{ env.RELEASE_VERSION }} + path: docs/site/ + + # Create GitHub App token for pushing to gh-pages as easyscience[bot]. + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + + # Configure git identity and remote URL so mike pushes as easyscience[bot]. + - name: Configure git for pushing + run: | + set -euo pipefail + git config user.name "easyscience[bot]" + git config user.email "${{ vars.EASYSCIENCE_APP_ID }}+easyscience[bot]@users.noreply.github.com" + git remote set-url origin "https://x-access-token:${{ steps.bot.outputs.token }}@github.com/${{ github.repository }}.git" + + # Fetch the gh-pages branch to ensure mike has the latest remote state. + # This is required because the checkout step only fetches the source branch, + # not the gh-pages branch that mike needs to update. + - name: Fetch gh-pages branch + run: | + git fetch origin gh-pages:gh-pages 2>/dev/null || true + + # Deploy versioned documentation using mike (MkDocs plugin for versioning). + # - For release tags (v*): deploys to versioned folder (e.g., /0.9.1/) and aliases to /latest/. + # - For branches: deploys to /dev/. + # The "${RELEASE_VERSION#v}" syntax strips the 'v' prefix (v0.9.1 -> 0.9.1). + # Also sets 'latest' as the default version for the version selector. + - name: Rebuild and deploy docs with mike + run: | + # Exit on error (-e), undefined vars (-u), and pipeline failures (pipefail) + set -euo pipefail + + REPO_NAME="${{ github.event.repository.name }}" + BASE_URL="https://easyscience.github.io/${REPO_NAME}" + + # Deploy the release version and update the "latest" alias + if [[ "${IS_RELEASE_TAG}" == "true" ]]; then + pixi run docs-deploy-pre "${RELEASE_VERSION#v}" latest + pixi run docs-set-default-pre latest + DEPLOYMENT_URL="${BASE_URL}/latest" + + # Deploy/update the "dev" alias (or whatever your convention is) + else + pixi run docs-deploy-pre dev + DEPLOYMENT_URL="${BASE_URL}/dev" + + fi + + # Add links to the action summary page for easy access + echo "🔗 deployment url [${DEPLOYMENT_URL}](${DEPLOYMENT_URL})" >> "${GITHUB_STEP_SUMMARY}" diff --git a/.github/workflows/issues-labels.yml b/.github/workflows/issues-labels.yml new file mode 100644 index 00000000..ed9d1c8b --- /dev/null +++ b/.github/workflows/issues-labels.yml @@ -0,0 +1,56 @@ +# Verifies if an issue has at least one of the `[scope]` and one of the +# `[priority]` labels. If not, the bot adds labels with a warning emoji +# to indicate that those labels need to be added. + +name: Issue labels check + +on: + issues: + types: [opened, labeled, unlabeled] + +permissions: + issues: write + +# Opt into Node.js 24 for all JavaScript actions. +# Remove once all referenced actions natively target Node 24. +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + check-labels: + if: github.actor != 'easyscience[bot]' + + runs-on: ubuntu-latest + + concurrency: + group: issue-labels-${{ github.event.issue.number }} + cancel-in-progress: true + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + + - name: Check for required [scope] label + uses: trstringer/require-label-prefix@v1 + with: + secret: ${{ steps.bot.outputs.token }} + prefix: '[scope]' + labelSeparator: ' ' + addLabel: true + defaultLabel: '[scope] ⚠️ label needed' + + - name: Check for required [priority] label + uses: trstringer/require-label-prefix@v1 + with: + secret: ${{ steps.bot.outputs.token }} + prefix: '[priority]' + labelSeparator: ' ' + addLabel: true + defaultLabel: '[priority] ⚠️ label needed' diff --git a/.github/workflows/lint-format.yml b/.github/workflows/lint-format.yml new file mode 100644 index 00000000..720c9a4a --- /dev/null +++ b/.github/workflows/lint-format.yml @@ -0,0 +1,127 @@ +# The workflow checks +# - the validity of pyproject.toml, +# - the presence and correctness of SPDX license headers, +# - linting and formatting of Python code, +# - linting and formatting of docstrings in Python code, +# - formatting of non-Python files (like markdown and toml). +# - linting of Python code in Jupyter notebooks (for library template). +# +# A summary of the checks is added to the GitHub Actions summary. + +name: Lint and format checks + +on: + # Trigger the workflow on push + push: + branches-ignore: [master, main] # Already verified in PR + # Do not run this workflow on creating a new tag starting with + # 'v', e.g. 'v1.0.3' (see publish-pypi.yml) + tags-ignore: ['v*'] + # Trigger the workflow on pull request + pull_request: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Allow only one concurrent workflow, skipping runs queued between the run +# in-progress and latest queued. And cancel in-progress runs. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +# Set the environment variables to be used in all jobs defined in this workflow +env: + CI_BRANCH: ${{ github.head_ref || github.ref_name }} + # Opt into Node.js 24 for all JavaScript actions. + # Remove once all referenced actions natively target Node 24. + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + lint-format: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + + - name: Run post-install developer steps + run: pixi run post-install + + - name: Check validity of pyproject.toml + id: pyproject + continue-on-error: true + shell: bash + run: pixi run pyproject-check + + - name: Check SPDX license headers + id: license_headers + continue-on-error: true + shell: bash + run: pixi run license-check + + - name: Check linting of Python code + id: py_lint + continue-on-error: true + shell: bash + run: pixi run py-lint-check + + - name: Check formatting of Python code + id: py_format + continue-on-error: true + shell: bash + run: pixi run py-format-check + + - name: Check linting of docstrings in Python code + id: docstring_lint + continue-on-error: true + shell: bash + run: pixi run docstring-lint-check + + - name: Check formatting of non-Python files (md, toml, etc.) + id: nonpy_format + continue-on-error: true + shell: bash + run: pixi run nonpy-format-check + + - name: Check linting of Python code in Jupyter notebooks (ipynb) + id: notebook_lint + continue-on-error: true + shell: bash + run: pixi run notebook-lint-check + + # Add summary + - name: Add quality checks summary + if: always() + shell: bash + run: | + { + echo "## 🧪 Checks Summary" + echo "" + echo "| Check | Status |" + echo "|-------|--------|" + echo "| pyproject.toml | ${{ steps.pyproject.outcome == 'success' && '✅' || '❌' }} |" + echo "| license headers | ${{ steps.license_headers.outcome == 'success' && '✅' || '❌' }} |" + echo "| py lint | ${{ steps.py_lint.outcome == 'success' && '✅' || '❌' }} |" + echo "| py format | ${{ steps.py_format.outcome == 'success' && '✅' || '❌' }} |" + echo "| docstring lint | ${{ steps.docstring_lint.outcome == 'success' && '✅' || '❌' }} |" + echo "| nonpy format | ${{ steps.nonpy_format.outcome == 'success' && '✅' || '❌' }} |" + echo "| notebooks lint | ${{ steps.notebook_lint.outcome == 'success' && '✅' || '❌' }} |" + } >> "$GITHUB_STEP_SUMMARY" + + # Fail job if any check failed + - name: Fail job if any check failed + if: | + steps.pyproject.outcome == 'failure' + || steps.license_headers.outcome == 'failure' + || steps.py_lint.outcome == 'failure' + || steps.py_format.outcome == 'failure' + || steps.docstring_lint.outcome == 'failure' + || steps.nonpy_format.outcome == 'failure' + || steps.notebook_lint.outcome == 'failure' + shell: bash + run: exit 1 diff --git a/.github/workflows/pr-labels.yml b/.github/workflows/pr-labels.yml new file mode 100644 index 00000000..642cd318 --- /dev/null +++ b/.github/workflows/pr-labels.yml @@ -0,0 +1,56 @@ +# Verifies if a pull request has at least one label from a set of valid +# labels before it can be merged. +# +# NOTE: +# This workflow may be triggered twice in quick succession when a PR is +# created: +# 1) `opened` — when the pull request is initially created +# 2) `labeled` — if labels are added immediately after creation +# (e.g. by manual labeling, another workflow, or GitHub App). +# +# These are separate GitHub events, so two workflow runs can be started. + +name: PR labels check + +on: + pull_request_target: + types: [opened, labeled, unlabeled, synchronize] + +permissions: + pull-requests: read + +jobs: + check-labels: + runs-on: ubuntu-latest + + steps: + - name: Check for valid labels + run: | + PR_LABELS=$(echo '${{ toJson(github.event.pull_request.labels.*.name) }}' | jq -r '.[]') + + echo "Current PR labels: $PR_LABELS" + VALID_LABELS=( + "[bot] release" + "[scope] bug" + "[scope] documentation" + "[scope] enhancement" + "[scope] maintenance" + "[scope] significant" + ) + + found=false + for label in "${VALID_LABELS[@]}"; do + if echo "$PR_LABELS" | grep -Fxq "$label"; then + echo "✅ Found valid label: $label" + found=true + break + fi + done + + if [ "$found" = false ]; then + echo "ERROR: PR must have at least one of the following labels:" + for label in "${VALID_LABELS[@]}"; do + echo " - $label" + done + exit 1 + fi diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml new file mode 100644 index 00000000..ed231ca8 --- /dev/null +++ b/.github/workflows/pypi-publish.yml @@ -0,0 +1,51 @@ +# Builds a Python package and publish it to PyPI when a new tag is +# created. + +name: PyPI publishing + +on: + # Runs on creating a new tag starting with 'v', e.g. 'v1.0.3' + push: + tags: ['v*'] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Opt into Node.js 24 for all JavaScript actions. +# Remove once all referenced actions natively target Node 24. +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + pypi-publish: + runs-on: ubuntu-latest + + permissions: + contents: read + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + + steps: + - name: Check-out repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 # full history with tags to get the version number by versioningit + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + + # Build the Python package (to dist/ folder) + - name: Create Python package + run: pixi run default-build + + # Publish the package to PyPI (from dist/ folder) + # Instead of publishing with personal access token, we use + # GitHub Actions OIDC to get a short-lived token from PyPI. + # New publisher must be previously configured in PyPI at + # https://pypi.org/manage/project/easyapplication/settings/publishing/ + # Use the following data: + # Owner: easyscience + # Repository name: easyapp + # Workflow name: pypi-publish.yml + - name: Publish to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + packages-dir: 'dist' diff --git a/.github/workflows/pypi-test.yml b/.github/workflows/pypi-test.yml new file mode 100644 index 00000000..8acd655d --- /dev/null +++ b/.github/workflows/pypi-test.yml @@ -0,0 +1,83 @@ +name: PyPI package tests + +on: + # Run daily, at 00:00. + schedule: + - cron: '0 0 * * *' + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Allow only one concurrent workflow, skipping runs queued between the run +# in-progress and latest queued. And cancel in-progress runs. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +# Set the environment variables to be used in all jobs defined in this workflow +env: + CI_BRANCH: ${{ github.head_ref || github.ref_name }} + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + # Opt into Node.js 24 for all JavaScript actions. + # Remove once all referenced actions natively target Node 24. + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + # Job 1: Test installation from PyPI on multiple OS + pypi-package-tests: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + with: + environments: '' + activate-environment: '' + run-install: false + frozen: false + + - name: Init pixi project + run: pixi init easyapplication + + - name: Set the minimum system requirements + working-directory: easyapplication + run: pixi project system-requirements add macos 14.0 + + - name: Add Python 3.14 from Conda + working-directory: easyapplication + run: pixi add "python=3.14" + + - name: Add other Conda dependencies + working-directory: easyapplication + run: pixi add gsl + + - name: Add easyapplication (with dev dependencies) from PyPI + working-directory: easyapplication + run: pixi add --pypi "easyapplication[dev]" + + - name: Run unit tests to verify the installation + working-directory: easyapplication + run: pixi run python -m pytest ../tests/unit/ --color=yes -v + + - name: Run functional tests to verify the installation + working-directory: easyapplication + run: pixi run python -m pytest ../tests/functional/ --color=yes -v + + - name: Run integration tests to verify the installation + working-directory: easyapplication + run: pixi run python -m pytest ../tests/integration/ --color=yes -n auto + + # Job 2: Build and publish dashboard (reusable workflow) + run-reusable-workflows: + needs: pypi-package-tests # depend on previous job + uses: ./.github/workflows/dashboard.yml + secrets: inherit diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml new file mode 100644 index 00000000..cdc0330c --- /dev/null +++ b/.github/workflows/release-notes.yml @@ -0,0 +1,76 @@ +# Drafts your next Release notes as pull requests are merged into +# default branch + +name: Release draft update + +on: + # Runs on pushes targeting the default branch (updates the real draft release) + push: + branches: [master, main] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Opt into Node.js 24 for all JavaScript actions. +# Remove once all referenced actions natively target Node 24. +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + draft-release-notes: + permissions: + # write permission is required to create a github release + contents: write + + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + with: + fetch-depth: 0 # full history with tags to get the version number + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + + - name: Drafts the next release notes + id: draft + uses: enhantica/drafterino@v2 + with: + config: | + title: 'easyapplication $COMPUTED_VERSION' + tag: 'v$COMPUTED_VERSION' + note-template: '- $PR_TITLE (#$PR_NUMBER)' + + default-bump: post + + major-bump-labels: ['[scope] significant'] + minor-bump-labels: ['[scope] enhancement'] + patch-bump-labels: ['[scope] bug', '[scope] maintenance'] + post-bump-labels: ['[scope] documentation'] + + release-notes: + - title: 'Added' + labels: ['[scope] significant', '[scope] enhancement'] + - title: 'Fixed' + labels: ['[scope] bug'] + - title: 'Changed' + labels: ['[scope] maintenance', '[scope] documentation'] + env: + GITHUB_TOKEN: ${{ steps.bot.outputs.token }} + + - name: Create GitHub draft release + uses: softprops/action-gh-release@v2 + with: + draft: true + tag_name: ${{ steps.draft.outputs.tag_name }} + name: ${{ steps.draft.outputs.release_name }} + body: ${{ steps.draft.outputs.release_notes }} + env: + GITHUB_TOKEN: ${{ steps.bot.outputs.token }} diff --git a/.github/workflows/release-pr.yml b/.github/workflows/release-pr.yml new file mode 100644 index 00000000..5694a2a0 --- /dev/null +++ b/.github/workflows/release-pr.yml @@ -0,0 +1,58 @@ +# This workflow creates an automated release PR from a source branch into the default branch. +# +# Usage: +# - Triggered manually via workflow_dispatch. +# - Creates a PR titled "Release: merge into ". +# - Adds the label "[bot] release" so it is excluded from changelogs. +# - The PR body makes clear that this is automation only (no review needed). + +name: 'Release PR (develop → master)' + +on: + workflow_dispatch: + inputs: + source_branch: + description: 'Source branch to create PR from' + required: false + default: 'develop' + type: string + +permissions: + contents: read + pull-requests: write + +env: + DEFAULT_BRANCH: ${{ github.event.repository.default_branch }} + SOURCE_BRANCH: ${{ inputs.source_branch || 'develop' }} + # Opt into Node.js 24 for all JavaScript actions. + # Remove once all referenced actions natively target Node 24. + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + create-pull-request: + runs-on: ubuntu-latest + steps: + - name: Checkout ${{ env.SOURCE_BRANCH }} branch + uses: actions/checkout@v5 + with: + ref: ${{ env.SOURCE_BRANCH }} + + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + + - name: Create PR from ${{ env.SOURCE_BRANCH }} to ${{ env.DEFAULT_BRANCH }} + env: + GH_TOKEN: ${{ steps.bot.outputs.token }} + run: | + gh pr create \ + --base ${{ env.DEFAULT_BRANCH }} \ + --head ${{ env.SOURCE_BRANCH }} \ + --title "Release: merge ${{ env.SOURCE_BRANCH }} into ${{ env.DEFAULT_BRANCH }}" \ + --label "[bot] release" \ + --body "This PR is created automatically to trigger the release pipeline. It merges the accumulated changes from \`${{ env.SOURCE_BRANCH }}\` into \`${{ env.DEFAULT_BRANCH }}\`. + + ⚠️ It is labeled \`[bot] release\` and is excluded from release notes and version bump logic." diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml new file mode 100644 index 00000000..a53e9afc --- /dev/null +++ b/.github/workflows/security.yml @@ -0,0 +1,98 @@ +# Code scanning (CodeQL) for vulnerabilities and insecure coding patterns. +# +# What this workflow does +# - Runs GitHub CodeQL analysis and uploads results to your repository's Security tab. +# - Triggers on PRs (so findings appear as PR checks) and on pushes to `develop`. +# - Runs on a weekly schedule. +# +# Where to find results on GitHub +# - Repository → Security → Code scanning alerts +# (You can filter by tool = CodeQL and by branch.) +# +# Where to configure on GitHub +# - Repository → Settings → Advanced Security +# Enable "GitHub Advanced Security" (if available) and configure CodeQL there. +# - Repository → Security → Code scanning alerts +# This page shows findings produced by this workflow. +# +# Notes about the scheduled run +# - Scheduled workflows are triggered from the repository's *default branch*. +# If your default branch is `master` but you want the scheduled scan to analyze +# `develop`, this workflow checks out `develop` explicitly for scheduled runs. +# +# References +# - CodeQL Action: https://github.com/github/codeql-action +# - Advanced setup docs: https://docs.github.com/en/code-security/code-scanning + +name: Security scans with CodeQL + +on: + # Run on pull requests so results show up as PR checks and code + # scanning alerts. + pull_request: + branches: [master, main, develop] + + # Run on pushes (e.g., after merging PRs). + push: + branches: [master, main, develop] + + # Run weekly. (Cron is in UTC.) + schedule: + - cron: '0 3 * * 1' + +permissions: + contents: read + security-events: write + +# Opt into Node.js 24 for all JavaScript actions. +# Remove once all referenced actions natively target Node 24. +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + codeql: + name: Code scanning + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + # Keep this list tight to avoid noise and speed up runs. + language: [python, actions] + + steps: + # Scheduled workflows run from the default branch. + # We explicitly analyze `develop` on the schedule to keep the scan + # focused on the active dev branch. + - name: Checkout repository (scheduled → develop) + if: ${{ github.event_name == 'schedule' }} + uses: actions/checkout@v5 + with: + ref: develop + + - name: Checkout repository + if: ${{ github.event_name != 'schedule' }} + uses: actions/checkout@v5 + + - name: Initialize CodeQL + uses: github/codeql-action/init@v4 + with: + languages: ${{ matrix.language }} + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v4 + + print-link: + name: Print results link + runs-on: ubuntu-latest + + needs: codeql + permissions: {} # no special perms needed just to print links + + steps: + - name: Add Code Scanning link to job summary + run: | + echo "## 🔎 CodeQL Results" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "View Code Scanning alerts here:" >> $GITHUB_STEP_SUMMARY + echo "${{ github.server_url }}/${{ github.repository }}/security/code-scanning" >> $GITHUB_STEP_SUMMARY diff --git a/.github/workflows/test-trigger.yml b/.github/workflows/test-trigger.yml new file mode 100644 index 00000000..f4a39e93 --- /dev/null +++ b/.github/workflows/test-trigger.yml @@ -0,0 +1,45 @@ +name: Scheduled code tests trigger + +on: + # Run daily, at 00:00. + schedule: + - cron: '0 0 * * *' + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +permissions: + contents: read + +# Opt into Node.js 24 for all JavaScript actions. +# Remove once all referenced actions natively target Node 24. +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + code-tests-trigger: + runs-on: ubuntu-latest + + steps: + - name: Checkout develop branch + uses: actions/checkout@v5 + with: + ref: develop + + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + + - name: Dispatch code tests workflow + uses: ./.github/actions/github-script + with: + github-token: ${{ steps.bot.outputs.token }} + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: "test.yml", + ref: "develop" + }); diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..2ea7a481 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,305 @@ +# This is the main workflow for testing the code before and after +# packaging it. +# The workflow is divided into three jobs: +# 1. env-prepare: +# - Prepare the environment for testing +# 2. source-test: +# - Test the code base against the latest code in the repository +# - Create the Python package +# - Upload the Python package for the next job +# 3. package-test: +# - Download the Python package (including extra files) from the previous job +# - Install the downloaded Python package +# - Test the code base against the installed package +# 4. dashboard-build-trigger: +# - Trigger the dashboard build workflow to update the code quality +# metrics on the dashboard + +name: Code and package tests + +on: + # Trigger the workflow on push + push: + branches-ignore: [master, main] # Already verified in PR + # But do not run this workflow on creating a new tag starting with + # 'v', e.g. 'v1.0.3' (see publish-pypi.yml) + tags-ignore: ['v*'] + # Trigger the workflow on pull request + pull_request: + branches: ['**'] + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Need permissions to trigger the dashboard build workflow +permissions: + actions: write + contents: read + +# Allow only one concurrent workflow, skipping runs queued between the run +# in-progress and latest queued. And cancel in-progress runs. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +# Set the environment variables to be used in all jobs defined in this workflow +env: + CI_BRANCH: ${{ github.head_ref || github.ref_name }} + PY_VERSIONS: '3.11 3.14' + PIXI_ENVS: 'py-311-env py-314-env' + # Opt into Node.js 24 for all JavaScript actions. + # Remove once all referenced actions natively target Node 24. + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + # Job 1: Set up environment variables + env-prepare: + runs-on: [ubuntu-latest] + + outputs: + pytest-marks: ${{ steps.set-mark.outputs.pytest_marks }} + + steps: + # Determine if integration tests should be run fully or only the fast ones + # (to save time on branches other than master and develop) + - name: Set mark for integration tests + id: set-mark + run: | + if [[ "${{ env.CI_BRANCH }}" == "master" || "${{ env.CI_BRANCH }}" == "develop" ]]; then + echo "pytest_marks=" >> $GITHUB_OUTPUT + else + echo "pytest_marks=-m fast" >> $GITHUB_OUTPUT + fi + + # Job 2: Test code + source-test: + needs: env-prepare # depend on previous job + + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, macos-15, windows-2022] + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + with: + environments: ${{ env.PIXI_ENVS }} + + - name: Run unit tests + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + env="py-$(echo $py_ver | tr -d .)-env" # Converts 3.XX -> py-3XX-env + + echo "Running tests in environment: $env" + pixi run --environment $env unit-tests + done + + - name: Run functional tests + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + env="py-$(echo $py_ver | tr -d .)-env" # Converts 3.XX -> py-3XX-env + + echo "Running tests in environment: $env" + pixi run --environment $env functional-tests + done + + - name: Run integration tests ${{ needs.env-prepare.outputs.pytest-marks }} + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + env="py-$(echo $py_ver | tr -d .)-env" # Converts 3.XX -> py-3XX-env + + echo "Running tests in environment: $env" + pixi run --environment $env integration-tests ${{ needs.env-prepare.outputs.pytest-marks }} + done + + # Delete all local tags when not on a tagged commit to force versioningit + # to fall back to the configured default-tag, which is '999.0.0' in our case. + # This is needed for testing the package in the next job, as its version + # must be higher than the PyPI version for pip to prefer the local version. + - name: Force using versioningit default tag (non tagged release) + if: startsWith(github.ref , 'refs/tags/v') != true + run: git tag --delete $(git tag) + + - name: Build package wheels for all Python versions + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + env="py-$(echo $py_ver | tr -d .)-env" # Converts 3.11 -> py-311-env + + echo "Building wheel in environment: $env" + pixi run --environment $env dist-build + + echo "Moving built wheel to dist/py$py_ver/" + pixi run mkdir -p dist/py$py_ver + pixi run mv dist/*.whl dist/py$py_ver/ + done + + - name: Remove Python cache files before uploading + shell: bash + run: pixi run clean-pycache + + # More than one file/dir need to be specified in 'path', to preserve the + # structure of the dist/ directory, not only its contents. + - name: Upload package (incl. extras) for next job + uses: ./.github/actions/upload-artifact + with: + name: easyapplication_${{ matrix.os }}_${{ runner.arch }} + path: dist/ + + # Job 3: Test the package + package-test: + needs: source-test # depend on previous job + + strategy: + fail-fast: false + matrix: + os: [ubuntu-24.04, macos-15, windows-2022] + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Download package (incl. extras) from previous job + uses: ./.github/actions/download-artifact + with: + # name and path should be taken from the upload step of the previous job + name: easyapplication_${{ matrix.os }}_${{ runner.arch }} + path: dist/ + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + with: + environments: '' + activate-environment: '' + run-install: false + frozen: false + + - name: Install easyapplication from the built wheel + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + echo "Initializing pixi project" + pixi init easyapplication_py$py_ver + cd easyapplication_py$py_ver + + echo "Adding Python $py_ver" + pixi add "python=$py_ver" + + echo "Setting macOS 14.0 as minimum required" + pixi project system-requirements add macos 14.0 + + echo "Looking for wheel in ../dist/py$py_ver/" + ls -l "../dist/py$py_ver/" + + whl_path=(../dist/"py$py_ver"/*.whl) + if [[ ! -f "${whl_path[0]}" ]]; then + echo "❌ No wheel found in ../dist/py$py_ver/" + exit 1 + fi + + whl_url="file://$(python -c 'import os,sys; print(os.path.abspath(sys.argv[1]))' "${whl_path[0]}")" + + echo "Adding easyapplication from: $whl_url" + pixi add --pypi "easyapplication[dev] @ ${whl_url}" + + echo "Exiting pixi project directory" + cd .. + done + + - name: Run unit tests + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + echo "Entering pixi project directory easyapplication_py$py_ver" + cd easyapplication_py$py_ver + + echo "Running tests" + pixi run python -m pytest ../tests/unit/ --color=yes -v + + echo "Exiting pixi project directory" + cd .. + done + + - name: Run functional tests + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + echo "Entering pixi project directory easyapplication_py$py_ver" + cd easyapplication_py$py_ver + + echo "Running tests" + pixi run python -m pytest ../tests/functional/ --color=yes -v + + echo "Exiting pixi project directory" + cd .. + done + + - name: Run integration tests ${{ needs.env-prepare.outputs.pytest-marks }} + shell: bash + run: | + set -euo pipefail + + for py_ver in $PY_VERSIONS; do + echo + echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" + + echo "Entering pixi project directory easyapplication_py$py_ver" + cd easyapplication_py$py_ver + + echo "Running tests" + pixi run python -m pytest ../tests/integration/ --color=yes -n auto -v ${{ needs.env-prepare.outputs.pytest-marks }} + + echo "Exiting pixi project directory" + cd .. + done + + # Job 4: Build and publish dashboard (reusable workflow) + run-reusable-workflows: + needs: package-test # depend on previous job + uses: ./.github/workflows/dashboard.yml + secrets: inherit diff --git a/.github/workflows/tutorial-tests-trigger.yml b/.github/workflows/tutorial-tests-trigger.yml new file mode 100644 index 00000000..f28d6527 --- /dev/null +++ b/.github/workflows/tutorial-tests-trigger.yml @@ -0,0 +1,45 @@ +name: Scheduled tutorial tests trigger + +on: + # Run daily, at 00:00. + schedule: + - cron: '0 0 * * *' + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +permissions: + contents: read + +# Opt into Node.js 24 for all JavaScript actions. +# Remove once all referenced actions natively target Node 24. +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + tutorial-tests-trigger: + runs-on: ubuntu-latest + + steps: + - name: Checkout develop branch + uses: actions/checkout@v5 + with: + ref: develop + + - name: Setup easyscience[bot] + id: bot + uses: ./.github/actions/setup-easyscience-bot + with: + app-id: ${{ vars.EASYSCIENCE_APP_ID }} + private-key: ${{ secrets.EASYSCIENCE_APP_KEY }} + + - name: Dispatch tutorial tests workflow + uses: ./.github/actions/github-script + with: + github-token: ${{ steps.bot.outputs.token }} + script: | + await github.rest.actions.createWorkflowDispatch({ + owner: context.repo.owner, + repo: context.repo.repo, + workflow_id: "tutorial-tests.yml", + ref: "develop" + }); diff --git a/.github/workflows/tutorial-tests.yml b/.github/workflows/tutorial-tests.yml new file mode 100644 index 00000000..66ff6921 --- /dev/null +++ b/.github/workflows/tutorial-tests.yml @@ -0,0 +1,63 @@ +name: Tutorial tests + +on: + # Trigger the workflow on push + push: + # Selected branches + branches: [develop] # master and main are already verified in PR + # Trigger the workflow on pull request + pull_request: + branches: ['**'] + # Trigger the workflow on workflow_call (to be called from other workflows) + # Needed, as standard schedule triggers the master branch only, but we want + # to run this workflow on develop branch. + workflow_call: + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +permissions: + contents: read + +# Allow only one concurrent workflow, skipping runs queued between the run +# in-progress and latest queued. And cancel in-progress runs. +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +# Set the environment variables to be used in all jobs defined in this workflow +env: + CI_BRANCH: ${{ github.head_ref || github.ref_name }} + # Opt into Node.js 24 for all JavaScript actions. + # Remove once all referenced actions natively target Node 24. + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true + +jobs: + # Job 1: Test tutorials as scripts and notebooks on multiple OS + tutorial-tests: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout repository + uses: actions/checkout@v5 + + - name: Set up pixi + uses: ./.github/actions/setup-pixi + + - name: Prepare notebooks + shell: bash + run: pixi run notebook-prepare + + - name: Test tutorials as notebooks + shell: bash + run: pixi run notebook-tests + + # Job 2: Build and publish dashboard (reusable workflow) + run-reusable-workflows: + needs: tutorial-tests # depend on previous job + uses: ./.github/workflows/dashboard.yml + secrets: inherit diff --git a/.gitignore b/.gitignore index cebd2acc..6dc595c7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,46 +1,39 @@ -# Qt Creator -*.autosave +# Python +__pycache__/ +.venv/ +.coverage +.pyc + +# Pixi +.pixi/ + +# PyInstaller +dist/ +build/ +*.spec -# Qt Creator Qml +# MkDocs +docs/site/ + +# Jupyter Notebooks +.ipynb_checkpoints + +# Node +node_modules/ + +# QtCreator +*.autosave *.qmlproject.user *.qmlproject.user.* - -# Qt Creator Python *.pyproject.user *.pyproject.user.* - -# Qt Creator C++ -*.pro.user -*.pro.user.* - -# Qt Creator CMake CMakeLists.txt.user* -# VS Code -.vscode/ - # PyCharm .idea/ -# Fortran -fort.1 - -# Python -__pycache__ -.venv -.coverage -.pyc - -# Poetry -dist -*.egg-info - -# PyInstaller -build -*.spec - -# Jupyter -.ipynb_checkpoints +# VS Code +.vscode/ # macOS .DS_Store @@ -48,9 +41,6 @@ build *.dmg # Misc -..* +.cache/ *.log *.zip -settings.ini* -temp.cif -.ci/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 00000000..9a3855f4 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,61 @@ +repos: + - repo: local + hooks: + # ------------- + # Manual checks + # ------------- + - id: pixi-pyproject-check + name: pixi run pyproject-check + entry: pixi run pyproject-check + language: system + pass_filenames: false + stages: [manual] + + - id: license-headers-check + name: pixi run license-check + entry: pixi run license-check + language: system + pass_filenames: false + stages: [manual] + + - id: pixi-py-lint-check + name: pixi run py-lint-check + entry: pixi run py-lint-check + language: system + pass_filenames: false + stages: [manual] + + - id: pixi-py-format-check + name: pixi run py-format-check + entry: pixi run py-format-check + language: system + pass_filenames: false + stages: [manual] + + - id: pixi-docstring-lint-check + name: pixi run docstring-lint-check + entry: pixi run docstring-lint-check + language: system + pass_filenames: false + stages: [manual] + + - id: pixi-nonpy-format-check + name: pixi run nonpy-format-check + entry: pixi run nonpy-format-check + language: system + pass_filenames: false + stages: [manual] + + - id: pixi-notebook-lint-check + name: pixi run notebook-lint-check + entry: pixi run notebook-lint-check + language: system + pass_filenames: false + stages: [manual] + + - id: pixi-unit-tests + name: pixi run unit-tests + entry: pixi run unit-tests + language: system + pass_filenames: false + stages: [manual] diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..a08c3c48 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,32 @@ +# Git +.git/ + +# Copier +.copier-answers*.yml + +# Pixi +.pixi +pixi.lock + +# MkDocs +docs/overrides/ +docs/site/ +docs/docs/assets/ + +# Python +.pytest_cache/ + +# MyPy +.mypy_cache/ + +# Ruff +.ruff_cache/ + +# Node +node_modules + +# Misc +.benchmarks +.cache +deps/ +tmp/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..fc3e7312 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,445 @@ +# Contributing to EasyApplication + +Thank you for your interest in contributing to **EasyApplication**! + +This guide explains how you can: + +- Report issues +- Contribute code +- Improve documentation +- Suggest enhancements +- Interact with the EasyScience community + +Whether you are an experienced developer or contributing for the first +time, this document walks you through the entire process step by step. + +Please make sure you follow the EasyScience organization-wide +[Code of Conduct](https://github.com/easyscience/.github/blob/master/CODE_OF_CONDUCT.md). + +--- + +## Table of Contents + +- [How to Interact With This Project](#how-to-interact-with-this-project) +- [1. Understanding the Development Model](#1-understanding-the-development-model) +- [2. Getting the Code](#2-getting-the-code) +- [3. Setting Up the Development Environment](#3-setting-up-the-development-environment) +- [4. Creating a Branch](#4-creating-a-branch) +- [5. Implementing Your Changes](#5-implementing-your-changes) +- [6. Code Quality Checks](#6-code-quality-checks) +- [7. Opening a Pull Request](#7-opening-a-pull-request) +- [8. Continuous Integration (CI)](#8-continuous-integration-ci) +- [9. Code Review](#9-code-review) +- [10. Documentation Contributions](#10-documentation-contributions) +- [11. Reporting Issues](#11-reporting-issues) +- [12. Security Issues](#12-security-issues) +- [13. Releases](#13-releases) + +--- + +## How to Interact With This Project + +If you are not planning to contribute code, you may want to: + +- 🐞 Report a bug — see [Reporting Issues](#11-reporting-issues) +- 🛡 Report a security issue — + see [Security Issues](#12-security-issues) +- 💬 Ask a question or start a discussion at + [Project Discussions](https://github.com/easyscience/easyapp/discussions) + +If you plan to contribute code or documentation, continue below. + +--- + +## 1. Understanding the Development Model + +Before you start coding, it is important to understand how development +works in this project. + +### Branching Strategy + +We use the following branches: + +- `master` — stable releases only +- `develop` — active development branch +- Short-lived branches — feature or fix branches created for a single + contribution and deleted after merge + +> [!IMPORTANT] +> +> All normal contributions must target the `develop` branch. +> +> - Do **not** open Pull Requests against `master` +> - Always create your branch from `develop` +> - Always target `develop` when opening a Pull Request + +See ADR easyscience/.github#12 for more details on the branching +strategy. + +--- + +## 2. Getting the Code + +### 2.1. If You Are an External Contributor + +If you are not a core maintainer of this repository, follow these steps. + +1. Open the repository page: `https://github.com/easyscience/easyapp` + +2. Click the **Fork** button (top-right corner). This creates your own + copy of the repository. + +3. Clone your fork locally: + + ```bash + git clone https://github.com//easyapp.git + cd easyapp + ``` + +4. Add the original repository as `upstream`: + + ```bash + git remote add upstream https://github.com/easyscience/easyapp.git + ``` + +5. Switch to the `develop` branch and update it: + + ```bash + git fetch upstream + git checkout develop + git pull upstream develop + ``` + +If you have contributed before, make sure your local `develop` branch is +up to date before starting new work. You can update it with: + +```bash +git fetch upstream +git pull upstream develop +``` + +This ensures you are working on the latest version of the project. + +### 2.2. If You Are a Core Team Member + +Core team members can create branches directly in this repository and +therefore do not need to fork it, but the rest of the workflow remains +the same. + +--- + +## 3. Setting Up the Development Environment + +You need: + +- Git +- Pixi + +EasyScience projects use **Pixi** to manage the development environment. + +To install Pixi, follow the official instructions: +https://pixi.prefix.dev/latest/installation/ + +You do **not** need to manually install Python. Pixi automatically: + +- Creates the correct Python environment +- Installs all required dependencies +- Installs development tools (linters, formatters, test tools) + +Set up the environment: + +```bash +pixi install +pixi run post-install # Install additional tooling +``` + +After this step, your development environment is ready. + +See ADR easyscience/.github#63 for more details about using Pixi for +development. + +--- + +## 4. Creating a Branch + +Never work directly on `develop`. + +Create a new branch: + +```bash +git checkout -b my-change develop +``` + +> [!IMPORTANT] +> +> Use a clear and descriptive name, for example: +> +> - `improve-solver-speed` +> - `fix-boundary-condition` +> - `add-tutorial-example` + +Clear branch names make reviews and history easier to understand. + +--- + +## 5. Implementing Your Changes + +While developing, make small, logical commits with clear messages. + +Example: + +```bash +git add . +git commit -m "Improve performance of time integrator for large systems" +``` + +--- + +## 6. Code Quality Checks + +> [!IMPORTANT] +> +> When adding new functionality or making changes, make sure to add or +> update the following as needed: +> +> - 📘 docstrings +> - 🧪 unit tests + +Before opening a Pull Request, always run: + +```bash +pixi run check +``` + +This command: + +- Validates the pyproject.toml file +- Checks for licence headers in code files +- Identifies linting and formatting issues in Python code +- Checks docstring linting and formatting issues in Python code +- Detects formatting issues in non-Python files (MD, YAML, TOML etc.) +- Checks linting issues in Jupyter notebooks (if applicable) +- Runs unit tests + +A successful run should look like this: + +```bash +pixi run pyproject-check.......................Passed +pixi run license-check.........................Passed +pixi run py-lint-check.........................Passed +pixi run py-format-check.......................Passed +pixi run docstring-lint-check..................Passed +pixi run nonpy-format-check....................Passed +pixi run notebook-lint-check...................Passed +pixi run unit-tests............................Passed +``` + +If something fails, read the error message carefully and fix the issue. + +You can run individual checks, for example, to run only unit tests: + +```bash +pixi run unit-tests +``` + +or to run only Python linting checks: + +```bash +pixi run py-lint-check +``` + +Some formatting issues can be fixed automatically: + +```bash +pixi run fix +``` + +If everything is correctly formatted, you will see: + +```text +✅ All auto-formatting steps completed successfully! +``` + +This indicates that the auto-formatting pipeline completed successfully. +If you do not see this message and no error messages appear, try running +the command again. + +If errors are reported, resolve them and re-run: + +```bash +pixi run check +``` + +> [!IMPORTANT] +> +> All checks must pass before your Pull Request can be merged. + +If you are unsure how to fix an issue, ask for help in your Pull Request +discussion. + +--- + +## 7. Opening a Pull Request + +Push your branch: + +```bash +git push origin my-change +``` + +On GitHub: + +- Click **Compare & Pull Request** +- Ensure the base branch is `develop` +- Write a clear and concise title +- Add a description explaining what changed and why +- Add the required `[scope]` label + +### Pull Request Title + +> [!IMPORTANT] +> +> The PR title appears in release notes and changelogs. It should be +> concise and informative. + +Good examples: + +- Improve performance of time integrator for large systems +- Fix incorrect boundary condition handling in solver +- Add adaptive step-size control to ODE solver +- Add tutorial for custom model configuration +- Refactor solver API for improved readability + +### Required `[scope]` Label + +> [!IMPORTANT] +> +> Each Pull Request must include a `[scope]` label, which is used for +> automatically suggesting version bumps when preparing a new release. + +The available scopes are: + +| Label | Description | +| ----------------------- | ----------------------------------------------------------------------- | +| `[scope] bug` | Bug report or fix (major.minor.**PATCH**) | +| `[scope] documentation` | Documentation-only changes (major.minor.patch.**POST**) | +| `[scope] enhancement` | Adds or improves features (major.**MINOR**.patch) | +| `[scope] maintenance` | Code/tooling cleanup without feature or bug fix (major.minor.**PATCH**) | +| `[scope] significant` | Breaking or major changes (**MAJOR**.minor.patch) | + +See ADR easyscience/.github#33 for more details on the standardized +labeling scheme. + +--- + +## 8. Continuous Integration (CI) + +After opening a Pull Request: + +- Automated checks run automatically +- You will see green checkmarks or red crosses + +If checks fail: + +1. Open the failing check +2. Read the logs +3. Fix the issue locally +4. Run `pixi run check` +5. Push your changes + +The Pull Request updates automatically. + +--- + +## 9. Code Review + +All Pull Requests are reviewed by at least one core team member. + +Code review is collaborative and aims to improve quality. + +Do not take comments personally — they are meant to help. + +To update your PR: + +```bash +git add . +git commit -m "Address review comments" +git push +``` + +--- + +## 10. Documentation Contributions + +> [!IMPORTANT] +> +> If your change affects user-facing functionality, update the project +> documentation accordingly — specifically the `nav:` (navigation) +> structure in `mkdocs.yml` and the relevant documentation Markdown +> files in `docs/docs/`. +> +> ```text +> 📁 docs +> ├── 📁 docs - Markdown files for documentation +> │ └── ... +> └── 📄 mkdocs.yml - Configuration file (navigation, theme, etc.) +> ``` + +This may include: + +- API documentation +- Examples +- Tutorials +- Jupyter notebooks + +Preview documentation locally: + +```bash +pixi run docs-serve +``` + +Open the URL shown in the terminal to review your changes. + +--- + +## 11. Reporting Issues + +If you find a bug but cannot work on a fix, please consider opening an +issue. + +When reporting an issue, it helps to: + +- Search existing issues first. +- Provide clear reproduction steps. +- Include logs, screenshots, and environment details. + +Clear and detailed reports help maintainers investigate and resolve +issues more effectively. + +--- + +## 12. Security Issues + +> [!IMPORTANT] +> +> Please do **not** report security vulnerabilities publicly. + +If you discover a potential vulnerability, please contact the +maintainers privately so the issue can be investigated and addressed +responsibly. + +--- + +## 13. Releases + +Once your contribution is merged into `develop`, it will eventually be +included in the next stable release. + +When enough changes have accumulated in `develop`, core team members +merge `develop` into `master` to prepare a new release. The release is +then tagged and published on GitHub and PyPI. + +--- + +Thank you for contributing to EasyApplication and the EasyScience +ecosystem! diff --git a/LICENSE b/LICENSE index 056a3c1e..c4e3e48e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,7 +1,6 @@ BSD 3-Clause License -Copyright (c) 2024, EasyApp contributors (https://github.com/easyscience/EasyApp). -All rights reserved. +Copyright (c) 2021-2026 EasyScience contributors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/README.md b/README.md index f519aa85..cd6601ba 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,41 @@ - +

+ + + + + + + EasyApplication + +

-EasyApp is a collection of Qt QML graphical components which allows one to quickly create cross-platform applications with an eye-catching and intuitive graphical interface based on the [EasyScience](http://github.com/EasyScience) framework. +**EasyApplication** is a collection of Qt/QML graphical components to create cross-platform applications with intuitive graphical interface based on the EasyScience framework. -* Website: https://app.easyscience.software -* Source code: https://github.com/EasyScience/EasyApp -* Bug reports: https://github.com/EasyScience/EasyApp/issues + -EasyApp is currently being used as the basis for the graphical interface in the following projects: -* [EasyDiffraction](http://github.com/EasyScience/EasyDiffraction) -* [EasyReflectometry](http://github.com/EasyScience/EasyReflectometry) -* [EasyTexture](http://github.com/EasyScience/EasyTextureApp) +**EasyApplication** is developed as a Python library. -More simple examples of how to use EasyApp are described in [EXAMPLES.md](https://github.com/EasyScience/EasyApp/blob/master/EXAMPLES.md) and presented in the project [examples](https://github.com/EasyScience/EasyApp/tree/master/examples) directory: -* [BasicQml](https://github.com/EasyScience/EasyApp/tree/master/examples/BasicQml) -* [BasicPy](https://github.com/EasyScience/EasyApp/tree/master/examples/BasicPy) -* [BasicC++](https://github.com/EasyScience/EasyApp/tree/master/examples/BasicC++) -* [IntermediatePy](https://github.com/EasyScience/EasyApp/tree/master/examples/IntermediatePy) -* [AdvancedPy](https://github.com/EasyScience/EasyApp/tree/master/examples/AdvancedPy) -If you want to see what EasyApp looks like, you can try the web demo based on the [BasicC++](https://github.com/EasyScience/EasyApp/tree/master/examples/BasicC++) example at https://easyscience.github.io/EasyApp/BasicC++.html. +License: [BSD 3-Clause](https://github.com/easyscience/easyapp/blob/master/LICENSE) + +## Useful Links + +### For Users + +- 📖 [Documentation](https://easyscience.github.io/easyapp/latest) +- 🚀 [Getting Started](https://easyscience.github.io/easyapp/latest/introduction) +- 🧪 [Tutorials](https://easyscience.github.io/easyapp/latest/tutorials) +- 💬 [Get in Touch](https://easyscience.github.io/easyapp/latest/introduction/#get-in-touch) +- 🧾 [Citation](https://easyscience.github.io/easyapp/latest/introduction/#citation) + +### For Contributors + +- 🧑‍💻 [Source Code](https://github.com/easyscience/easyapp) +- 🐞 [Issue Tracker](https://github.com/easyscience/easyapp/issues) +- 💡 [Discussions](https://github.com/easyscience/easyapp/discussions) +- 🤝 [Contributing Guide](https://github.com/easyscience/easyapp/blob/master/CONTRIBUTING.md) +- 🛡 [Code of Conduct](https://github.com/easyscience/.github/blob/master/CODE_OF_CONDUCT.md) + + diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 00000000..f62b13ab --- /dev/null +++ b/codecov.yml @@ -0,0 +1,13 @@ +# Codecov configuration +# https://docs.codecov.com/docs/codecovyml-reference + +coverage: + status: + project: + default: + # Make project coverage informational (won't block PR) + informational: true + patch: + default: + # Require patch coverage but with threshold + threshold: 1% diff --git a/docs/docs/api-reference/index.md b/docs/docs/api-reference/index.md new file mode 100644 index 00000000..87933712 --- /dev/null +++ b/docs/docs/api-reference/index.md @@ -0,0 +1,8 @@ +--- +icon: material/code-braces-box +--- + +# :material-code-braces-box: API Reference + +This section contains the reference detailing the functions and modules +available in EasyApplication. diff --git a/docs/docs/assets/javascripts/extra.js b/docs/docs/assets/javascripts/extra.js new file mode 100644 index 00000000..9596ee07 --- /dev/null +++ b/docs/docs/assets/javascripts/extra.js @@ -0,0 +1,27 @@ +;(function () { + 'use strict' + + // Variables + const header = document.getElementsByTagName('header')[0] + + console.log(window.pageYOffset) + + // Hide-show header shadow + function toggleHeaderShadow() { + if (window.pageYOffset <= 0) { + header.classList.remove('md-header--shadow') + } else { + header.classList.add('md-header--shadow') + } + } + + // Onload + window.onload = function () { + toggleHeaderShadow() + } + + // Onscroll + window.onscroll = function () { + toggleHeaderShadow() + } +})() diff --git a/docs/docs/assets/javascripts/mathjax.js b/docs/docs/assets/javascripts/mathjax.js new file mode 100644 index 00000000..333524ec --- /dev/null +++ b/docs/docs/assets/javascripts/mathjax.js @@ -0,0 +1,33 @@ +window.MathJax = { + tex: { + //inlineMath: [['\\(', '\\)']], + //displayMath: [['\\[', '\\]']], + // Add support for $...$ and \(...\) delimiters + inlineMath: [ + ['$', '$'], + ['\\(', '\\)'], + ], + // Add support for $$...$$ and \[...]\ delimiters + displayMath: [ + ['$$', '$$'], + ['\\[', '\\]'], + ], + processEscapes: true, + processEnvironments: true, + }, + options: { + //ignoreHtmlClass: ".*|", + //processHtmlClass: "arithmatex" + // Skip code blocks only + skipHtmlTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code'], + // Only ignore explicit opt-out + ignoreHtmlClass: 'no-mathjax|tex2jax_ignore', + }, +} + +document$.subscribe(() => { + MathJax.startup.output.clearCache() + MathJax.typesetClear() + MathJax.texReset() + MathJax.typesetPromise() +}) diff --git a/docs/docs/assets/stylesheets/extra.css b/docs/docs/assets/stylesheets/extra.css new file mode 100644 index 00000000..a625be80 --- /dev/null +++ b/docs/docs/assets/stylesheets/extra.css @@ -0,0 +1,359 @@ +/*************/ +/* Variables */ +/*************/ + +:root { + --sz-link-color--lightmode: #0184c7; + --sz-hovered-link-color--lightmode: #37bdf9; + --sz-body-background-color--lightmode: #fafafa; + --sz-body-text-color--lightmode: #525252; + --sz-body-heading-color--lightmode: #434343; + --sz-code-background-color--lightmode: #ececec; + + --sz-link-color--darkmode: #37bdf9; + --sz-hovered-link-color--darkmode: #2890c0; + --sz-body-background-color--darkmode: #262626; + --sz-body-text-color--darkmode: #a3a3a3; + --sz-body-heading-color--darkmode: #e5e5e5; + --sz-code-background-color--darkmode: #212121; +} + +/****************/ +/* Color styles */ +/****************/ + +/* Default styles https://github.com/squidfunk/mkdocs-material/blob/master/src/assets/stylesheets/main/_typeset.scss */ + +/* Light mode */ + +/* Default light mode https://github.com/squidfunk/mkdocs-material/blob/master/src/assets/stylesheets/main/_colors.scss */ + +[data-md-color-scheme="default"] { + + /* Primary color shades */ + --md-primary-fg-color: var(--sz-body-background-color--lightmode); /* Navigation background */ + --md-primary-bg-color: var(--sz-body-text-color--lightmode); /* E.g., Header title and icons */ + --md-primary-bg-color--light: var(--sz-body-text-color--lightmode); /* E.g., Header search */ + + /* Accent color shades */ + --md-accent-fg-color: var(--sz-hovered-link-color--lightmode); /* E.g., Hovered `a` elements & copy icon in code */ + + /* Default color shades */ + --md-default-fg-color: var(--sz-body-text-color--lightmode); + --md-default-fg-color--light: var(--sz-body-heading-color--lightmode); /* E.g., `h1` color & TOC viewed items & `$` in code */ + --md-default-fg-color--lighter: var(--sz-body-text-color--lightmode); /* E.g., `¶` sign near `h1-h8` */ + --md-default-fg-color--lightest: var(--sz-body-text-color--lightmode); /* E.g., Copy icon in code */ + --md-default-bg-color: var(--sz-body-background-color--lightmode); + + /* Code color shades */ + --md-code-bg-color: var(--sz-code-background-color--lightmode); + + /* Typeset color shades */ + --md-typeset-color: var(--sz-body-text-color--lightmode); + + /* Typeset `a` color shades */ + --md-typeset-a-color: var(--sz-link-color--lightmode); + + /* Footer color shades */ + --md-footer-fg-color: var(--sz-body-text-color--lightmode); /* E.g., Next -> */ + --md-footer-fg-color--light: var(--sz-body-text-color--lightmode); /* E.g., © 2022 EasyDiffraction, Material for MkDocs */ + --md-footer-fg-color--lighter: var(--sz-body-text-color--lightmode); /* E.g. Made with */ + --md-footer-bg-color: hsla(0, 0%, 0%, 0.0); /* Space with, e.g., Next -> */ + --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.0); /* Space with, e.g., © 2022 EasyDiffraction */ + + /* Custom colors */ + --sz-red-color: #F44336; + --sz-blue-color: #03A9F4; + --sz-green-color: #4CAF50; + --sz-orange-color: #FF9800; + + /* Logo display */ + --md-footer-logo-dark-mode: none; + --md-footer-logo-light-mode: block; +} + +/* Dark mode */ + +/* Default dark mode: https://github.com/squidfunk/mkdocs-material/blob/master/src/assets/stylesheets/palette/_scheme.scss */ + +[data-md-color-scheme="slate"] { + + /* Primary color shades */ + --md-primary-fg-color: var(--sz-body-background-color--darkmode); /* Navigation background */ + --md-primary-bg-color: var(--sz-body-text-color--darkmode); /* E.g., Header title and icons */ + --md-primary-bg-color--light: var(--sz-body-text-color--darkmode); /* E.g., Header search */ + + /* Accent color shades */ + --md-accent-fg-color: var(--sz-hovered-link-color--darkmode); /* E.g., Hovered `a` elements & copy icon in code */ + + /* Default color shades */ + --md-default-fg-color: var(--sz-body-text-color--darkmode); + --md-default-fg-color--light: var(--sz-body-heading-color--darkmode); /* E.g., `h1` color & TOC viewed items & `$` in code */ + --md-default-fg-color--lighter: var(--sz-body-text-color--darkmode); /* E.g., `¶` sign near `h1-h8` */ + --md-default-fg-color--lightest: var(--sz-body-text-color--darkmode); /* E.g., Copy icon in code */ + --md-default-bg-color: var(--sz-body-background-color--darkmode); + + /* Code color shades */ + --md-code-bg-color: var(--sz-code-background-color--darkmode); + + /* Typeset color shades */ + --md-typeset-color: var(--sz-body-text-color--darkmode); + + /* Typeset `a` color shades */ + --md-typeset-a-color: var(--sz-link-color--darkmode); + + /* Footer color shades */ + --md-footer-fg-color: var(--sz-body-text-color--darkmode); /* E.g., Next -> */ + --md-footer-fg-color--light: var(--sz-body-text-color--darkmode); /* E.g., © 2022 EasyDiffraction, Material for MkDocs */ + --md-footer-fg-color--lighter: var(--sz-body-text-color--darkmode); /* E.g. Made with */ + --md-footer-bg-color: hsla(0, 0%, 0%, 0.0); /* Space with, e.g., Next -> */ + --md-footer-bg-color--dark: hsla(0, 0%, 0%, 0.0); /* Space with, e.g., © 2022 EasyDiffraction */ + + /* Custom colors */ + --sz-red-color: #EF9A9A; + --sz-blue-color: #81D4FA; + --sz-green-color: #A5D6A7; + --sz-orange-color: #FFCC80; + + /* Logo display */ + --md-footer-logo-dark-mode: block; + --md-footer-logo-light-mode: none; +} + +/*****************/ +/* Custom styles */ +/*****************/ + +/* Logo */ + +#logo_light_mode { + display: var(--md-footer-logo-light-mode); +} + +#logo_dark_mode { + display: var(--md-footer-logo-dark-mode); +} + +/* Customize default styles of MkDocs Material */ + +/* Hide navigation title */ +label.md-nav__title[for="__drawer"] { + height: 0; +} + +/* Hide site title (first topic) while keeping page title and version selector */ +.md-header__topic:first-child .md-ellipsis { + display: none; +} + +/* Increase logo size */ +.md-logo :is(img, svg) { + height: 1.8rem !important; +} + +/* Hide GH repo with counts (top right page corner) */ +.md-header__source { + display: none; +} + +/* Hide GH repo with counts (navigation bar in mobile view) */ +.md-nav__source { + display: none; +} + +/* Ensure all horizontal lines in the navigation list are removed or hidden */ +.md-nav__item { + /* Removes any border starting from the second level */ + border: none !important; + /* Modifies the background color to hide the first horizontal line */ + background-color: var(--md-default-bg-color); +} + +/* Increase TOC (on the right) width */ +.md-nav--secondary { + margin-left: -10px; + margin-right: -4px; +} + +/* */ +.md-nav__item > .md-nav__link { + padding-left: 0.5em; /* Default */ +} + +/* Change line height of the tabel cells */ +.md-typeset td, +.md-typeset th { + line-height: 1.25 !important; +} + +/* Change vertical alignment of the icon inside the tabel cells */ +.md-typeset td .twemoji { + vertical-align: sub !important; +} + +/* Change the width of the primary sidebar */ +/* +.md-sidebar--primary { + width: 240px; +} +*/ + +/* Change the overall width of the page */ +.md-grid { + max-width: 1280px; +} + +/* Needed for mkdocs-jupyter to show download and other buttons on top of the notebook */ +.md-content__button { + position: relative !important; +} + +/* Background color of the search input field */ +.md-search__input { + background-color: var(--md-code-bg-color) !important; +} + +/* Customize default style of mkdocs-jupyter plugin */ + +/* Set the width of the notebook to fill 100% and not reduce by the width of .md-content__button's +Adjust the margins and paddings to fit the defaults in MkDocs Material and do not crop the label in the header +*/ +.jupyter-wrapper { + width: 100% !important; + display: flex !important; +} + +.jp-Notebook { + padding: 0 !important; + margin-top: -3em !important; + + /* Ensure notebook content stretches across the page */ + width: 100% !important; + max-width: 100% !important; + + /* mkdocs-material + some notebook HTML end up as flex */ + align-items: stretch !important; +} + +.jp-Notebook .jp-Cell { + /* Key: flex children often need min-width: 0 to prevent weird shrink */ + width: 100% !important; + max-width: 100% !important; + min-width: 0 !important; + + /* Removes jupyter cell paddings */ + padding-left: 0 !important; +} + +/* Removes jupyter cell prefixes, like In[123]: */ +.prompt, +.jp-InputPrompt, +.jp-OutputPrompt { + display: none !important; +} + +/* Removes jupyter output cell padding to align with input cell text */ +.jp-RenderedText { + padding-left: 0.85em !important; +} + +/* Extra styling the panda dataframes, on top of the style included in the code */ +table.dataframe { + float: left; + margin-left: 0.75em !important; + margin-bottom: 0.5em !important; + font-size: var(--jp-code-font-size) !important; + color: var(--md-primary-bg-color) !important; + /* Allow table cell wrapping in MkDocs-Jupyter outputs */ + /* + table-layout: auto !important; + width: auto !important; + */ +} + +/* Allow wrap for the last column */ +/* +table.dataframe td:last-child, +table.dataframe th:last-child { + white-space: normal !important; + word-break: break-word !important; +} +*/ + +/* Custom styles for the CIF files */ + +.cif { + padding-left: 1em; + padding-right: 1em; + padding-top: 1px; + padding-bottom: 1px; + background-color: var(--md-code-bg-color); + font-size: small; +} +.red { + color: var(--sz-red-color); +} +.green { + color: var(--sz-green-color); +} +.blue { + color: var(--sz-blue-color); +} +.orange { + color: var(--sz-orange-color); +} +.grey { + color: grey; +} + +/**********/ +/* Labels */ +/**********/ + +.label-cif { + padding-top: 0.5ex; + padding-bottom: 0.5ex; + padding-left: 0.9ex; + padding-right: 0.9ex; + border-radius: 1ex; + color: var(--md-default-fg-color) !important; + background-color: var(--md-code-bg-color); +} + +p .label-cif, li .label-cif { + vertical-align: 5%; + font-size: 12px; +} + +.label-cif:hover { + color: white !important; +} + +.label-experiment { + padding-top: 0.25ex; + padding-bottom: 0.6ex; + padding-left: 0.9ex; + padding-right: 0.9ex; + border-radius: 1ex; + color: var(--md-default-fg-color) !important; + background-color: rgba(55, 189, 249, 0.1); +} + +p .label-experiment, li .label-experiment { + vertical-align: 5%; + font-size: 12px; +} + +h1 .label-experiment { + padding-top: 0.05ex; + padding-bottom: 0.4ex; + padding-left: 0.9ex; + padding-right: 0.9ex; + border-radius: 0.75ex; + color: var(--md-default-fg-color) !important; + background-color: rgba(55, 189, 249, 0.1); +} + +.label-experiment:hover { + color: white !important; +} diff --git a/docs/docs/index.md b/docs/docs/index.md new file mode 100644 index 00000000..03176c8c --- /dev/null +++ b/docs/docs/index.md @@ -0,0 +1,20 @@ +![](assets/images/logo_dark.svg#gh-dark-mode-only)![](assets/images/logo_light.svg#gh-light-mode-only) + +# Qt/QML components for building graphical applications + +Here is a brief overview of the main documentation sections: + +- [:material-information-slab-circle: Introduction](introduction/index.md) – + Provides a description of EasyApplication, including its purpose, licensing, + latest release details, and contact information. +- [:material-cog-box: Installation & Setup](installation-and-setup/index.md) – + Guides users through system requirements, environment configuration, and the + installation process. +- [:material-book-open-variant: User Guide](user-guide/index.md) – Covers core + concepts, key terminology, workflow steps, and essential parameters for + effective use of EasyApplication. +- [:material-school: Tutorials](tutorials/index.md) – Offers practical, + step-by-step examples demonstrating common workflows and data analysis tasks. +- [:material-code-braces-box: API Reference](api-reference/index.md) – An + auto-generated reference detailing the available functions and modules in + EasyApplication. diff --git a/docs/docs/installation-and-setup/index.md b/docs/docs/installation-and-setup/index.md new file mode 100644 index 00000000..23719f0a --- /dev/null +++ b/docs/docs/installation-and-setup/index.md @@ -0,0 +1,281 @@ +--- +icon: material/cog-box +--- + +# :material-cog-box: Installation & Setup + +**EasyApplication** is a cross-platform Python library compatible with +**Python 3.11** through **3.14**. + +To install and set up EasyApplication, we recommend using +[**Pixi**](https://pixi.prefix.dev), a modern package manager for Windows, +macOS, and Linux. + +??? note "Main benefits of using Pixi" + + - **Ease of use**: Pixi simplifies the installation process, making it + accessible even for users with limited experience in package management. + - **Python version control**: Pixi allows specifying and managing different + Python versions for each project, ensuring compatibility. + - **Isolated environments**: Pixi creates isolated environments for each + project, preventing conflicts between different package versions. + - **PyPI and Conda support**: Pixi can install packages from both PyPI and + Conda repositories, providing access to a wide range of libraries. + +An alternative installation method using the traditional **pip** package +manager is also provided. + +## Installing with Pixi recommended { #installing-with-pixi data-toc-label="Installing with Pixi" } + +This section describes the simplest way to set up EasyApplication using +**Pixi**. + +#### Installing Pixi + +- Install Pixi by following the instructions on the + [official Pixi Installation Guide](https://pixi.prefix.dev/latest/installation). + +#### Setting up EasyApplication with Pixi + + + +- Choose a project location (local drive recommended). + + ??? warning ":fontawesome-brands-windows: Windows + OneDrive" + + We **do not recommend creating a Pixi project inside OneDrive or other + synced folders**. + + By default, Pixi creates the virtual environment inside the project + directory (in `.pixi/`). On Windows, synced folders such as OneDrive + may cause file‑system issues (e.g., path-length limitations or + restricted link operations), which can lead to unexpected install + errors or environments being recreated. + + Instead, create your project in a **local directory on your drive** + where you have full write permissions. + + + +- Initialize a new Pixi project and navigate into it: + ```txt + pixi init easyapplication + cd easyapplication + ``` +- Set the Python version for the Pixi environment (e.g., 3.14): + ```txt + pixi add python=3.14 + ``` +- Add EasyApplication to the Pixi environment from PyPI: + ```txt + pixi add --pypi easyapplication + ``` +- Add a Pixi task to run EasyApplication commands easily: + ```txt + pixi task add easyapplication "python -m easyapplication" + ``` + +#### Updating Pixi and EasyApplication + +- To update all packages in the Pixi environment, including EasyApplication: + ```txt + pixi update + ``` +- To update Pixi itself to the latest version: + ```txt + pixi self-update + ``` + +#### Uninstalling Pixi + +- Follow the + [official Pixi Guide](https://pixi.prefix.dev/latest/installation/#uninstall). + +## Classical Installation + +This section describes how to install EasyApplication using the traditional +method with **pip**. It is assumed that you are familiar with Python package +management and virtual environments. + +### Environment Setup optional { #environment-setup data-toc-label="Environment Setup" } + +We recommend using a **virtual environment** to isolate dependencies and +avoid conflicts with system-wide packages. If any issues arise, you can +simply delete and recreate the environment. + +#### Creating and Activating a Virtual Environment: + + + +- Create a new virtual environment: + ```txt + python3 -m venv venv + ``` +- Activate the environment: + + === ":material-apple: macOS" + ```txt + . venv/bin/activate + ``` + === ":material-linux: Linux" + ```txt + . venv/bin/activate + ``` + === ":fontawesome-brands-windows: Windows" + ```txt + . venv/Scripts/activate # Windows with Unix-like shells + .\venv\Scripts\activate.bat # Windows with CMD + .\venv\Scripts\activate.ps1 # Windows with PowerShell + ``` + +- The terminal should now show `(venv)`, indicating that the virtual environment + is active. + + + +#### Deactivating and Removing the Virtual Environment: + + + +- Exit the environment: + ```txt + deactivate + ``` +- If this environment is no longer needed, delete it: + + === ":material-apple: macOS" + ```txt + rm -rf venv + ``` + === ":material-linux: Linux" + ```txt + rm -rf venv + ``` + === ":fontawesome-brands-windows: Windows" + ```txt + rmdir /s /q venv + ``` + + + +### Installing from PyPI { #from-pypi } + +EasyApplication is available on **PyPI (Python Package Index)** and can be +installed using `pip`. To do so, use the following command: + +```txt +pip install easyapplication +``` + +To install a specific version of EasyApplication, e.g., 1.0.3: + +```txt +pip install 'easyapplication==1.0.3' +``` + +To upgrade to the latest version: + +```txt +pip install --upgrade easyapplication +``` + +To upgrade to the latest version and force reinstallation of all +dependencies (useful if files are corrupted): + +```txt +pip install --upgrade --force-reinstall easyapplication +``` + +To check the installed version: + +```txt +pip show easyapplication +``` + +### Installing from GitHub alternative { #from-github data-toc-label="Installing from GitHub" } + +Installing unreleased versions is generally not recommended but may be +useful for testing. + +To install EasyApplication from the `develop` branch of GitHub, for example: + +```txt +pip install git+https://github.com/easyscience/easyapp@develop +``` + +To include extra dependencies (e.g., dev): + +```txt +pip install 'easyapplication[dev] @ git+https://github.com/easyscience/easyapp@develop' +``` + +## How to Run Tutorials + +EasyApplication includes a collection of **Jupyter Notebook examples** that +demonstrate key functionality. These tutorials serve as **step-by-step +guides** to help users understand the data analysis workflow. They are +available as **static HTML pages** in the +[:material-school: Tutorials](../tutorials/index.md) section. + +In the next sections, we explain how to set up Jupyter and run the +tutorials interactively in two different ways: locally or online via +Google Colab. + +If you decide to run the tutorials locally, you need to download them +first. This can be done individually via the :material-download: +**Download Notebook** button available on each tutorial page, or all at +once using the command line, as shown below. + +### Run Tutorials Locally with Pixi recommended { #running-with-pixi data-toc-label="Run Tutorials Locally with Pixi" } + +- Navigate to your existing Pixi project, created as described in the + [Installing with Pixi](#installing-with-pixi) section. +- Add JupyterLab and the Pixi kernel for Jupyter: + ```txt + pixi add --pypi jupyterlab pixi-kernel + ``` +- Download all the EasyApplication tutorials to the `tutorials/` directory: + ```txt + pixi run easyapplication download-all-tutorials + ``` +- Start JupyterLab in the `tutorials/` directory to access the + notebooks: + ```txt + pixi run jupyter lab tutorials/ + ``` +- Your web browser should open automatically. Click on one of the + `*.ipynb` files and select the `Python (Pixi)` kernel to get started. + +### Classical Run Tutorials Locally + +- Install Jupyter Notebook and IPython kernel: + ```txt + pip install notebook ipykernel + ``` +- Add the virtual environment as a Jupyter kernel: + ```txt + python -m ipykernel install --user --name=venv --display-name "EasyApplication Python kernel" + ``` +- Download all the EasyApplication tutorials to the `tutorials/` directory: + ```txt + python -m easyapplication download-all-tutorials + ``` +- Launch the Jupyter Notebook server (opens browser automatically at + `http://localhost:8888/`): + ```txt + jupyter notebook tutorials/ + ``` +- Open one of the `*.ipynb` files and select the + `EasyApplication Python kernel` to get started. + +### Run Tutorials via Google Colab + +**Google Colab** lets you run Jupyter Notebooks in the cloud without any +local installation. This is the fastest way to start experimenting with +EasyApplication. + +- Ensure you have a **Google account**. +- Go to the **[:material-school: Tutorials](../tutorials/index.md)** + section. +- Click the :google-colab: **Open in Google Colab** button on any + tutorial. diff --git a/docs/docs/introduction/index.md b/docs/docs/introduction/index.md new file mode 100644 index 00000000..ab5be286 --- /dev/null +++ b/docs/docs/introduction/index.md @@ -0,0 +1,68 @@ +--- +icon: material/information-slab-circle +--- + +# :material-information-slab-circle: Introduction + +## Description + +**EasyApplication** is a collection of Qt/QML graphical components to create cross-platform applications with intuitive graphical interface based on the EasyScience framework. + + +**EasyApplication** is developed as a Python library. + + + + + +## License + +**EasyApplication** library is released under the +[BSD 3-Clause License](https://raw.githubusercontent.com/easyscience/easyapp/master/LICENSE). + +## Releases + +The latest version of the **EasyApplication** library is +[{{ vars.release_version }}](https://github.com/easyscience/easyapp/releases/latest). + +For a complete list of new features, bug fixes, and improvements, see +the +[GitHub Releases page](https://github.com/easyscience/easyapp/releases). + +## Citation + +If you use **EasyApplication** library in your work, +please cite the specific version you used. + +All official releases of the **EasyApplication** library are archived +on Zenodo, each with a version-specific Digital Object Identifier (DOI). + +Citation details in various styles (e.g., APA, MLA) and formats (e.g., +BibTeX, JSON) are available on the +[Zenodo archive page](https://doi.org/10.5281/zenodo.18163581). + +## Contributing + +We welcome contributions of any kind! + +**EasyApplication** is intended to be a community-driven, open-source +project supported by a diverse group of contributors. + +The project is maintained by the +[European Spallation Source (ESS)](https://ess.eu). + +If you would like to report a bug or request a new feature, please use +the [GitHub Issue Tracker](https://github.com/easyscience/easyapp/issues) +(A free GitHub account is required.) + +To contribute code, documentation, or tests, please see our +[:material-account-plus: Contributing Guidelines](https://github.com/easyscience/easyapp/blob/master/CONTRIBUTING.md) +for detailed development instructions. + +## Get in Touch + +For general questions or feedback, please contact us at +[support@easyscience.org](mailto:support@easyscience.org). diff --git a/docs/docs/tutorials/index.md b/docs/docs/tutorials/index.md new file mode 100644 index 00000000..a57bbbf6 --- /dev/null +++ b/docs/docs/tutorials/index.md @@ -0,0 +1,21 @@ +--- +icon: material/school +--- + +# :material-school: Tutorials + +This section presents a collection of **Jupyter Notebook** tutorials +that demonstrate how to use EasyApplication for various tasks. These +tutorials serve as self-contained, step-by-step **guides** to help users +grasp the workflow of data analysis using EasyApplication. + +Instructions on how to run the tutorials are provided in the +[:material-cog-box: Installation & Setup](../installation-and-setup/index.md#how-to-run-tutorials) +section of the documentation. + +The tutorials are organized into the following categories: + +## Getting Started + +- [Dummy tutorial](tutorial.ipynb) – A dummy tutorial to ensure the + tutorial infrastructure is working correctly. diff --git a/docs/docs/tutorials/tutorial.py b/docs/docs/tutorials/tutorial.py new file mode 100644 index 00000000..5d88f6b4 --- /dev/null +++ b/docs/docs/tutorials/tutorial.py @@ -0,0 +1,20 @@ +# %% [markdown] +# # Dummy Tutorial +# +# This is a dummy tutorial file to ensure that the tutorials section is +# not empty. You can replace this file with actual tutorial content as +# needed. + +# %% [markdown] +# ## Import Library + +# %% +import easyapplication + +# %% [markdown] +# ## Step 1: Blah Blah Blah + +# %% +# This is a placeholder for tutorial content. +print('This is a dummy tutorial.') +print('Imported library:', easyapplication) diff --git a/docs/docs/user-guide/index.md b/docs/docs/user-guide/index.md new file mode 100644 index 00000000..3ea00516 --- /dev/null +++ b/docs/docs/user-guide/index.md @@ -0,0 +1,24 @@ +--- +icon: material/book-open-variant +--- + +# :material-book-open-variant: User Guide + +This section provides an overview of the **core concepts**, +**key parameters** and **workflow steps** required for using EasyApplication +effectively. + +Here is a brief overview of the User Guide sections: + +- [Glossary](#) – Defines common terms and labels used throughout the + documentation. +- [Concept](#) – Introduces the overall idea behind data analysis in + EasyApplication. +- [Data Format](#) – Explains the data structures and file formats used + by EasyApplication. +- [Parameters](#) – Describes how parameters are structured, named, and + accessed within the EasyApplication. +- [First Steps](#) – Shows how to begin using EasyApplication in Python + or Jupyter notebooks. +- [Analysis Workflow](#) – Breaks down the data analysis pipeline into + practical, sequential steps. diff --git a/docs/includes/abbreviations.md b/docs/includes/abbreviations.md new file mode 100644 index 00000000..682f16ff --- /dev/null +++ b/docs/includes/abbreviations.md @@ -0,0 +1,15 @@ + + +*[CIF]: Crystallographic Information File. +*[curl]: Command-line tool for transferring data with URLs. +*[GitHub]: A web-based platform for version control and collaboration. +*[Google Colab]: Cloud service that allows you to run Jupyter Notebooks in the cloud. +*[IUCr]: International Union of Crystallography. +*[Jupyter Notebook]: An open-source web application that allows you to create and share documents that contain live code, equations, visualizations, and narrative text. +*[JupyterLab]: Web-based interactive development environment for notebooks, code, and data. +*[pip]: Package installer for Python. +*[PyPI]: The Python Package Index is a repository of software for the Python programming language. +*[Conda]: Conda is a cross-platform, language-agnostic binary package manager. +*[Pixi]: A modern package manager for Windows, macOS, and Linux. + + diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml new file mode 100644 index 00000000..06cd27d2 --- /dev/null +++ b/docs/mkdocs.yml @@ -0,0 +1,180 @@ +# Project information +site_name: EasyApplication Library +site_url: https://easyscience.github.io/easyapp + +# Repository +repo_url: https://github.com/easyscience/easyapp +edit_uri: edit/develop/docs/ + +# Copyright +copyright: © 2021-2026 EasyApplication + +# Sets the theme and theme-specific configuration +theme: + name: material + custom_dir: overrides + features: + #- content.action.edit # Temporary disable edit button (until decided on which branch to use and where to host the notebooks) + #- content.action.view + - content.code.annotate + - content.code.copy # Auto generated button to copy a code block's content + - content.tooltips + - navigation.footer + - navigation.indexes + #- navigation.instant # Instant loading, but it causes issues with rendering equations + #- navigation.sections + - navigation.top # Back-to-top button + - navigation.tracking # Anchor tracking + - search.highlight + - search.share + - search.suggest + - toc.follow + palette: + # Palette toggle for light mode + - media: '(prefers-color-scheme: light)' + scheme: default + primary: custom + toggle: + icon: fontawesome/solid/sun + name: Switch to dark mode + # Palette toggle for dark mode + - media: '(prefers-color-scheme: dark)' + scheme: slate + primary: custom + toggle: + icon: fontawesome/solid/moon + name: Switch to light mode + font: + text: Mulish + code: Roboto Mono + icon: + edit: material/file-edit-outline + favicon: assets/images/favicon.png + logo_dark_mode: assets/images/logo_dark.svg + logo_light_mode: assets/images/logo_light.svg + +# A set of key-value pairs, where the values can be any valid YAML +# construct, that will be passed to the template +extra: + generator: false # Disable `Made with Material for MkDocs` (bottom left) + social: # Extra icons in the bottom right corner + - icon: easyscience # File: overrides/.icons/easyscience.svg + link: https://easyscience.org + name: EasyScience Framework Webpage + - icon: easyapplication # File: overrides/.icons/easyapplication.svg + link: https://easyscience.github.io/application + name: EasyApplication Main Webpage + - icon: fontawesome/brands/github # Name as in Font Awesome + link: https://github.com/easyscience/easyapp + name: EasyApplication Library Source Code on GitHub + # Set custom variables to be used in Markdown and HTML files + vars: + ci_branch: !ENV CI_BRANCH + github_repository: !ENV GITHUB_REPOSITORY + release_version: !ENV RELEASE_VERSION + docs_version: !ENV DOCS_VERSION + notebooks_dir: !ENV NOTEBOOKS_DIR + # Renders a version selector in the header + version: + provider: mike + +# Customization to be included by the theme +extra_css: + - assets/stylesheets/extra.css + +extra_javascript: + - assets/javascripts/extra.js + # MathJax for rendering mathematical expressions + - assets/javascripts/mathjax.js # Custom MathJax config to ensure compatibility with mkdocs-jupyter + - https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js # Official MathJax CDN + +# A list of extensions beyond the ones that MkDocs uses by default (meta, toc, tables, and fenced_code) +markdown_extensions: + - abbr + - admonition + - attr_list + - def_list + - footnotes + - pymdownx.arithmatex: # rendering of equations and integrates with MathJax or KaTeX + generic: true + - pymdownx.blocks.caption + - pymdownx.details + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + options: + custom_icons: + - docs/overrides/.icons + - pymdownx.highlight: # whether highlighting should be carried out during build time by Pygments + use_pygments: true + pygments_lang_class: true + - pymdownx.snippets: + auto_append: + - docs/includes/abbreviations.md + - pymdownx.superfences: # whether highlighting should be carried out during build time by Pygments + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: # enables content tabs + alternate_style: true + - pymdownx.tasklist: + custom_checkbox: true + - toc: + toc_depth: 3 + +# A list of plugins (with optional configuration settings) to use when building the site +plugins: + - autorefs + - inline-svg + - markdownextradata # Plugin that injects the mkdocs.yml extra variables into the Markdown template + - mike # Plugin that makes it easy to deploy multiple versions of the docs + - mkdocs-jupyter: + include: ['*.ipynb'] # Default: ['*.py', '*.ipynb'] + execute: false # Do not execute notebooks during build. They are expected to be pre-executed. + allow_errors: false + include_source: true + include_requirejs: true # Required for Plotly + #custom_mathjax_url: 'https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js' # See 'extra_javascript' above + ignore_h1_titles: true # Use titles defined in the nav section below + remove_tag_config: + remove_input_tags: + - hide-in-docs + - mkdocstrings: + handlers: + python: + paths: ['src'] # Change 'src' to your actual sources directory + options: + docstring_style: numpy + group_by_category: false + heading_level: 1 + show_root_heading: true + show_root_full_path: false + show_submodules: true + show_source: true + - search + +# Determines additional directories to watch when running mkdocs serve +watch: + - includes + - overrides + - ../src + +# Exclude files and folders from the global navigation +not_in_nav: | + index.md + +# Format and layout of the global navigation for the site +nav: + - Introduction: + - Introduction: introduction/index.md + - Installation & Setup: + - Installation & Setup: installation-and-setup/index.md + - User Guide: + - User Guide: user-guide/index.md + - Tutorials: + - Tutorials: tutorials/index.md + - Getting Started: + - Dummy tutorial: tutorials/tutorial.ipynb + - API Reference: + - API Reference: api-reference/index.md diff --git a/docs/overrides/.icons/app.svg b/docs/overrides/.icons/app.svg new file mode 100644 index 00000000..b4fdd4f3 --- /dev/null +++ b/docs/overrides/.icons/app.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/docs/overrides/.icons/google-colab.svg b/docs/overrides/.icons/google-colab.svg new file mode 100644 index 00000000..9cd9d1b0 --- /dev/null +++ b/docs/overrides/.icons/google-colab.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/docs/overrides/main.html b/docs/overrides/main.html new file mode 100644 index 00000000..2e146827 --- /dev/null +++ b/docs/overrides/main.html @@ -0,0 +1,39 @@ +{% extends "base.html" %} + +{% block content %} + +{% if page.nb_url %} + {# Parse notebook path/URL #} + {% set parts = page.nb_url.split('/') %} + {% set tutorial_name = parts[-2] %} + {% set filename = parts[-1] %} + + {# Colab url #} + {% set base_colab_url = "https://colab.research.google.com/github/" %} + {% set colab_url = + base_colab_url ~ config.extra.vars.github_repository ~ + "/blob/gh-pages/" ~ config.extra.vars.docs_version ~ + "/tutorials/" ~ tutorial_name ~ "/" ~ filename + %} + + {# Download link: relative to the current page #} + {% set file_url = filename %} + + {# Open in Colab (absolute GitHub URL; works anywhere) #} + + {% include ".icons/google-colab.svg" %} + + + {# Download: use a RELATIVE link to the file next to this page #} + + {% include ".icons/material/download.svg" %} + +{% endif %} + +{{ super() }} +{% endblock content %} diff --git a/docs/overrides/partials/logo.html b/docs/overrides/partials/logo.html new file mode 100644 index 00000000..78fa69ca --- /dev/null +++ b/docs/overrides/partials/logo.html @@ -0,0 +1,15 @@ +{% if ( config.theme.logo_light_mode and config.theme.logo_dark_mode ) %} +logo +logo +{% elif config.theme.logo %} +logo +{% else %} {% set icon = config.theme.icon.logo or "material/library" %} {% +include ".icons/" ~ icon ~ ".svg" %} {% endif %} diff --git a/pixi.lock b/pixi.lock index cbcfe22e..32c345d2 100644 --- a/pixi.lock +++ b/pixi.lock @@ -5,12 +5,29 @@ environments: - url: https://conda.anaconda.org/conda-forge/ indexes: - https://pypi.org/simple + options: + pypi-prerelease-mode: if-necessary-or-explicit packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_libgcc_mutex-0.1-conda_forge.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-2_gnu.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/bcrypt-5.0.0-py313h843e2db_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py313hf46b229_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.14.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.3-py313heb322e3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dunamai-1.26.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/eval-type-backport-0.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/eval_type_backport-0.2.2-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/funcy-2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-7.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-ansible-filters-1.3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1aa0949_4.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libexpat-2.7.1-hecca717_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libffi-3.5.2-h9ec8514_0.conda @@ -18,17 +35,41 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libgomp-15.2.0-h767d61c_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/liblzma-5.8.1-hb9d3cd8_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libmpdec-4.0.0-hb9d3cd8_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.21-h280c20c_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.4-h0c1763c_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libstdcxx-15.2.0-h8f9b012_7.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libuuid-2.41.2-he9a06e4_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.1-hb9d3cd8_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py313h3dea7bd_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/openssl-3.5.4-h26f9b46_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-4.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/plumbum-1.10.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt_toolkit-3.0.51-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.3-py313h843e2db_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pynacl-1.6.2-py313h5008379_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.13.9-hc97d973_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pywin32-on-windows-0.1.0-pyh1179c8e_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py313h3dea7bd_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/questionary-2.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.6.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/34/0e/1e9841cc46196c55ac3eac0b8e08044a88cc70c8cc29e9dc1e33b2ced2b7/pyside6-6.9.3-cp39-abi3-manylinux_2_28_x86_64.whl @@ -37,21 +78,60 @@ environments: - pypi: https://files.pythonhosted.org/packages/be/82/c1c6932f9849bc5e75c93c38a29419505a6e3e0037261e28f3e7ecbf2751/shiboken6-6.9.3-cp39-abi3-manylinux_2_28_x86_64.whl - pypi: ./ osx-64: + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/bcrypt-5.0.0-py313hcc225dc_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/bzip2-1.0.8-h500dc9f_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cffi-2.0.0-py313hf57695f_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.14.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/cryptography-46.0.3-py313h36c3d76_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dunamai-1.26.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/eval-type-backport-0.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/eval_type_backport-0.2.2-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/funcy-2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-7.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-ansible-filters-1.3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libexpat-2.7.1-h21dd04a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libffi-3.5.2-h750e83c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/liblzma-5.8.1-hd471939_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libmpdec-4.0.0-h6e16a3a_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/libsodium-1.0.21-hc6ced15_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libsqlite-3.50.4-h39a8b3b_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/libzlib-1.3.1-hd23fc13_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py313h035b7d0_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/ncurses-6.5-h0622a9a_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/openssl-3.5.4-h230baf5_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-4.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/plumbum-1.10.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt_toolkit-3.0.51-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pydantic-core-2.46.3-py313h23ec8f2_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/pynacl-1.6.2-py313hf61a874_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/python-3.13.9-h17c18a5_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pywin32-on-windows-0.1.0-pyh1179c8e_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py313h7c6a591_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/questionary-2.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/readline-8.2-h7cca4af_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-64/tk-8.6.13-hf689a15_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.6.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h4132b18_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.1-pyhcf101f3_0.conda - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/98/84b16f78b5d92dd234fb1eb9890a350a5b0c83d985bb8c44a92f813a2d02/pyside6-6.10.0-cp39-abi3-macosx_13_0_universal2.whl - pypi: https://files.pythonhosted.org/packages/47/23/9fbdec2ce16244ac3fe28e6d44c39c70465c93a03325939a792fd00fde7f/pyside6_addons-6.10.0-cp39-abi3-macosx_13_0_universal2.whl @@ -59,22 +139,61 @@ environments: - pypi: https://files.pythonhosted.org/packages/fd/78/3e730aea82089dd82b1e092bc265778bda329459e6ad9b7134eec5fff3f2/shiboken6-6.10.0-cp39-abi3-macosx_13_0_universal2.whl - pypi: ./ osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bcrypt-5.0.0-py313h2c089d5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-hbd8a1cb_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py313h224173a_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.14.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cryptography-46.0.3-py313h76c770c_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dunamai-1.26.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/eval-type-backport-0.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/eval_type_backport-0.2.2-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/funcy-2.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-7.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-ansible-filters-1.3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libexpat-2.7.1-hec049ff_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libffi-3.5.2-he5f378a_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/liblzma-5.8.1-h39f12f2_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libmpdec-4.0.0-h5505292_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.21-h1a92334_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.50.4-h4237e3c_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.1-h8359307_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py313h65a2061_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/ncurses-6.5-h5e97a16_3.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/openssl-3.5.4-h5503f6c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-4.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/plumbum-1.10.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt_toolkit-3.0.51-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.3-py313h212e517_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pynacl-1.6.2-py313h6940bce_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.13.9-hfc2f54d_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pywin32-on-windows-0.1.0-pyh1179c8e_3.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py313h65a2061_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/questionary-2.1.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.2-h1d1bf99_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h892fb3f_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.6.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.1-pyhcf101f3_0.conda - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/e6/98/84b16f78b5d92dd234fb1eb9890a350a5b0c83d985bb8c44a92f813a2d02/pyside6-6.10.0-cp39-abi3-macosx_13_0_universal2.whl - pypi: https://files.pythonhosted.org/packages/47/23/9fbdec2ce16244ac3fe28e6d44c39c70465c93a03325939a792fd00fde7f/pyside6_addons-6.10.0-cp39-abi3-macosx_13_0_universal2.whl @@ -82,23 +201,63 @@ environments: - pypi: https://files.pythonhosted.org/packages/fd/78/3e730aea82089dd82b1e092bc265778bda329459e6ad9b7134eec5fff3f2/shiboken6-6.10.0-cp39-abi3-macosx_13_0_universal2.whl - pypi: ./ win-64: + - conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/bcrypt-5.0.0-py313hfbe8231_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_8.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2025.10.5-h4c7d964_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py313h5ea7bf4_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.14.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cryptography-46.0.3-py313hf5c5e30_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/dunamai-1.26.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/eval-type-backport-0.2.2-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/eval_type_backport-0.2.2-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/funcy-2.0-pyhd8ed1ab_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-7.1.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/invoke-3.0.3-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-ansible-filters-1.3.2-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libexpat-2.7.1-hac47afa_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libffi-3.5.2-h52bdfb6_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/liblzma-5.8.1-h2466b09_2.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libmpdec-4.0.0-h2466b09_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.21-h6a83c73_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.50.4-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.1-h2466b09_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py313hd650c13_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/openssl-3.5.4-h725018a_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-4.0.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/plumbum-1.10.0-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/prompt_toolkit-3.0.51-hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.3-pyhcf101f3_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.3-py313hfbe8231_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pynacl-1.6.2-py313hed4ef92_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.13.9-h09917c8_101_cp313.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.13-8_cp313.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-311-py313h40c08fc_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/pywin32-on-windows-0.1.0-pyh07e9846_2.tar.bz2 + - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py313hd650c13_1.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/questionary-2.1.1-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h2c6b04d_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/ucrt-10.0.26100.0-h57928b3_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc-14.3-h2b53caa_32.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vc14_runtime-14.44.35208-h818238b_32.conda - conda: https://conda.anaconda.org/conda-forge/win-64/vcomp14-14.44.35208-h818238b_32.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.6.0-pyhd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.1-pyhcf101f3_0.conda - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/43/67/94794ebaf198bbdb35cb77f19f38370f9b323b036ab149874bc33c38faab/pyside6-6.10.0-cp39-abi3-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/7d/5d/a3c32f85ac7f905c95679967c0ddda0ba043c273b75623cc90d8185064e4/pyside6_addons-6.10.0-cp39-abi3-win_amd64.whl @@ -127,6 +286,83 @@ packages: purls: [] size: 23621 timestamp: 1650670423406 +- conda: https://conda.anaconda.org/conda-forge/noarch/annotated-types-0.7.0-pyhd8ed1ab_1.conda + sha256: e0ea1ba78fbb64f17062601edda82097fcf815012cf52bb704150a2668110d48 + md5: 2934f256a8acfe48f6ebb4fce6cde29c + depends: + - python >=3.9 + - typing-extensions >=4.0.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/annotated-types?source=hash-mapping + size: 18074 + timestamp: 1733247158254 +- conda: https://conda.anaconda.org/conda-forge/linux-64/bcrypt-5.0.0-py313h843e2db_1.conda + sha256: cdf7496797af275f8bb5edd270aa06303dde623c7dd3a5941b0ce085d8f4cdc5 + md5: b59ec3796cba93d0db5f71e361176f27 + depends: + - python + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.13.* *_cp313 + constrains: + - __glibc >=2.17 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/bcrypt?source=hash-mapping + size: 292710 + timestamp: 1762497710166 +- conda: https://conda.anaconda.org/conda-forge/osx-64/bcrypt-5.0.0-py313hcc225dc_1.conda + sha256: b92b8fcad1fd3a74e42777c7f03e6c4dc931aae93c9f981295928bc45484f7bf + md5: 4adea8a12c7fd3e512e9e47436f84c94 + depends: + - python + - __osx >=10.13 + - python_abi 3.13.* *_cp313 + constrains: + - __osx >=10.13 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/bcrypt?source=hash-mapping + size: 278409 + timestamp: 1762497780574 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/bcrypt-5.0.0-py313h2c089d5_1.conda + sha256: c8e3b95ad2665dc16ac37888fb91dd481fa759bad1fbf799451d12d6bfaec600 + md5: 4b14cba28eaf98170c2ddd7e900efa07 + depends: + - python + - __osx >=11.0 + - python 3.13.* *_cp313 + - python_abi 3.13.* *_cp313 + constrains: + - __osx >=11.0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/bcrypt?source=hash-mapping + size: 267465 + timestamp: 1762497730829 +- conda: https://conda.anaconda.org/conda-forge/win-64/bcrypt-5.0.0-py313hfbe8231_1.conda + sha256: 21baf1316a8160fd3b83e7128803735b107f47c58ba5d5305a372fd6c679d42b + md5: 2a664b5cc93fbc9d7ca595b206a299f0 + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.13.* *_cp313 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/bcrypt?source=hash-mapping + size: 170702 + timestamp: 1762497739995 - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_8.conda sha256: c30daba32ddebbb7ded490f0e371eae90f51e72db620554089103b4a6934b0d5 md5: 51a19bba1b8ebfb60df25cde030b7ebc @@ -188,14 +424,233 @@ packages: purls: [] size: 155907 timestamp: 1759649036195 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py313hf46b229_1.conda + sha256: 2162a91819945c826c6ef5efe379e88b1df0fe9a387eeba23ddcf7ebeacd5bd6 + md5: d0616e7935acab407d1543b28c446f6f + depends: + - __glibc >=2.17,<3.0.a0 + - libffi >=3.5.2,<3.6.0a0 + - libgcc >=14 + - pycparser + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping + size: 298357 + timestamp: 1761202966461 +- conda: https://conda.anaconda.org/conda-forge/osx-64/cffi-2.0.0-py313hf57695f_1.conda + sha256: 16c8c80bebe1c3d671382a64beaa16996e632f5b75963379e2b084eb6bc02053 + md5: b10f64f2e725afc9bf2d9b30eff6d0ea + depends: + - __osx >=10.13 + - libffi >=3.5.2,<3.6.0a0 + - pycparser + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping + size: 290946 + timestamp: 1761203173891 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py313h224173a_1.conda + sha256: 1fa69651f5e81c25d48ac42064db825ed1a3e53039629db69f86b952f5ce603c + md5: 050374657d1c7a4f2ea443c0d0cbd9a0 + depends: + - __osx >=11.0 + - libffi >=3.5.2,<3.6.0a0 + - pycparser + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping + size: 291376 + timestamp: 1761203583358 +- conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py313h5ea7bf4_1.conda + sha256: f867a11f42bb64a09b232e3decf10f8a8fe5194d7e3a216c6bac9f40483bd1c6 + md5: 55b44664f66a2caf584d72196aa98af9 + depends: + - pycparser + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: MIT + license_family: MIT + purls: + - pkg:pypi/cffi?source=hash-mapping + size: 292681 + timestamp: 1761203203673 +- conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda + sha256: ab29d57dc70786c1269633ba3dff20288b81664d3ff8d21af995742e2bb03287 + md5: 962b9857ee8e7018c22f2776ffa0b2d7 + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/colorama?source=hash-mapping + size: 27011 + timestamp: 1733218222191 +- conda: https://conda.anaconda.org/conda-forge/noarch/copier-9.14.3-pyhcf101f3_0.conda + sha256: e5a34aa9414037802e6b78eda9d51f61dcc6e94d37268bb4b85844c3ebd21728 + md5: 67cf71f2fbd9fa0bf4ebb9535c08e3fb + depends: + - python >=3.10 + - colorama >=0.4.6 + - dunamai >=1.7.0 + - funcy >=1.17 + - jinja2 >=3.1.5 + - jinja2-ansible-filters >=1.3.1 + - packaging >=23.0 + - pathspec >=0.9.0 + - plumbum >=1.6.9 + - prompt-toolkit <3.0.52 + - pydantic >=2.4.2 + - pygments >=2.7.1 + - pyyaml >=5.3.1 + - questionary >=1.8.1 + - eval-type-backport >=0.1.3,<0.3.0 + - platformdirs >=4.3.6 + - typing_extensions >=4.0.0,<5.0.0 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/copier?source=hash-mapping + size: 56172 + timestamp: 1775837250252 +- conda: https://conda.anaconda.org/conda-forge/linux-64/cryptography-46.0.3-py313heb322e3_1.conda + sha256: beb4f2fa46bf3d550bf5bf2a07796be14cbe73ceebe43b28e634ee778b547e99 + md5: 4e6278c519f2766ea707361f81b33364 + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.14 + - libgcc >=14 + - openssl >=3.5.4,<4.0a0 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + constrains: + - __glibc >=2.17 + license: Apache-2.0 AND BSD-3-Clause AND PSF-2.0 AND MIT + license_family: BSD + purls: + - pkg:pypi/cryptography?source=hash-mapping + size: 1723198 + timestamp: 1764805305302 +- conda: https://conda.anaconda.org/conda-forge/osx-64/cryptography-46.0.3-py313h36c3d76_1.conda + sha256: cb0ced5293955feebf09401bfa18ab4c4c136db0ad795e993131deba3b60dd4f + md5: b168b30831ed35aeedf36adc4c148c1c + depends: + - __osx >=10.13 + - cffi >=1.14 + - openssl >=3.5.4,<4.0a0 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + constrains: + - __osx >=10.13 + license: Apache-2.0 AND BSD-3-Clause AND PSF-2.0 AND MIT + license_family: BSD + purls: + - pkg:pypi/cryptography?source=hash-mapping + size: 1652256 + timestamp: 1764805778768 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cryptography-46.0.3-py313h76c770c_1.conda + sha256: ea8b464e53db32fb64fa22b736be8ee3b401d0395dc62e191d0cb8c36b7a86be + md5: f6a4c9667a9994f3a499b4ce23e80959 + depends: + - __osx >=11.0 + - cffi >=1.14 + - openssl >=3.5.4,<4.0a0 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + constrains: + - __osx >=11.0 + license: Apache-2.0 AND BSD-3-Clause AND PSF-2.0 AND MIT + license_family: BSD + purls: + - pkg:pypi/cryptography?source=hash-mapping + size: 1596896 + timestamp: 1764805652572 +- conda: https://conda.anaconda.org/conda-forge/win-64/cryptography-46.0.3-py313hf5c5e30_1.conda + sha256: c6a6b26f3d66612794041e4e35ed8d5782332c1a37fd11cce733e94593c7c51e + md5: 5349b57b1b430a7437345ba1c48ce502 + depends: + - cffi >=1.14 + - openssl >=3.5.4,<4.0a0 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 AND BSD-3-Clause AND PSF-2.0 AND MIT + license_family: BSD + purls: + - pkg:pypi/cryptography?source=hash-mapping + size: 1488294 + timestamp: 1764805888325 +- conda: https://conda.anaconda.org/conda-forge/noarch/dunamai-1.26.1-pyhd8ed1ab_0.conda + sha256: 930f21584babebdabc8310f894557d032211f6cb427f7028c7c92cab6fe6cc1f + md5: dbb824a3a87ac1e2ce02f8227be67e74 + depends: + - importlib-metadata >=1.6.0 + - packaging >=20.9 + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/dunamai?source=hash-mapping + size: 30866 + timestamp: 1775597880526 - pypi: ./ name: easyapp version: 0.8.0 - sha256: 9a70274e50d805916093ff0760fe747728bce9023b76cb802b1feba354b24fd6 + sha256: a82e1aa5b7c1f8abb5503bd7192888b3d49dc081c30d1d1d225d43ba87f0faf6 requires_dist: - pyside6 - requires_python: '>=3.10' - editable: true + requires_python: '>=3.11' +- conda: https://conda.anaconda.org/conda-forge/noarch/eval-type-backport-0.2.2-pyhd8ed1ab_0.conda + sha256: 05ffdcb83903c159bfbb78a07fbcce6fd6dda41df9c55ed75e0eb1db5528048f + md5: 879479fda1dddb002fdc4885cea33740 + depends: + - eval_type_backport >=0.2.2,<0.2.3.0a0 + - python >=3.9 + license: MIT + license_family: MIT + purls: [] + size: 6662 + timestamp: 1734857849281 +- conda: https://conda.anaconda.org/conda-forge/noarch/eval_type_backport-0.2.2-pyha770c72_0.conda + sha256: 2d721421a60676216e10837a240c75e2190e093920a4016a469fa9a62c95ab5f + md5: 8681d7f876da5e66a1c7fce424509383 + depends: + - python >=3.9 + constrains: + - eval-type-backport >=0.2.2,<0.2.3.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/eval-type-backport?source=hash-mapping + size: 11520 + timestamp: 1734857840035 +- conda: https://conda.anaconda.org/conda-forge/noarch/funcy-2.0-pyhd8ed1ab_1.conda + sha256: 4a3e3e86b7b49aaa2a0faa57da2bcf39f7ee858499e8335132f83bfeed79191e + md5: 84f8955e99a8944fdee49da39edb0add + depends: + - python >=3.9 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/funcy?source=hash-mapping + size: 30249 + timestamp: 1734381235500 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/icu-75.1-hfee45f7_0.conda sha256: 9ba12c93406f3df5ab0a43db8a4b4ef67a5871dfd401010fbe29b218b2cbe620 md5: 5eb22c1d7b3fc4abb50d92d621583137 @@ -206,6 +661,70 @@ packages: purls: [] size: 11857802 timestamp: 1720853997952 +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib-metadata-8.8.0-pyhcf101f3_0.conda + sha256: 82ab2a0d91ca1e7e63ab6a4939356667ef683905dea631bc2121aa534d347b16 + md5: 080594bf4493e6bae2607e65390c520a + depends: + - python >=3.10 + - zipp >=3.20 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/importlib-metadata?source=compressed-mapping + size: 34387 + timestamp: 1773931568510 +- conda: https://conda.anaconda.org/conda-forge/noarch/importlib_resources-7.1.0-pyhd8ed1ab_0.conda + sha256: a563a51aa522998172838e867e6dedcf630bc45796e8612f5a1f6d73e9c8125a + md5: 0ba6225c279baf7ea9473a62ea0ec9ae + depends: + - python >=3.10 + - zipp >=3.1.0 + constrains: + - importlib-resources >=7.1.0,<7.1.1.0a0 + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/importlib-resources?source=compressed-mapping + size: 34809 + timestamp: 1776068839274 +- conda: https://conda.anaconda.org/conda-forge/noarch/invoke-3.0.3-pyhd8ed1ab_0.conda + sha256: d7421c54944dec04d2671e23196a15583d48f309d06fbcf995b5dfa6d586a5e9 + md5: 4dee387356d58bdc4b686ae3424fbd9e + depends: + - python >=3.10 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/invoke?source=hash-mapping + size: 133811 + timestamp: 1775579645825 +- conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-3.1.6-pyhcf101f3_1.conda + sha256: fc9ca7348a4f25fed2079f2153ecdcf5f9cf2a0bc36c4172420ca09e1849df7b + md5: 04558c96691bed63104678757beb4f8d + depends: + - markupsafe >=2.0 + - python >=3.10 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/jinja2?source=compressed-mapping + size: 120685 + timestamp: 1764517220861 +- conda: https://conda.anaconda.org/conda-forge/noarch/jinja2-ansible-filters-1.3.2-pyhd8ed1ab_1.conda + sha256: a36789229ca9ce5315265b9d425abce9acb4691f5864aea69e935020545a9acb + md5: 974c5b3e353f031cfcf2365c9d375926 + depends: + - jinja2 + - python >=3.9 + - pyyaml + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/jinja2-ansible-filters?source=hash-mapping + size: 20315 + timestamp: 1734906203051 - conda: https://conda.anaconda.org/conda-forge/linux-64/ld_impl_linux-64-2.44-h1aa0949_4.conda sha256: 96b6900ca0489d9e5d0318a6b49f8eff43fd85fef6e07cb0c25344ee94cd7a3a md5: c94ab6ff54ba5172cf1c58267005670f @@ -427,6 +946,45 @@ packages: purls: [] size: 88657 timestamp: 1723861474602 +- conda: https://conda.anaconda.org/conda-forge/linux-64/libsodium-1.0.21-h280c20c_3.conda + sha256: 64e5c80cbce4680a2d25179949739a6def695d72c40ca28f010711764e372d97 + md5: 7af961ef4aa2c1136e11dd43ded245ab + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: ISC + purls: [] + size: 277661 + timestamp: 1772479381288 +- conda: https://conda.anaconda.org/conda-forge/osx-64/libsodium-1.0.21-hc6ced15_3.conda + sha256: 7dd254e844372fbf3a60a7c029df1ea0cb3fa0b18586cda769d9cd6cc0e59c4b + md5: c4b8a6c8a8aa6ed657a3c1c1eb6917e9 + depends: + - __osx >=10.13 + license: ISC + purls: [] + size: 291865 + timestamp: 1772479644707 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsodium-1.0.21-h1a92334_3.conda + sha256: df603472ea1ebd8e7d4fb71e4360fe48d10b11c240df51c129de1da2ff9e8227 + md5: 7cc5247987e6d115134ebab15186bc13 + depends: + - __osx >=11.0 + license: ISC + purls: [] + size: 248039 + timestamp: 1772479570912 +- conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.21-h6a83c73_3.conda + sha256: d915f4fa8ebbf237c7a6e511ed458f2cfdc7c76843a924740318a15d0dd33d6d + md5: da2aa614d16a795b3007b6f4a1318a81 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: ISC + purls: [] + size: 276860 + timestamp: 1772479407566 - conda: https://conda.anaconda.org/conda-forge/linux-64/libsqlite-3.50.4-h0c1763c_0.conda sha256: 6d9c32fc369af5a84875725f7ddfbfc2ace795c28f246dc70055a79f9b2003da md5: 0b367fad34931cb79e0d6b7e5c06bb1c @@ -545,6 +1103,70 @@ packages: purls: [] size: 55476 timestamp: 1727963768015 +- conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py313h3dea7bd_1.conda + sha256: 72ed7c0216541d65a17b171bf2eec4a3b81e9158d8ed48e59e1ecd3ae302d263 + md5: aeb9b9da79fd0258b3db091d1fefcd71 + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping + size: 26100 + timestamp: 1772445154165 +- conda: https://conda.anaconda.org/conda-forge/osx-64/markupsafe-3.0.3-py313h035b7d0_1.conda + sha256: e589b345402e352fb47394f7bc311c241f37627a34a9becc9299b395809a5853 + md5: 3d88718cbd26857fb68fa899e80177ea + depends: + - __osx >=11.0 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping + size: 25312 + timestamp: 1772445439146 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py313h65a2061_1.conda + sha256: f62892a42948c61aa0a13d9a36ff811651f0a1102331223594aecf3cc042bece + md5: 0195d558b0c0ab8f4af3089af83067c5 + depends: + - __osx >=11.0 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping + size: 26009 + timestamp: 1772445537524 +- conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py313hd650c13_1.conda + sha256: 9dc626b6c00bc2dbd2494df689876ff675b93d92636ba5df8e37b99040a1f6bc + md5: 5cc690ddf943700e0ef50a265df31f03 + depends: + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + constrains: + - jinja2 >=3.0.0 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/markupsafe?source=hash-mapping + size: 28992 + timestamp: 1772445161959 - conda: https://conda.anaconda.org/conda-forge/linux-64/ncurses-6.5-h2d0b736_3.conda sha256: 3fde293232fa3fca98635e1167de6b7c7fda83caf24b9d6c91ec9eefb4f4d586 md5: 47e340acb35de30501a76c7c799c41d7 @@ -620,11 +1242,273 @@ packages: purls: [] size: 9218823 timestamp: 1759326176247 +- conda: https://conda.anaconda.org/conda-forge/noarch/packaging-26.2-pyhc364b38_0.conda + sha256: 3906abfb6511a3bb309e39b9b1b7bc38f50a723971de2395489fd1f379255890 + md5: 4c06a92e74452cfa53623a81592e8934 + depends: + - python >=3.8 + - python + license: Apache-2.0 + license_family: APACHE + purls: + - pkg:pypi/packaging?source=compressed-mapping + size: 91574 + timestamp: 1777103621679 +- conda: https://conda.anaconda.org/conda-forge/noarch/paramiko-4.0.0-pyhd8ed1ab_0.conda + sha256: ce76d5a1fc6c7ef636cbdbf14ce2d601a1bfa0dd8d286507c1fd02546fccb94b + md5: 1a884d2b1ea21abfb73911dcdb8342e4 + depends: + - bcrypt >=3.2 + - cryptography >=3.3 + - invoke >=2.0 + - pynacl >=1.5 + - python >=3.9 + license: LGPL-2.1-or-later + license_family: LGPL + purls: + - pkg:pypi/paramiko?source=hash-mapping + size: 159896 + timestamp: 1755102147074 +- conda: https://conda.anaconda.org/conda-forge/noarch/pathspec-1.1.1-pyhd8ed1ab_0.conda + sha256: 6eaee417d33f298db79bc7185ab1208604c0e6cf51dade34cd513c6f9db9c6f3 + md5: 11adc78451c998c0fd162584abfa3559 + depends: + - python >=3.10 + license: MPL-2.0 + license_family: MOZILLA + purls: + - pkg:pypi/pathspec?source=compressed-mapping + size: 56559 + timestamp: 1777271601895 - pypi: https://files.pythonhosted.org/packages/44/3c/d717024885424591d5376220b5e836c2d5293ce2011523c9de23ff7bf068/pip-25.3-py3-none-any.whl name: pip version: '25.3' sha256: 9655943313a94722b7774661c21049070f6bbb0a1516bf02f7c8d5d9201514cd requires_python: '>=3.9' +- conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_0.conda + sha256: 8f29915c172f1f7f4f7c9391cd5dac3ebf5d13745c8b7c8006032615246345a5 + md5: 89c0b6d1793601a2a3a3f7d2d3d8b937 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/platformdirs?source=compressed-mapping + size: 25862 + timestamp: 1775741140609 +- conda: https://conda.anaconda.org/conda-forge/noarch/plumbum-1.10.0-pyhcf101f3_0.conda + sha256: 972e0c1c9f0b1763d482e885732baef7f9a0cbe8686f5e2c6bdd88838619a59a + md5: 7fd2e38f4e608f5ebd80f871352c5495 + depends: + - python >=3.10 + - pywin32-on-windows + - paramiko + - importlib_resources + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/plumbum?source=hash-mapping + size: 103911 + timestamp: 1761911487086 +- conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.51-pyha770c72_0.conda + sha256: ebc1bb62ac612af6d40667da266ff723662394c0ca78935340a5b5c14831227b + md5: d17ae9db4dc594267181bd199bf9a551 + depends: + - python >=3.9 + - wcwidth + constrains: + - prompt_toolkit 3.0.51 + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/prompt-toolkit?source=hash-mapping + size: 271841 + timestamp: 1744724188108 +- conda: https://conda.anaconda.org/conda-forge/noarch/prompt_toolkit-3.0.51-hd8ed1ab_0.conda + sha256: 936189f0373836c1c77cd2d6e71ba1e583e2d3920bf6d015e96ee2d729b5e543 + md5: 1e61ab85dd7c60e5e73d853ea035dc29 + depends: + - prompt-toolkit >=3.0.51,<3.0.52.0a0 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 7182 + timestamp: 1744724189376 +- conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda + sha256: 79db7928d13fab2d892592223d7570f5061c192f27b9febd1a418427b719acc6 + md5: 12c566707c80111f9799308d9e265aef + depends: + - python >=3.9 + - python + license: BSD-3-Clause + license_family: BSD + purls: + - pkg:pypi/pycparser?source=hash-mapping + size: 110100 + timestamp: 1733195786147 +- conda: https://conda.anaconda.org/conda-forge/noarch/pydantic-2.13.3-pyhcf101f3_0.conda + sha256: 12909d5c2bfb31492667dd4132ac900dd47f8162bd8b1dd9e5973ce8ea28ca1a + md5: f690e6f204efd2e5c06b57518a383d98 + depends: + - typing-inspection >=0.4.2 + - typing_extensions >=4.14.1 + - python >=3.10 + - annotated-types >=0.6.0 + - pydantic-core ==2.46.3 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/pydantic?source=compressed-mapping + size: 346352 + timestamp: 1776728341165 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pydantic-core-2.46.3-py313h843e2db_0.conda + sha256: 885d7809b6f9e011f8cd7294ab030535a65637a229fb5e49453f7be4f05c3083 + md5: 81752daa2838eec5df529e5c60044dc5 + depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python_abi 3.13.* *_cp313 + constrains: + - __glibc >=2.17 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=hash-mapping + size: 1909995 + timestamp: 1776704319736 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pydantic-core-2.46.3-py313h23ec8f2_0.conda + sha256: 7357f1f4c9b722dbf3331f9d4793505ff5aa03e5e425f48d18074daffdc7c2b8 + md5: a5aefd2880a59680270677265aaa7cc6 + depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 + - __osx >=11.0 + - python_abi 3.13.* *_cp313 + constrains: + - __osx >=10.13 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=hash-mapping + size: 1894226 + timestamp: 1776704380520 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pydantic-core-2.46.3-py313h212e517_0.conda + sha256: c09c7ce40f6e3496a4bb61ef5057b40c1ae834653e6ea7cdf4ddd6534b929922 + md5: 2f753ff1d3b847e16cf2692fe7df0655 + depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 + - python 3.13.* *_cp313 + - __osx >=11.0 + - python_abi 3.13.* *_cp313 + constrains: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=hash-mapping + size: 1727305 + timestamp: 1776704499510 +- conda: https://conda.anaconda.org/conda-forge/win-64/pydantic-core-2.46.3-py313hfbe8231_0.conda + sha256: cd4adb96d98c26e493782db78728a3c64a9a3b7f4d8cd095f99f8b4d78170058 + md5: 6d685c3ef2cd263b182755e2c4fc3a1c + depends: + - python + - typing-extensions >=4.6.0,!=4.7.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.13.* *_cp313 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pydantic-core?source=hash-mapping + size: 1898684 + timestamp: 1776704352654 +- conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda + sha256: cf70b2f5ad9ae472b71235e5c8a736c9316df3705746de419b59d442e8348e86 + md5: 16c18772b340887160c79a6acc022db0 + depends: + - python >=3.10 + license: BSD-2-Clause + license_family: BSD + purls: + - pkg:pypi/pygments?source=compressed-mapping + size: 893031 + timestamp: 1774796815820 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pynacl-1.6.2-py313h5008379_1.conda + sha256: 51e80a7bef95025ad47a92acb69ee0e78f01e107655c86fe76abcde2ac688166 + md5: c4426edfc5514a2c9be6871557bce52b + depends: + - __glibc >=2.17,<3.0.a0 + - cffi >=1.4.1 + - libgcc >=14 + - libsodium >=1.0.21,<1.0.22.0a0 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - six + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/pynacl?source=hash-mapping + size: 1191901 + timestamp: 1772171244923 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pynacl-1.6.2-py313hf61a874_1.conda + sha256: 2638c5a765310ac71641f37dc614792eb39b9a62059758de6b8ffc06b0639c1f + md5: 3037cfd226c8d60a492c9d0a5fe0d277 + depends: + - __osx >=11.0 + - cffi >=1.4.1 + - libsodium >=1.0.21,<1.0.22.0a0 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - six + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/pynacl?source=hash-mapping + size: 1195552 + timestamp: 1772171533952 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pynacl-1.6.2-py313h6940bce_1.conda + sha256: ef6ab85953ebf81b3b0c6d0b6377eb1a318b07823e76d2c8e850365e60d9ca5c + md5: fca94b9ddea6d5c63ccb6b5ad6d8e232 + depends: + - __osx >=11.0 + - cffi >=1.4.1 + - libsodium >=1.0.21,<1.0.22.0a0 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + - six + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/pynacl?source=hash-mapping + size: 1192924 + timestamp: 1772171603602 +- conda: https://conda.anaconda.org/conda-forge/win-64/pynacl-1.6.2-py313hed4ef92_1.conda + sha256: 0960e727d0bd70e07bca0c049c4c4afa0b41bcea050135db49f54c1c0c02ed7f + md5: 99b7617a643bed6a403fc908c1e167f0 + depends: + - cffi >=1.4.1 + - libsodium >=1.0.21,<1.0.22.0a0 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - six + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + license: Apache-2.0 + license_family: Apache + purls: + - pkg:pypi/pynacl?source=hash-mapping + size: 1186552 + timestamp: 1772171311072 - pypi: https://files.pythonhosted.org/packages/34/0e/1e9841cc46196c55ac3eac0b8e08044a88cc70c8cc29e9dc1e33b2ced2b7/pyside6-6.9.3-cp39-abi3-manylinux_2_28_x86_64.whl name: pyside6 version: 6.9.3 @@ -807,6 +1691,118 @@ packages: purls: [] size: 7002 timestamp: 1752805902938 +- conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-311-py313h40c08fc_1.conda + sha256: 87eaeb79b5961e0f216aa840bc35d5f0b9b123acffaecc4fda4de48891901f20 + md5: 1ce4f826332dca56c76a5b0cc89fb19e + depends: + - python + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - python_abi 3.13.* *_cp313 + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/pywin32?source=hash-mapping + size: 6695114 + timestamp: 1756487139550 +- conda: https://conda.anaconda.org/conda-forge/noarch/pywin32-on-windows-0.1.0-pyh07e9846_2.tar.bz2 + sha256: 09803b75cccc16d8586d2f41ea890658d165f4afc359973fa1c7904a2c140eae + md5: 91733394059b880d9cc0d010c20abda0 + depends: + - python >=2.7 + - pywin32 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 5282 + timestamp: 1646866839398 +- conda: https://conda.anaconda.org/conda-forge/noarch/pywin32-on-windows-0.1.0-pyh1179c8e_3.tar.bz2 + sha256: 6502696aaef571913b22a808b15c185bd8ea4aabb952685deb29e6a6765761cb + md5: 2807a0becd1d986fe1ef9b7f8135f215 + depends: + - __unix + - python >=2.7 + license: BSD-3-Clause + license_family: BSD + purls: [] + size: 4856 + timestamp: 1646866525560 +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py313h3dea7bd_1.conda + sha256: ef7df29b38ef04ec67a8888a4aa039973eaa377e8c4b59a7be0a1c50cd7e4ac6 + md5: f256753e840c3cd3766488c9437a8f8b + depends: + - __glibc >=2.17,<3.0.a0 + - libgcc >=14 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyyaml?source=compressed-mapping + size: 201616 + timestamp: 1770223543730 +- conda: https://conda.anaconda.org/conda-forge/osx-64/pyyaml-6.0.3-py313h7c6a591_1.conda + sha256: ab5f6c27d24facd1832481ccd8f432c676472d57596a3feaa77880a1462cdb2a + md5: 0eaf6cf9939bb465ee62b17d04254f9e + depends: + - __osx >=10.13 + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping + size: 192051 + timestamp: 1770223971430 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py313h65a2061_1.conda + sha256: 950725516f67c9691d81bb8dde8419581c5332c5da3da10c9ba8cbb1698b825d + md5: 5d0c8b92128c93027632ca8f8dc1190f + depends: + - __osx >=11.0 + - python >=3.13,<3.14.0a0 + - python >=3.13,<3.14.0a0 *_cp313 + - python_abi 3.13.* *_cp313 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping + size: 188763 + timestamp: 1770224094408 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py313hd650c13_1.conda + sha256: dfaed50de8ee72a51096163b87631921688851001e38c78a841eba1ae8b35889 + md5: c1bdb8dd255c79fb9c428ad25cc6ee54 + depends: + - python >=3.13,<3.14.0a0 + - python_abi 3.13.* *_cp313 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - yaml >=0.2.5,<0.3.0a0 + license: MIT + license_family: MIT + purls: + - pkg:pypi/pyyaml?source=hash-mapping + size: 180992 + timestamp: 1770223457761 +- conda: https://conda.anaconda.org/conda-forge/noarch/questionary-2.1.1-pyhd8ed1ab_0.conda + sha256: 0604c6dff3af5f53e34fceb985395d08287137f220450108a795bcd1959caf14 + md5: 34fa231b5c5927684b03bb296bb94ddc + depends: + - prompt_toolkit >=2.0,<4.0 + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/questionary?source=hash-mapping + size: 31257 + timestamp: 1757356458097 - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.2-h8c095d6_2.conda sha256: 2d6d0c026902561ed77cd646b5021aef2d4db22e57a5b0178dfc669231e06d2c md5: 283b96675859b20a825f8fa30f311446 @@ -853,6 +1849,18 @@ packages: version: 6.10.0 sha256: 7a5f5f400ebfb3a13616030815708289c2154e701a60b9db7833b843e0bee543 requires_python: '>=3.9,<3.14' +- conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda + sha256: 458227f759d5e3fcec5d9b7acce54e10c9e1f4f4b7ec978f3bfd54ce4ee9853d + md5: 3339e3b65d58accf4ca4fb8748ab16b3 + depends: + - python >=3.9 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/six?source=hash-mapping + size: 18455 + timestamp: 1753199211006 - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_hd72426e_102.conda sha256: a84ff687119e6d8752346d1d408d5cf360dee0badd487a472aa8ddedfdc219e1 md5: a0116df4f4ed05c303811a837d5b39d8 @@ -899,6 +1907,41 @@ packages: purls: [] size: 3466348 timestamp: 1748388121356 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda + sha256: 7c2df5721c742c2a47b2c8f960e718c930031663ac1174da67c1ed5999f7938c + md5: edd329d7d3a4ab45dcf905899a7a6115 + depends: + - typing_extensions ==4.15.0 pyhcf101f3_0 + license: PSF-2.0 + license_family: PSF + purls: [] + size: 91383 + timestamp: 1756220668932 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing-inspection-0.4.2-pyhcf101f3_2.conda + sha256: 8b90d2f19f9458b8c58a55e1fcdc1d90c1603a847a47654d8a454549413ba60a + md5: 53f5409c5cfd6c5a66417d68e3f0a864 + depends: + - python >=3.10 + - typing_extensions >=4.12.0 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/typing-inspection?source=compressed-mapping + size: 20935 + timestamp: 1777105465795 +- conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda + sha256: 032271135bca55aeb156cee361c81350c6f3fb203f57d024d7e5a1fc9ef18731 + md5: 0caa1af407ecff61170c9437a808404d + depends: + - python >=3.10 + - python + license: PSF-2.0 + license_family: PSF + purls: + - pkg:pypi/typing-extensions?source=hash-mapping + size: 51692 + timestamp: 1756220668932 - conda: https://conda.anaconda.org/conda-forge/noarch/tzdata-2025b-h78e105d_0.conda sha256: 5aaa366385d716557e365f0a4e9c3fca43ba196872abbbe3d56bb610d131e192 md5: 4222072737ccff51314b5ece9c7d6f5a @@ -953,6 +1996,75 @@ packages: purls: [] size: 114846 timestamp: 1760418593847 +- conda: https://conda.anaconda.org/conda-forge/noarch/wcwidth-0.6.0-pyhd8ed1ab_0.conda + sha256: e298b508b2473c4227206800dfb14c39e4b14fd79d4636132e9e1e4244cdf4aa + md5: c3197f8c0d5b955c904616b716aca093 + depends: + - python >=3.10 + license: MIT + license_family: MIT + purls: + - pkg:pypi/wcwidth?source=hash-mapping + size: 71550 + timestamp: 1770634638503 +- conda: https://conda.anaconda.org/conda-forge/linux-64/yaml-0.2.5-h280c20c_3.conda + sha256: 6d9ea2f731e284e9316d95fa61869fe7bbba33df7929f82693c121022810f4ad + md5: a77f85f77be52ff59391544bfe73390a + depends: + - libgcc >=14 + - __glibc >=2.17,<3.0.a0 + license: MIT + license_family: MIT + purls: [] + size: 85189 + timestamp: 1753484064210 +- conda: https://conda.anaconda.org/conda-forge/osx-64/yaml-0.2.5-h4132b18_3.conda + sha256: a335161bfa57b64e6794c3c354e7d49449b28b8d8a7c4ed02bf04c3f009953f9 + md5: a645bb90997d3fc2aea0adf6517059bd + depends: + - __osx >=10.13 + license: MIT + license_family: MIT + purls: [] + size: 79419 + timestamp: 1753484072608 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/yaml-0.2.5-h925e9cb_3.conda + sha256: b03433b13d89f5567e828ea9f1a7d5c5d697bf374c28a4168d71e9464f5dafac + md5: 78a0fe9e9c50d2c381e8ee47e3ea437d + depends: + - __osx >=11.0 + license: MIT + license_family: MIT + purls: [] + size: 83386 + timestamp: 1753484079473 +- conda: https://conda.anaconda.org/conda-forge/win-64/yaml-0.2.5-h6a83c73_3.conda + sha256: 80ee68c1e7683a35295232ea79bcc87279d31ffeda04a1665efdb43cbd50a309 + md5: 433699cba6602098ae8957a323da2664 + depends: + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + - vc >=14.3,<15 + - vc14_runtime >=14.44.35208 + - ucrt >=10.0.20348.0 + license: MIT + license_family: MIT + purls: [] + size: 63944 + timestamp: 1753484092156 +- conda: https://conda.anaconda.org/conda-forge/noarch/zipp-3.23.1-pyhcf101f3_0.conda + sha256: 523616c0530d305d2216c2b4a8dfd3872628b60083255b89c5e0d8c42e738cca + md5: e1c36c6121a7c9c76f2f148f1e83b983 + depends: + - python >=3.10 + - python + license: MIT + license_family: MIT + purls: + - pkg:pypi/zipp?source=compressed-mapping + size: 24461 + timestamp: 1776131454755 - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb8e6e7a_2.conda sha256: a4166e3d8ff4e35932510aaff7aa90772f84b4d07e9f6f83c614cba7ceefe0eb md5: 6432cb5d4ac0046c3ac0a8a0f95842f9 diff --git a/pixi.toml b/pixi.toml index 0faecf02..f87e7754 100644 --- a/pixi.toml +++ b/pixi.toml @@ -5,7 +5,7 @@ # Platform-independent [activation.env] -PYTHONIOENCODING = "utf-8" +PYTHONIOENCODING = 'utf-8' # Platform-specific @@ -27,22 +27,48 @@ PYTHONPATH = "${PIXI_PROJECT_ROOT}/src;%PYTHONPATH%" ########### [workspace] + # Supported platforms for the lock file (pixi.lock) -platforms = ['win-64', 'linux-64', 'osx-64', 'osx-arm64'] +platforms = ['win-64', 'linux-64', 'osx-arm64'] # Channels for fetching packages channels = ['conda-forge'] +##################### +# SYSTEM REQUIREMENTS +##################### + +[system-requirements] + +# Set minimum supported version for macOS to be 14.0 to ensure packages +# like `skipp` that only have wheels for macOS 14.0+ (macosx_14_0_arm64) +# are used instead of building from source. This is a workaround for +# Pixi, see https://github.com/prefix-dev/pixi/issues/5667 +macos = '14.0' + ########## # FEATURES ########## -[dependencies] # == [feature.default.dependencies] -python = '3.13.*' +# Default feature configuration + +[dependencies] +nodejs = '*' # Required for Prettier (non-Python formatting) +jupyterlab = '*' # Jupyter notebooks +pixi-kernel = '*' # Pixi Jupyter kernel [pypi-dependencies] # == [feature.default.pypi-dependencies] -pip = '*' -easyapp = { path = ".", editable = true } +#pip = '*' # Native package installer +easyapplication = { path = '.', editable = true, extras = ['dev'] } + +# Specific features: Set specific Python versions + + +[feature.py-min.dependencies] +python = '3.11.*' +[feature.py-max.dependencies] +python = '3.14.*' + ############## @@ -53,12 +79,200 @@ easyapp = { path = ".", editable = true } # The `default` feature is always included in all environments. # Additional features can be specified per environment. +py-311-env = { features = ['py-min'] } +py-314-env = { features = ['py-max'] } # The `default` environment is always created and includes the `default` feature. # It does not need to be specified explicitly unless non-default features are included. +default = { features = ['py-max'] } ####### # TASKS ####### -#[tasks] +[tasks] + +################## +# 🧪 Testing Tasks +################## + +unit-tests = 'python -m pytest tests/unit/ --color=yes -v' +functional-tests = 'python -m pytest tests/functional/ --color=yes -v' +integration-tests = 'python -m pytest tests/integration/ --color=yes -n auto -v' +notebook-tests = 'python -m pytest --nbmake docs/docs/tutorials/ --nbmake-timeout=1200 --color=yes -n auto -v' + +test = { depends-on = ['unit-tests'] } + +########### +# ✔️ Checks +########### + +pyproject-check = 'python -m validate_pyproject pyproject.toml' +docstring-lint-check = 'pydoclint --quiet src/' +notebook-lint-check = 'nbqa ruff docs/docs/tutorials/' +py-lint-check = 'ruff check src/ tests/ docs/docs/tutorials/' +py-format-check = 'ruff format --check src/ tests/ docs/docs/tutorials/' +nonpy-format-check = 'npx prettier --list-different --config=prettierrc.toml --ignore-unknown .' +nonpy-format-check-modified = 'python tools/nonpy_prettier_modified.py' + +check = 'pre-commit run --hook-stage manual --all-files' + +########## +# 🛠️ Fixes +########## + +docstring-transform = 'pixi run docstripy src/ -s=numpy -w' +docstring-format-fix = 'format-docstring src/' +notebook-lint-fix = 'nbqa ruff --fix docs/docs/tutorials/' +py-lint-fix = 'ruff check --fix src/ tests/ docs/docs/tutorials/' +py-lint-fix-unsafe = 'ruff check --fix --unsafe-fixes src/ tests/ docs/docs/tutorials/' +py-format-fix = 'ruff format src/ tests/ docs/docs/tutorials/' +nonpy-format-fix = 'npx prettier --write --list-different --config=prettierrc.toml --ignore-unknown .' +nonpy-format-fix-modified = 'python tools/nonpy_prettier_modified.py --write' +success-message = 'echo "✅ All auto-formatting steps completed successfully!"' + +fix = { depends-on = [ + 'docstring-format-fix', + 'py-format-fix', + 'py-lint-fix', + 'nonpy-format-fix', + 'notebook-lint-fix', + 'success-message', +] } + +#################### +# 🧮 Code Complexity +#################### + +complexity-check = 'radon cc -s src/' +complexity-check-json = 'radon cc -s -j src/' +maintainability-check = 'radon mi src/' +maintainability-check-json = 'radon mi -j src/' +raw-metrics = 'radon raw -s src/' +raw-metrics-json = 'radon raw -s -j src/' + +############# +# 📊 Coverage +############# + +unit-tests-coverage = 'pixi run unit-tests --cov=src/easyapplication --cov-report=term-missing' +functional-tests-coverage = 'pixi run functional-tests --cov=src/easyapplication --cov-report=term-missing' +integration-tests-coverage = 'pixi run integration-tests --cov=src/easyapplication --cov-report=term-missing' +docstring-coverage = 'interrogate -c pyproject.toml src/easyapplication' + +cov = { depends-on = [ + 'docstring-coverage', + 'unit-tests-coverage', + 'integration-tests-coverage', +] } + +######################## +# 📓 Notebook Management +######################## + +notebook-convert = 'jupytext docs/docs/tutorials/*.py --from py:percent --to ipynb' +notebook-strip = 'nbstripout docs/docs/tutorials/*.ipynb' +notebook-tweak = 'python tools/tweak_notebooks.py docs/docs/tutorials/' +notebook-exec = 'python -m pytest --nbmake docs/docs/tutorials/ --nbmake-timeout=1200 --overwrite --color=yes -n auto -v' + +notebook-prepare = { depends-on = [ + #'notebook-convert', + 'notebook-strip', + #'notebook-tweak', +] } + +######################## +# 📚 Documentation Tasks +######################## + +docs-vars = "JUPYTER_PLATFORM_DIRS=1 PYTHONWARNINGS='ignore::RuntimeWarning'" +docs-pre = 'pixi run docs-vars python -m mkdocs' +docs-serve = 'pixi run docs-pre serve -f docs/mkdocs.yml' +docs-serve-dirty = 'pixi run docs-serve --dirty' +docs-build = 'pixi run docs-pre build -f docs/mkdocs.yml' +docs-build-local = 'pixi run docs-build --no-directory-urls' + +docs-deploy-pre = 'mike deploy -F docs/mkdocs.yml --push --branch gh-pages --update-aliases --alias-type redirect' +docs-set-default-pre = 'mike set-default -F docs/mkdocs.yml --push --branch gh-pages' + +docs-update-assets = 'python tools/update_docs_assets.py' + +############################## +# 📦 Template Management Tasks +############################## + +copier-copy = 'copier copy gh:easyscience/templates . --data-file .copier-answers.yml --data template_type=lib' +copier-recopy = 'copier recopy --data-file .copier-answers.yml --data template_type=lib' +copier-update = 'copier update --data-file .copier-answers.yml --data template_type=lib' + +##################### +# 🪝 Pre-commit Hooks +##################### + +pre-commit-clean = 'pre-commit clean' +pre-commit-install = 'pre-commit install --hook-type pre-commit --hook-type pre-push --overwrite' +pre-commit-uninstall = 'pre-commit uninstall --hook-type pre-commit --hook-type pre-push' +pre-commit-setup = { depends-on = [ + 'pre-commit-clean', + 'pre-commit-uninstall', + 'pre-commit-install', +] } + +################# +# 🐙️ GitHub Tasks +################# + +repo-wiki = 'gh api -X PATCH repos/easyscience/easyapp -f has_wiki=false' +repo-discussions = 'gh api -X PATCH repos/easyscience/easyapp -f has_discussions=true' +repo-description = "gh api -X PATCH repos/easyscience/easyapp -f description='Qt/QML components for building graphical applications'" +repo-homepage = "gh api -X PATCH repos/easyscience/easyapp -f homepage='https://easyscience.github.io/easyapp'" +repo-config = { depends-on = [ + 'repo-wiki', + 'repo-discussions', + 'repo-description', + 'repo-homepage', +] } + +master-protection = 'gh api -X POST repos/easyscience/easyapp/rulesets --input .github/configs/rulesets-master.json' +develop-protection = 'gh api -X POST repos/easyscience/easyapp/rulesets --input .github/configs/rulesets-develop.json' +gh-pages-protection = 'gh api -X POST repos/easyscience/easyapp/rulesets --input .github/configs/rulesets-gh-pages.json' +branch-protection = { depends-on = [ + 'master-protection', + 'develop-protection', + 'gh-pages-protection', +] } + +pages-deployment = 'gh api -X POST repos/easyscience/easyapp/pages --input .github/configs/pages-deployment.json' + +github-labels = 'python tools/update_github_labels.py' + +######################### +# ⚖️ SPDX License Headers +######################### + +license-remove = 'python tools/license_headers.py remove src/ tests/ --exclude-from-pyproject-toml tool.ruff.exclude' +license-add = 'python tools/license_headers.py add src/ tests/ --exclude-from-pyproject-toml tool.ruff.exclude' +license-check = 'python tools/license_headers.py check src/ tests/ --exclude-from-pyproject-toml tool.ruff.exclude' + +#################################### +# 🚀 Other Development & Build Tasks +#################################### + +default-build = 'python -m build' +dist-build = 'python -m build --wheel --outdir dist' + +npm-config = 'npm config set registry https://registry.npmjs.org/' +prettier-install = 'npm install --no-save --no-audit --no-fund prettier prettier-plugin-toml' + +clean-pycache = "find . -type d -name '__pycache__' -prune -exec rm -rf '{}' +" + +post-install = { depends-on = [ + 'npm-config', + 'prettier-install', + #'pre-commit-setup', +] } + +########################## +# 🔗 Main Package Shortcut +########################## +easyapplication = 'python -m easyapplication' \ No newline at end of file diff --git a/prettierrc.toml b/prettierrc.toml new file mode 100644 index 00000000..b98c86eb --- /dev/null +++ b/prettierrc.toml @@ -0,0 +1,22 @@ +plugins = [ + "prettier-plugin-toml", # use the TOML plugin +] + +endOfLine = 'lf' # change line endings to LF +proseWrap = 'always' # change wrapping in Markdown files +semi = false # remove semicolons +singleQuote = true # use single quotes instead of double quotes +tabWidth = 2 # change tab width to 2 spaces +useTabs = false # use spaces instead of tabs + +printWidth = 79 # wrap lines at 79 characters + +[[overrides]] +files = ["*.md"] +[overrides.options] +printWidth = 72 # wrap Markdown files at 72 characters + +[[overrides]] +files = ["*.yml", "*.yaml"] +[overrides.options] +printWidth = 88 # wrap YAML files at 88 characters diff --git a/pyproject.toml b/pyproject.toml index 7385333c..c7748ae1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,33 +1,338 @@ -[build-system] -requires = ['hatchling'] -build-backend = 'hatchling.build' +############################### +# Configuration for the project +############################### [project] -name = 'EasyApp' -version = '0.8.0' -description = 'Collection of graphical components to quickly create cross-platform applications in the EasyScience universe.' -authors = [ - {name = 'Andrew Sazonov', email = 'andrew.sazonov@ess.eu'} -] -license = {file = 'LICENSE'} +name = 'easyapplication' +dynamic = ['version'] # Use versioningit to manage the version +description = 'Qt/QML components for building graphical applications' +authors = [{ name = 'EasyScience contributors' }] +readme = 'README.md' +license = 'BSD-3-Clause' +license-files = ['LICENSE'] classifiers = [ - 'Development Status :: 4 - Beta', + 'Intended Audience :: Science/Research', + 'Topic :: Scientific/Engineering', 'License :: OSI Approved :: BSD License', - 'Programming Language :: Other', + 'Operating System :: OS Independent', + 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3', - 'Operating System :: MacOS', - 'Operating System :: Microsoft :: Windows', - 'Operating System :: POSIX :: Linux' + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', + 'Programming Language :: Python :: 3.14', ] requires-python = '>=3.11' dependencies = [ - 'PySide6' + #'easyscience', # The base library of the EasyScience framework + 'pooch', # Data downloader +] + +[project.optional-dependencies] +dev = [ + 'GitPython', # Interact with Git repositories + 'build', # Building the package + 'pre-commit', # Pre-commit hooks + 'jinja2', # Templating + 'nbmake', # Building notebooks + 'nbstripout', # Strip output from notebooks + 'nbqa', # Linting and formatting notebooks + 'pytest', # Testing + 'pytest-cov', # Test coverage + 'pytest-xdist', # Enable parallel testing + 'ruff', # Linting and formatting code + 'radon', # Code complexity and maintainability + 'validate-pyproject[all]', # Validate pyproject.toml + 'versioningit', # Automatic versioning from git tags + 'jupytext', # Jupyter notebook text format support + 'jupyterquiz', # Quizzes in Jupyter notebooks + 'pydoclint', # Docstring linter + 'format-docstring', # Docstring formatter + 'docstripy', # Convert docstrings to other formats + 'interrogate', # Docstring coverage checker + 'copier', # Template management + 'mike', # MkDocs: Versioned documentation support + 'mkdocs', # Static site generator + 'mkdocs-material', # Documentation framework on top of MkDocs + 'mkdocs-autorefs', # MkDocs: Auto-references support + 'mkdocs-jupyter', # MkDocs: Jupyter notebook support + 'mkdocs-plugin-inline-svg', # MkDocs: Inline SVG support + 'mkdocs-markdownextradata-plugin', # MkDocs: Markdown extra data support, such as global variables + 'mkdocstrings-python', # MkDocs: Python docstring support + 'pyyaml', # YAML parser + 'spdx-headers', # SPDX license header validation ] [project.urls] -homepage = 'https://app.easyscience.software' -source = 'https://github.com/EasyScience/EasyApp' -tracker = 'https://github.com/EasyScience/EasyApp/issues' +Documentation = 'https://easyscience.github.io/easyapp' +'Release Notes' = 'https://github.com/easyscience/easyapp/releases' +'Source Code' = 'https://github.com/easyscience/easyapp' +'Issue Tracker' = 'https://github.com/easyscience/easyapp/issues' + +############################ +# Build system configuration +############################ + +[build-system] +build-backend = 'hatchling.build' +requires = ['hatchling', 'versioningit'] + +############################# +# Configuration for hatchling +############################# + +# 'hatch' -- Build system for Python +# https://hatch.pypa.io/ [tool.hatch.build.targets.wheel] -packages = ['src/EasyApp'] +packages = ['src/easyapplication'] + +[tool.hatch.metadata] +allow-direct-references = true + +[tool.hatch.version] +source = 'versioningit' # Use versioningit to manage the version + +################################ +# Configuration for versioningit +################################ + +# 'versioningit' -- Versioning from git tags +# https://versioningit.readthedocs.io/ + +# Versioningit generates versions from git tags, so we don't need to +# either specify them statically in pyproject.toml or save them in the +# source code. Do not use {distance} in the version format, as it +# forces a version bump for every commit, which triggers unnecessary +# pixi.lock update without any changes to the source code. + +[tool.versioningit.format] +distance = '{base_version}+dev{distance}' # example: 1.2.3.post4+dev3 +dirty = '{base_version}+dirty{distance}' # example: 0.5.8+dirty3 +distance-dirty = '{base_version}+devdirty{distance}' # example: 0.5.8+devdirty3 + +# Configure how versioningit detects versions from Git +# - 'match' ensures it only considers tags starting with 'v' +# - 'default-tag' is used as a fallback when no matching tag is found +[tool.versioningit.vcs] +method = 'git' +match = ['v*'] +default-tag = 'v999.0.0' + +################################ +# Configuration for interrogate +################################ + +# 'interrogate' -- Docstring coverage checker +# https://interrogate.readthedocs.io/en/latest/ + +[tool.interrogate] +fail-under = 0 # Minimum docstring coverage percentage to pass +verbose = 1 +#exclude = ['src/**/__init__.py'] + +####################################### +# Configuration for coverage/pytest-cov +####################################### + +# 'coverage' -- Code coverage measurement tool +# https://coverage.readthedocs.io/en/latest/ + +[tool.coverage.run] +branch = true # Measure branch coverage as well +source = ['src'] # Limit coverage to the source code directory + +[tool.coverage.report] +show_missing = true # Show missing lines +skip_covered = false # Skip files with 100% coverage in the report +fail_under = 0 # Minimum coverage percentage to pass + +########################## +# Configuration for pytest +########################## + +# 'pytest' -- Testing framework +# https://docs.pytest.org/en/stable/ + +[tool.pytest.ini_options] +addopts = '--import-mode=importlib' +markers = ['fast: mark test as fast (should be run on every push)'] +testpaths = ['tests'] + +######################## +# Configuration for ruff +######################## + +# 'ruff' -- Python linter and code formatter +# https://docs.astral.sh/ruff/rules/ + +[tool.ruff] +exclude = [ + 'tmp', +] +indent-width = 4 +line-length = 99 # See also `max-line-length` in [tool.ruff.lint.pycodestyle] +preview = true # Enable new rules that are not yet stable, like DOC + +# Formatting options for Ruff + +[tool.ruff.format] +docstring-code-format = true # Whether to format code snippets in docstrings +docstring-code-line-length = 72 # Line length for code snippets in docstrings +indent-style = 'space' # PEP 8 recommends using spaces over tabs +quote-style = 'single' # But double quotes in docstrings (PEP 8, PEP 257) + +# Linting rules to use with Ruff + +[tool.ruff.lint] +select = [ + # Various rules + #'C90', # https://docs.astral.sh/ruff/rules/#mccabe-c90 + #'D', # https://docs.astral.sh/ruff/rules/#pydocstyle-d + 'F', # https://docs.astral.sh/ruff/rules/#pyflakes-f + #'FLY', # https://docs.astral.sh/ruff/rules/#flynt-fly + #'FURB', # https://docs.astral.sh/ruff/rules/#refurb-furb + 'I', # https://docs.astral.sh/ruff/rules/#isort-i + #'N', # https://docs.astral.sh/ruff/rules/#pep8-naming-n + #'NPY', # https://docs.astral.sh/ruff/rules/#numpy-specific-rules-npy + #'PGH', # https://docs.astral.sh/ruff/rules/#pygrep-hooks-pgh + #'PERF', # https://docs.astral.sh/ruff/rules/#perflint-perf + #'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf + #'TRY', # https://docs.astral.sh/ruff/rules/#tryceratops-try + #'UP', # https://docs.astral.sh/ruff/rules/#pyupgrade-up + # pycodestyle (E, W) rules + 'E', # https://docs.astral.sh/ruff/rules/#error-e + 'W', # https://docs.astral.sh/ruff/rules/#warning-w + # Pylint (PL) rules + #'PLC', # https://docs.astral.sh/ruff/rules/#convention-plc + #'PLE', # https://docs.astral.sh/ruff/rules/#error-ple + #'PLR', # https://docs.astral.sh/ruff/rules/#refactor-plr + #'PLW', # https://docs.astral.sh/ruff/rules/#warning-plw + # flake8 rules + #'A', # https://docs.astral.sh/ruff/rules/#flake8-builtins-a + #'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann + #'ARG', # https://docs.astral.sh/ruff/rules/#flake8-unused-arguments-arg + #'ASYNC', # https://docs.astral.sh/ruff/rules/#flake8-async-async + #'B', # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b + #'BLE', # https://docs.astral.sh/ruff/rules/#flake8-blind-except-ble + #'C4', # https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4 + #'COM', # https://docs.astral.sh/ruff/rules/#flake8-commas-com + #'DTZ', # https://docs.astral.sh/ruff/rules/#flake8-datetimez-dtz + #'EM', # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em + #'FA', # https://docs.astral.sh/ruff/rules/#flake8-future-annotations-fa + #'FBT', # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt + #'FIX', # https://docs.astral.sh/ruff/rules/#flake8-fixme-fix + #'G', # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g + #'ICN', # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn + #'INP', # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp + #'ISC', # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc + #'LOG', # https://docs.astral.sh/ruff/rules/#flake8-logging-log + #'PIE', # https://docs.astral.sh/ruff/rules/#flake8-pie-pie + #'PT', # https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt + #'PTH', # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth + #'PYI', # https://docs.astral.sh/ruff/rules/#flake8-pyi-pyi + #'RET', # https://docs.astral.sh/ruff/rules/#flake8-return-ret + #'RSE', # https://docs.astral.sh/ruff/rules/#flake8-raise-rse + 'S', # https://docs.astral.sh/ruff/rules/#flake8-bandit-s + #'SIM', # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim + #'SLF', # https://docs.astral.sh/ruff/rules/#flake8-self-slf + #'SLOT', # https://docs.astral.sh/ruff/rules/#flake8-slots-slot + #'T20', # https://docs.astral.sh/ruff/rules/#flake8-print-t20 + #'TC', # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tc + #'TD', # https://docs.astral.sh/ruff/rules/#flake8-todos-td + #'TID', # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid +] + +# Exceptions to the linting rules + +# Ignore specific rules globally +ignore = [ + 'COM812', # https://docs.astral.sh/ruff/rules/missing-trailing-comma/ + # The following is replaced by 'D'/[tool.ruff.lint.pydocstyle] and [tool.pydoclint] + 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc + # Disable, as [tool.format_docstring] split one-line docstrings into the canonical multi-line layout + 'D200', # https://docs.astral.sh/ruff/rules/unnecessary-multiline-docstring/ +] + +# Ignore specific rules in certain files or directories +[tool.ruff.lint.per-file-ignores] +'*/__init__.py' = [ + 'F401', # re-exports are intentional in __init__.py +] +'tests/**' = [ + 'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann + 'D', # https://docs.astral.sh/ruff/rules/#pydocstyle-d + 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc + 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ + 'S101', # https://docs.astral.sh/ruff/rules/assert/ +] +'docs/**' = [ + 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ + 'T201', # https://docs.astral.sh/ruff/rules/print/ +] + +# Specific options for certain rules + +[tool.ruff.lint.flake8-tidy-imports] +# Disallow all relative imports +ban-relative-imports = 'all' + +[tool.ruff.lint.isort] +# Forces all from imports to appear on their own line +force-single-line = true + +[tool.ruff.lint.mccabe] +# Cyclomatic complexity threshold (default is 10) +max-complexity = 10 + +[tool.ruff.lint.pycodestyle] +# PEP 8 line length guidance: +# https://peps.python.org/pep-0008/#maximum-line-length +# Use 99 characters as the project-wide maximum for regular code lines. +# Use 72 characters for docstrings. +max-line-length = 99 # See also `line-length` in [tool.ruff] +max-doc-length = 72 + +[tool.ruff.lint.pydocstyle] +convention = 'numpy' + +[tool.ruff.lint.pylint] +# Ruff counts `self`/`cls` in max-args; traditional pylint does not. +# Setting 6 here matches pylint's default of 5 (excluding self). +max-args = 6 +max-positional-args = 6 + +############################# +# Configuration for pydoclint +############################# + +# 'pydoclint' -- Docstring linter, a faster alternative to +# 'darglint' or 'darglint2'. +# https://pypi.org/project/pydoclint/ + +# This is a more advanced docstring linter compared to Ruff's built-in +# docstring check rules D or DOC. For example, among many other things, +# it can check that arguments in the docstring, which are used by MkDocs +# and IDEs to render parameter documentation, remain synchronized with +# the parameter declarations in the code (in function's signature). + +[tool.pydoclint] +exclude = '\.' # Temporarily disable pydoclint until we are ready +style = 'numpy' +check-style-mismatch = true +check-arg-defaults = true +allow-init-docstring = true + +#################################### +# Configuration for format-docstring +#################################### + +# 'format-docstring' -- Code formatter for docstrings +# https://github.com/jsh9/format-docstring + +[tool.format_docstring] +exclude = '\.' # Temporarily disable format-docstring until we are ready +docstring_style = 'numpy' +line_length = 72 +fix_rst_backticks = true +verbose = 'default' diff --git a/src/easyapplication/__init__.py b/src/easyapplication/__init__.py new file mode 100644 index 00000000..e8208c23 --- /dev/null +++ b/src/easyapplication/__init__.py @@ -0,0 +1,3 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""EasyApplication library.""" diff --git a/tests/functional/test_dummy.py b/tests/functional/test_dummy.py new file mode 100644 index 00000000..32360eee --- /dev/null +++ b/tests/functional/test_dummy.py @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2021-2026 EasyApplication contributors +# SPDX-License-Identifier: BSD-3-Clause + +def test_dummy(): + calculated = 2 + 2 + expected = 4 + assert calculated == expected diff --git a/tests/integration/fitting/test_dummy.py b/tests/integration/fitting/test_dummy.py new file mode 100644 index 00000000..78ed65c9 --- /dev/null +++ b/tests/integration/fitting/test_dummy.py @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2021-2026 EasyApplication contributors +# SPDX-License-Identifier: BSD-3-Clause + +import pytest + + +@pytest.mark.fast +def test_dummy_fast(): + calculated = 2 + 2 + expected = 4 + assert calculated == expected + + +def test_dummy_slow(): + calculated = sum(i * j for i in range(10000) for j in range(10000)) + expected = 2499500025000000 + assert calculated == expected \ No newline at end of file diff --git a/tests/integration/scipp-analysis/test_dummy.py b/tests/integration/scipp-analysis/test_dummy.py new file mode 100644 index 00000000..78ed65c9 --- /dev/null +++ b/tests/integration/scipp-analysis/test_dummy.py @@ -0,0 +1,17 @@ +# SPDX-FileCopyrightText: 2021-2026 EasyApplication contributors +# SPDX-License-Identifier: BSD-3-Clause + +import pytest + + +@pytest.mark.fast +def test_dummy_fast(): + calculated = 2 + 2 + expected = 4 + assert calculated == expected + + +def test_dummy_slow(): + calculated = sum(i * j for i in range(10000) for j in range(10000)) + expected = 2499500025000000 + assert calculated == expected \ No newline at end of file diff --git a/tests/unit/test_dummy.py b/tests/unit/test_dummy.py new file mode 100644 index 00000000..32360eee --- /dev/null +++ b/tests/unit/test_dummy.py @@ -0,0 +1,7 @@ +# SPDX-FileCopyrightText: 2021-2026 EasyApplication contributors +# SPDX-License-Identifier: BSD-3-Clause + +def test_dummy(): + calculated = 2 + 2 + expected = 4 + assert calculated == expected diff --git a/tools/license_headers.py b/tools/license_headers.py new file mode 100644 index 00000000..f276ca1b --- /dev/null +++ b/tools/license_headers.py @@ -0,0 +1,321 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Add, remove, or check SPDX headers in Python files.""" + +from __future__ import annotations + +import argparse +import fnmatch +import tomllib +from datetime import datetime +from pathlib import Path +from typing import Any +from typing import Optional +from typing import Union + +from git import Repo +from spdx_headers.core import find_repository_root +from spdx_headers.core import get_copyright_info +from spdx_headers.core import has_spdx_header +from spdx_headers.data import load_license_data +from spdx_headers.operations import add_header_to_single_file +from spdx_headers.operations import remove_header_from_single_file + +LICENSE_DATABASE = load_license_data() + + +def load_pyproject(repo_path: Union[str, Path]) -> dict[str, Any]: + """ + Load and return parsed ``pyproject.toml`` data for the repository. + """ + repo_root = find_repository_root(repo_path) + pyproject_path = repo_root / 'pyproject.toml' + + with pyproject_path.open('rb') as file_handle: + return tomllib.load(file_handle) + + +def get_pyproject_value(pyproject_data: dict[str, Any], dotted_key: str) -> Any: + """Return a nested ``pyproject.toml`` value from a dotted key.""" + value: Any = pyproject_data + for part in dotted_key.split('.'): + if not isinstance(value, dict) or part not in value: + raise KeyError(dotted_key) + value = value[part] + return value + + +def normalize_pattern(pattern: str) -> str: + """Normalize an exclude pattern to a POSIX-style relative path.""" + normalized = Path(pattern).as_posix() + if normalized.startswith('./'): + normalized = normalized[2:] + return normalized.rstrip('/') + + +def get_exclude_patterns( + repo_path: Union[str, Path], + exclude_values: list[str], + exclude_from_pyproject_toml: Optional[str], +) -> list[str]: + """ + Return normalized exclude patterns from CLI and ``pyproject.toml``. + """ + pyproject_data = load_pyproject(repo_path) + patterns: list[str] = [] + + if exclude_from_pyproject_toml: + value = get_pyproject_value(pyproject_data, exclude_from_pyproject_toml) + if not isinstance(value, list) or not all(isinstance(item, str) for item in value): + raise ValueError( + f'{exclude_from_pyproject_toml} in pyproject.toml must be a list of strings.', + ) + patterns.extend(value) + + for item in exclude_values: + try: + value = get_pyproject_value(pyproject_data, item) + except KeyError: + patterns.append(item) + continue + + if not isinstance(value, list) or not all(isinstance(entry, str) for entry in value): + raise ValueError(f'{item} in pyproject.toml must be a list of strings.') + patterns.extend(value) + + normalized_patterns: list[str] = [] + seen: set[str] = set() + for pattern in patterns: + normalized = normalize_pattern(pattern) + if normalized and normalized not in seen: + normalized_patterns.append(normalized) + seen.add(normalized) + + return normalized_patterns + + +def get_file_creation_year(file_path: Union[str, Path]) -> str: + """Return the year the file was first added to Git history. + + If the year cannot be determined, fall back to the current year. + """ + file_path = Path(file_path) + + repo = Repo(file_path, search_parent_directories=True) + root = Path(repo.working_tree_dir).resolve() + rel_path = file_path.resolve().relative_to(root) + + rel_path_git = rel_path.as_posix() + + log_output = repo.git.log( + '--follow', + '--diff-filter=A', + '--reverse', + '--format=%ad', + '--date=format:%Y', + '--', + rel_path_git, + ).strip() + + year = log_output.splitlines()[0].strip() if log_output else '' + + return year or str(datetime.now().year) + + +def get_org_url(repo_path: Union[str, Path]) -> str: + """ + Return the organization URL derived from the repository source URL. + """ + pyproject_data = load_pyproject(repo_path) + repo_url = pyproject_data['project']['urls']['Source Code'] + return repo_url.rsplit('/', 1)[0] + + +def get_project_license(repo_path: Union[str, Path]) -> str: + """Return the project license value from ``pyproject.toml``.""" + pyproject_data = load_pyproject(repo_path) + return pyproject_data['project']['license'] + + +def get_copyright_holder(repo_path: Union[str, Path]) -> str: + """Return the repository copyright holder name.""" + _, name, _ = get_copyright_info(repo_path) + return name + + +def add_spdx_header( + target_file: Union[str, Path], + *, + license_key: str, + copyright_holder: str, + org_url: str, +) -> None: + """Add SPDX headers to one file.""" + year = get_file_creation_year(target_file) + + add_header_to_single_file( + filepath=target_file, + license_key=license_key, + license_data=LICENSE_DATABASE, + year=year, + name=copyright_holder, + email=org_url, + ) + + +def is_excluded(relative_path: str, exclude_patterns: list[str]) -> bool: + """Return whether a relative path should be excluded.""" + for pattern in exclude_patterns: + if fnmatch.fnmatch(relative_path, pattern): + return True + if relative_path == pattern: + return True + if relative_path.startswith(f'{pattern}/'): + return True + return False + + +def iter_python_files( + paths: list[str], + *, + repo_root: Path, + exclude_patterns: list[str], + parser: argparse.ArgumentParser, +) -> list[Path]: + """Collect Python files under the given paths after exclusions.""" + files: list[Path] = [] + seen: set[Path] = set() + + for base_dir in paths: + base_path = Path(base_dir) + if not base_path.exists(): + parser.error(f'Path does not exist: {base_dir}') + + if base_path.is_file(): + candidates = [base_path] if base_path.suffix == '.py' else [] + else: + candidates = sorted(base_path.rglob('*.py')) + + for py_file in candidates: + resolved = py_file.resolve() + try: + relative_path = resolved.relative_to(repo_root).as_posix() + except ValueError: + relative_path = py_file.as_posix() + + if is_excluded(relative_path, exclude_patterns): + continue + + if resolved not in seen: + files.append(py_file) + seen.add(resolved) + + return files + + +def run_add( + files: list[Path], + *, + license_key: str, + copyright_holder: str, + org_url: str, +) -> int: + """Add SPDX headers to all selected files.""" + for py_file in files: + add_spdx_header( + py_file, + license_key=license_key, + copyright_holder=copyright_holder, + org_url=org_url, + ) + return 0 + + +def run_remove(files: list[Path]) -> int: + """Remove SPDX headers from all selected files.""" + for py_file in files: + remove_header_from_single_file(py_file) + return 0 + + +def run_check(files: list[Path]) -> int: + """Check SPDX headers in all selected files.""" + missing_files = [py_file for py_file in files if not has_spdx_header(py_file)] + + if not missing_files: + print('✓ All Python files have valid SPDX headers.') + return 0 + + print('✗ The following files are missing SPDX headers:') + for py_file in missing_files: + print(f' - {py_file.as_posix()}') + print(f'\nFound {len(missing_files)} files without SPDX headers.') + return 1 + + +def build_parser() -> argparse.ArgumentParser: + """Build the CLI argument parser.""" + parser = argparse.ArgumentParser( + description='Add, remove, or check SPDX headers in Python files.', + ) + subparsers = parser.add_subparsers(dest='command', required=True) + + for command_name in ('check', 'remove', 'add'): + command_parser = subparsers.add_parser(command_name) + command_parser.add_argument( + 'paths', + nargs='+', + help='Relative paths to scan (e.g. src tests)', + ) + command_parser.add_argument( + '--exclude', + nargs='*', + default=[], + help='Exclude paths, glob patterns, or pyproject dotted keys.', + ) + command_parser.add_argument( + '--exclude-from-pyproject-toml', + help='Read exclude patterns from a dotted key in pyproject.toml.', + ) + + return parser + + +def main(argv: Optional[list[str]] = None) -> int: + """Run the SPDX header CLI.""" + parser = build_parser() + args = parser.parse_args(argv) + + repo_path = Path('.').resolve() + repo_root = find_repository_root(repo_path).resolve() + exclude_patterns = get_exclude_patterns( + repo_path, + args.exclude, + args.exclude_from_pyproject_toml, + ) + files = iter_python_files( + args.paths, + repo_root=repo_root, + exclude_patterns=exclude_patterns, + parser=parser, + ) + + if args.command == 'check': + return run_check(files) + + if args.command == 'remove': + return run_remove(files) + + license_key = get_project_license(repo_path) + copyright_holder = get_copyright_holder(repo_path) + org_url = get_org_url(repo_path) + return run_add( + files, + license_key=license_key, + copyright_holder=copyright_holder, + org_url=org_url, + ) + + +if __name__ == '__main__': + raise SystemExit(main()) diff --git a/tools/test_scripts.py b/tools/test_scripts.py new file mode 100644 index 00000000..e24b28e2 --- /dev/null +++ b/tools/test_scripts.py @@ -0,0 +1,57 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Test runner for tutorial scripts in the 'tutorials' directory. + +This test discovers and executes all Python scripts located under the +'tutorials' directory to ensure they run without errors. + +Important: each script must be executed in a fresh Python process. +Running many tutorials in-process (e.g. via runpy) leaks global state +between scripts (notably cached calculator dictionaries keyed only by +model/experiment names), which can cause false failures. +""" + +import os +import subprocess # noqa: S404 +import sys +from pathlib import Path + +import pytest + +_repo_root = Path(__file__).resolve().parents[1] +_src_root = _repo_root / 'src' + +# Discover tutorial scripts, excluding temporary checkpoint files +TUTORIALS = [ + p for p in Path('docs/docs/tutorials').rglob('*.py') if '.ipynb_checkpoints' not in p.parts +] + + +@pytest.mark.parametrize('script_path', TUTORIALS) +def test_script_runs(script_path: Path): + """Execute a tutorial script and fail if it raises an exception. + + Each script is run in the context of __main__ to mimic standalone + execution. + """ + env = os.environ.copy() + if _src_root.exists(): + existing = env.get('PYTHONPATH', '') + env['PYTHONPATH'] = ( + str(_src_root) if not existing else str(_src_root) + os.pathsep + existing + ) + + # This is a test harness executing repo-local tutorial scripts. + # We intentionally use subprocess isolation to prevent cross-test + # global state leaks (e.g. calculator caches) that can cause false + # failures when running tutorials in a shared interpreter. + result = subprocess.run( # noqa: S603 + [sys.executable, str(script_path)], + cwd=str(_repo_root), + env=env, + capture_output=True, + text=True, + ) + if result.returncode != 0: + details = (result.stdout or '') + (result.stderr or '') + pytest.fail(f'{script_path}\n{details.strip()}') diff --git a/tools/update_docs_assets.py b/tools/update_docs_assets.py new file mode 100644 index 00000000..f4fe7f36 --- /dev/null +++ b/tools/update_docs_assets.py @@ -0,0 +1,91 @@ +""" +Update documentation assets from the assets-branding repository. + +This script fetches branding assets (logos, icons, images) from the +easyscience/assets-branding GitHub repository and copies them to the +appropriate locations in the documentation directory. +""" + +import shutil +from pathlib import Path + +import pooch + +# Configuration: Define what to fetch and where to copy +GITHUB_REPO = 'easyscience/assets-branding' +GITHUB_BRANCH = 'master' +BASE_URL = f'https://raw.githubusercontent.com/{GITHUB_REPO}/refs/heads/{GITHUB_BRANCH}' +PROJECT_NAME = 'easyapplication' + +# Mapping of source files to destination paths +# Format: "source_path_in_repo": "destination_path_in_project" +ASSETS_MAP = { + # Logos + f'{PROJECT_NAME}/logos/dark.svg': 'docs/docs/assets/images/logo_dark.svg', + f'{PROJECT_NAME}/logos/light.svg': 'docs/docs/assets/images/logo_light.svg', + # Favicon + f'{PROJECT_NAME}/icons/color.png': 'docs/docs/assets/images/favicon.png', + # Icon overrides + f'{PROJECT_NAME}/icons/bw.svg': f'docs/overrides/.icons/{PROJECT_NAME}.svg', + 'easyscience-org/icons/eso-icon_bw.svg': 'docs/overrides/.icons/easyscience.svg', +} + + +def fetch_and_copy_asset( + source_path: str, + dest_path: str, + cache_dir: Path, +) -> None: + """ + Fetch an asset from GitHub and copy it to the destination. + + Args: + source_path: Path to the file in the GitHub repository + dest_path: Destination path in the project + cache_dir: Directory to cache downloaded files + """ + url = f'{BASE_URL}/{source_path}' + + # Create a unique cache filename based on source path + cache_filename = source_path.replace('/', '_') + + # Download file using pooch + file_path = pooch.retrieve( + url=url, + known_hash=None, # Skip hash verification + path=cache_dir, + fname=cache_filename, + ) + + # Create destination directory if it doesn't exist + dest = Path(dest_path) + dest.parent.mkdir(parents=True, exist_ok=True) + + # Copy the file to destination + shutil.copy2(file_path, dest) + print(f'Copied {file_path} -> {dest_path}') + + +def main(): + """Main function to update all documentation assets.""" + print('📥 Updating documentation assets...') + print(f' Repository: {GITHUB_REPO}') + print(f' Branch: {GITHUB_BRANCH}\n') + + # Use a temporary cache directory + cache_dir = Path.home() / '.cache' / GITHUB_REPO + cache_dir.mkdir(parents=True, exist_ok=True) + + # Fetch and copy each asset + for source_path, dest_path in ASSETS_MAP.items(): + try: + fetch_and_copy_asset(source_path, dest_path, cache_dir) + print() + except Exception as e: + print(f'❌ Failed to fetch {source_path}: {e}') + + print('\n✅ Documentation assets updated successfully!') + + +if __name__ == '__main__': + main() diff --git a/tools/update_github_labels.py b/tools/update_github_labels.py new file mode 100644 index 00000000..84de575e --- /dev/null +++ b/tools/update_github_labels.py @@ -0,0 +1,341 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause +"""Set/update GitHub labels for current or specified easyscience +repository. + +Requires: + - gh CLI installed + - gh auth login completed + +Usage: + python update_github_labels.py + python update_github_labels.py --dry-run + python update_github_labels.py --repo easyscience/my-repo + python update_github_labels.py --repo easyscience/my-repo --dry-run +""" + +from __future__ import annotations + +import argparse +import json +import shlex +import subprocess # noqa: S404 +import sys +from dataclasses import dataclass + +EASYSCIENCE_ORG = 'easyscience' + + +# Data structures + + +@dataclass(frozen=True) +class Label: + """A GitHub label with name, color, and description.""" + + name: str + color: str + description: str = '' + + +@dataclass(frozen=True) +class LabelRename: + """Mapping from old label name to new label name.""" + + old: str + new: str + + +class Colors: + """Hex color codes for label groups.""" + + SCOPE = 'd73a4a' + MAINTAINER = '0e8a16' + PRIORITY = 'fbca04' + BOT = '5319e7' + + +LABEL_RENAMES = [ + # Default GitHub labels to rename (if they exist) + LabelRename('bug', '[scope] bug'), + LabelRename('documentation', '[scope] documentation'), + LabelRename('duplicate', '[maintainer] duplicate'), + LabelRename('enhancement', '[scope] enhancement'), + LabelRename('good first issue', '[maintainer] good first issue'), + LabelRename('help wanted', '[maintainer] help wanted'), + LabelRename('invalid', '[maintainer] invalid'), + LabelRename('question', '[maintainer] question'), + LabelRename('wontfix', '[maintainer] wontfix'), + # Custom label renames (if they exist) + LabelRename('[bot] pull request', '[bot] release'), +] + +LABELS = [ + # Scope labels + Label( + '[scope] bug', + Colors.SCOPE, + 'Bug report or fix (major.minor.PATCH)', + ), + Label( + '[scope] documentation', + Colors.SCOPE, + 'Documentation only changes (major.minor.patch.POST)', + ), + Label( + '[scope] enhancement', + Colors.SCOPE, + 'Adds/improves features (major.MINOR.patch)', + ), + Label( + '[scope] maintenance', + Colors.SCOPE, + 'Code/tooling cleanup, no feature or bugfix (major.minor.PATCH)', + ), + Label( + '[scope] significant', + Colors.SCOPE, + 'Breaking or major changes (MAJOR.minor.patch)', + ), + Label( + '[scope] ⚠️ label needed', + Colors.SCOPE, + 'Automatically added to issues and PRs without a [scope] label', + ), + # Maintainer labels + Label( + '[maintainer] duplicate', + Colors.MAINTAINER, + 'Already reported or submitted', + ), + Label( + '[maintainer] good first issue', + Colors.MAINTAINER, + 'Good entry-level issue for newcomers', + ), + Label( + '[maintainer] help wanted', + Colors.MAINTAINER, + 'Needs additional help to resolve or implement', + ), + Label( + '[maintainer] invalid', + Colors.MAINTAINER, + 'Invalid, incorrect or outdated', + ), + Label( + '[maintainer] question', + Colors.MAINTAINER, + 'Needs clarification, discussion, or more information', + ), + Label( + '[maintainer] wontfix', + Colors.MAINTAINER, + 'Will not be fixed or continued', + ), + # Priority labels + Label( + '[priority] lowest', + Colors.PRIORITY, + 'Very low urgency', + ), + Label( + '[priority] low', + Colors.PRIORITY, + 'Low importance', + ), + Label( + '[priority] medium', + Colors.PRIORITY, + 'Normal/default priority', + ), + Label( + '[priority] high', + Colors.PRIORITY, + 'Should be prioritized soon', + ), + Label( + '[priority] highest', + Colors.PRIORITY, + 'Urgent. Needs attention ASAP', + ), + Label( + '[priority] ⚠️ label needed', + Colors.PRIORITY, + 'Automatically added to issues without a [priority] label', + ), + # Bot label + Label( + '[bot] release', + Colors.BOT, + 'Automated release PR. Excluded from changelog/versioning', + ), + Label( + '[bot] backmerge', + Colors.BOT, + 'Automated backmerge master → develop failed due to conflicts', + ), +] + + +# Helpers + + +@dataclass(frozen=True) +class CmdResult: + """Result of a shell command execution.""" + + returncode: int + stdout: str + stderr: str + + +def run_cmd( + args: list[str], + *, + dry_run: bool, + check: bool = True, +) -> CmdResult: + """Run a command (or print it in dry-run mode).""" + cmd_str = ' '.join(shlex.quote(a) for a in args) + + if dry_run: + print(f' [dry-run] {cmd_str}') + return CmdResult(0, '', '') + + proc = subprocess.run( + args=args, + text=True, + capture_output=True, + ) + result = CmdResult( + proc.returncode, + proc.stdout.strip(), + proc.stderr.strip(), + ) + + if check and proc.returncode != 0: + raise RuntimeError(f'Command failed ({proc.returncode}): {cmd_str}\n{result.stderr}') + + return result + + +def get_current_repo() -> str: + """Get the current repository name in 'owner/repo' format.""" + result = subprocess.run( + args=[ + 'gh', + 'repo', + 'view', + '--json', + 'nameWithOwner', + ], + text=True, + capture_output=True, + check=True, + ) + data = json.loads(result.stdout) + name_with_owner = data.get('nameWithOwner', '') + + if '/' not in name_with_owner: + raise RuntimeError('Could not determine current repository name') + + return name_with_owner + + +def rename_label( + repo: str, + rename: LabelRename, + *, + dry_run: bool, +) -> None: + """Rename a label, silently skipping if it doesn't exist.""" + result = run_cmd( + args=[ + 'gh', + 'label', + 'edit', + rename.old, + '--name', + rename.new, + '--repo', + repo, + ], + dry_run=dry_run, + check=False, + ) + + if dry_run or result.returncode == 0: + print(f' Rename: {rename.old!r} → {rename.new!r}') + else: + print(f' Skip (not found): {rename.old!r}') + + +def upsert_label( + repo: str, + label: Label, + *, + dry_run: bool, +) -> None: + """Create or update a label.""" + run_cmd( + [ + 'gh', + 'label', + 'create', + label.name, + '--color', + label.color, + '--description', + label.description, + '--force', + '--repo', + repo, + ], + dry_run=dry_run, + ) + print(f' Upsert: {label.name!r}') + + +# Main + + +def main() -> int: + """Entry point: parse arguments and sync labels.""" + parser = argparse.ArgumentParser(description='Sync GitHub labels for easyscience repos') + parser.add_argument( + '--repo', + help='Target repository (owner/name)', + ) + parser.add_argument( + '--dry-run', + action='store_true', + help='Print actions without applying changes', + ) + args = parser.parse_args() + + repo = args.repo or get_current_repo() + org = repo.split('/')[0] + + if org.lower() != EASYSCIENCE_ORG: + print(f"Error: repository '{repo}' is not under '{EASYSCIENCE_ORG}'", file=sys.stderr) + return 2 + + print(f'Repository: {repo}') + if args.dry_run: + print('Mode: DRY-RUN (no changes will be made)\n') + + print('\nRenaming default labels...') + for rename in LABEL_RENAMES: + rename_label(repo, rename, dry_run=args.dry_run) + + print('\nUpserting labels...') + for label in LABELS: + upsert_label(repo, label, dry_run=args.dry_run) + + print('\nDone.') + return 0 + + +if __name__ == '__main__': + raise SystemExit(main()) From c2f48ee7b464ab1124ef8d383c0f7a9a97aee60c Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 29 Apr 2026 15:46:28 +0200 Subject: [PATCH 2/7] Reorganize structure --- resources/images/ea_logo.svg | 21 ----------- resources/images/ea_logo_wfont.svg | 34 ------------------ resources/images/vscode_debug.jpg | Bin 50517 -> 0 bytes src/EasyApp/Logic/Utils/__init__.py | 1 - src/EasyApp/Logic/__init__.py | 2 -- src/EasyApp/__init__.py | 7 ---- .../Gui/Animations/ColorReset.qml | 0 .../Gui/Animations/ThemeChange.qml | 0 .../Gui/Animations/TranslationChange.qml | 0 .../Gui/Animations/qmldir | 0 .../Charts/ChartTemplateHeatmap2dPlotly.html | 0 .../Charts/ChartTemplateSimple1dPlotly.html | 0 .../Gui/Charts/ChartViewHeatmap2dPlotly.qml | 0 .../Gui/Charts/ChartViewSimple1dPlotly.qml | 0 .../Gui/Charts/Plotly1dBarPlot.qml | 0 .../Gui/Charts/Plotly1dLine.qml | 0 .../Gui/Charts/Plotly1dMeasVsCalc.qml | 0 .../Gui/Charts/Plotly2dHeatmap.qml | 0 .../Gui/Charts/Plotly2dPolarHeatmap.qml | 0 .../Gui/Charts/Plotly3dScatter.qml | 0 .../Gui/Charts/Plotly3dSurface.qml | 0 .../Gui/Charts/QtCharts1dBase 2.qml | 0 .../Gui/Charts/QtCharts1dBase.qml | 0 .../Gui/Charts/QtCharts1dMeasVsCalc.qml | 0 .../Gui/Charts/QtCharts1dValueAxis.qml | 0 .../Gui/Charts/qmldir | 0 .../Gui/Components/AboutDialog.qml | 0 .../Gui/Components/AppBarCentralTabs.qml | 0 .../Gui/Components/AppBarLeftButtons.qml | 0 .../Gui/Components/AppBarRightButtons.qml | 0 .../Gui/Components/ApplicationWindow.qml | 0 .../Gui/Components/BasicReport.qml | 0 .../Gui/Components/ContentArea.qml | 0 .../Gui/Components/ContentPage.qml | 0 .../Gui/Components/GuideWindow.qml | 0 .../Gui/Components/GuideWindowContainer.qml | 0 .../Gui/Components/JsonListModel.qml | 0 .../Gui/Components/MainContent.qml | 0 .../Gui/Components/PreferencesDialog.qml | 0 .../Components/ProjectDescriptionDialog.qml | 0 .../Gui/Components/SideBar.qml | 0 .../Gui/Components/SideBarColumn.qml | 0 .../Gui/Components/TableView.qml | 0 .../Gui/Components/TableViewAdvancedLabel.qml | 0 .../Gui/Components/TableViewButton.qml | 0 .../Gui/Components/TableViewCheckBox.qml | 0 .../Gui/Components/TableViewComboBox.qml | 0 .../Gui/Components/TableViewDelegate.qml | 0 .../Gui/Components/TableViewHeader.qml | 0 .../Gui/Components/TableViewLabel.qml | 0 .../Gui/Components/TableViewLabelControl.qml | 0 .../Gui/Components/TableViewParameter.qml | 0 .../Gui/Components/TableViewTextInput.qml | 0 .../TableViewTwoRowsAdvancedLabel.qml | 0 .../Gui/Components/qmldir | 0 .../Gui/Elements/AppBarTabButton.qml | 0 .../Gui/Elements/ApplicationWindow.qml | 0 .../Gui/Elements/Button.qml | 0 .../Gui/Elements/CheckBox.qml | 0 .../Gui/Elements/CheckIndicator.qml | 0 .../Gui/Elements/ComboBox.qml | 0 .../Gui/Elements/CursorDelegate.qml | 0 .../Gui/Elements/Dialog.qml | 0 .../Gui/Elements/DialogButtonBox.qml | 0 .../Gui/Elements/GroupBox.qml | 0 .../Gui/Elements/GroupButton.qml | 0 .../Gui/Elements/GroupColumn.qml | 0 .../Gui/Elements/GroupRow.qml | 0 .../Gui/Elements/Label.qml | 0 .../Gui/Elements/LinkedImage.qml | 0 .../Gui/Elements/Menu.qml | 0 .../Gui/Elements/MenuItem.qml | 0 .../Gui/Elements/ParamComboBox.qml | 0 .../Gui/Elements/ParamTextField.qml | 0 .../Gui/Elements/Parameter.qml | 0 .../Gui/Elements/RadioButton.qml | 0 .../Gui/Elements/RadioIndicator.qml | 0 .../Gui/Elements/RemoteController.qml | 0 .../Gui/Elements/RemotePointer.qml | 0 .../Gui/Elements/RunningLabel.qml | 0 .../Gui/Elements/ScrollBar.qml | 0 .../Gui/Elements/ScrollIndicator.qml | 0 .../Gui/Elements/SideBarButton.qml | 0 .../Gui/Elements/Slider.qml | 0 .../Gui/Elements/SliderHandle.qml | 0 .../Gui/Elements/SpinBox.qml | 0 .../Gui/Elements/SplashScreen.qml | 0 .../Gui/Elements/StatusBar.qml | 0 .../Gui/Elements/StatusBarItem.qml | 0 .../Gui/Elements/TabBar.qml | 0 .../Gui/Elements/TabButton.qml | 0 .../Gui/Elements/TextArea.qml | 0 .../Gui/Elements/TextField.qml | 0 .../Gui/Elements/TextInput.qml | 0 .../Gui/Elements/ToolButton.qml | 0 .../Gui/Elements/ToolTip.qml | 0 .../Gui/Elements/ToolTipShadow.qml | 0 .../Gui/Elements/qmldir | 0 .../Gui/Globals/Vars.qml | 0 .../Gui/Globals/qmldir | 0 .../Gui/Html/BasicReport.html | 0 .../Gui/Html/BlankPage.html | 0 .../Gui/Html/PTSans-Bold.ttf | Bin .../Gui/Html/PTSans-Regular.ttf | Bin .../Gui/Html/Plotly1dBarPlot.html | 0 .../Gui/Html/Plotly1dLine.html | 0 .../Gui/Html/Plotly1dMeasVsCalc.html | 0 .../Gui/Html/Plotly2dHeatmap.html | 0 .../Gui/Html/Plotly2dPolarHeatmap.html | 0 .../Gui/Html/Plotly3dMesh.html | 0 .../Gui/Html/Plotly3dScatter.html | 0 .../Gui/Html/Plotly3dSurface.html | 0 .../Gui/Html/plotly-2.18.0.min.js | 0 .../Gui/Logic/Plotting.js | 0 .../Gui/Logic/ProjectConfig.js | 0 .../Gui/Logic/Translate.js | 0 .../Gui/Logic/Utils.js | 0 .../Gui/Logic/qmldir | 0 .../Fonts/Encode_Sans/EncodeSans-Black.ttf | Bin .../Fonts/Encode_Sans/EncodeSans-Bold.ttf | Bin .../Encode_Sans/EncodeSans-ExtraBold.ttf | Bin .../Encode_Sans/EncodeSans-ExtraLight.ttf | Bin .../Fonts/Encode_Sans/EncodeSans-Light.ttf | Bin .../Fonts/Encode_Sans/EncodeSans-Medium.ttf | Bin .../Fonts/Encode_Sans/EncodeSans-Regular.ttf | Bin .../Fonts/Encode_Sans/EncodeSans-SemiBold.ttf | Bin .../Fonts/Encode_Sans/EncodeSans-Thin.ttf | Bin .../Gui/Resources/Fonts/Encode_Sans/OFL.txt | 0 .../Resources/Fonts/Encode_Sans/download.txt | 0 .../EncodeSansCondensed-Black.ttf | Bin .../EncodeSansCondensed-Bold.ttf | Bin .../EncodeSansCondensed-ExtraBold.ttf | Bin .../EncodeSansCondensed-ExtraLight.ttf | Bin .../EncodeSansCondensed-Light.ttf | Bin .../EncodeSansCondensed-Medium.ttf | Bin .../EncodeSansCondensed-Regular.ttf | Bin .../EncodeSansCondensed-SemiBold.ttf | Bin .../EncodeSansCondensed-Thin.ttf | Bin .../Fonts/Encode_Sans_Condensed/OFL.txt | 0 .../Fonts/Encode_Sans_Condensed/download.txt | 0 .../EncodeSansExpanded-Black.ttf | Bin .../EncodeSansExpanded-Bold.ttf | Bin .../EncodeSansExpanded-ExtraBold.ttf | Bin .../EncodeSansExpanded-ExtraLight.ttf | Bin .../EncodeSansExpanded-Light.ttf | Bin .../EncodeSansExpanded-Medium.ttf | Bin .../EncodeSansExpanded-Regular.ttf | Bin .../EncodeSansExpanded-SemiBold.ttf | Bin .../EncodeSansExpanded-Thin.ttf | Bin .../Fonts/Encode_Sans_Expanded/OFL.txt | 0 .../Fonts/Encode_Sans_Expanded/download.txt | 0 .../Font Awesome 5 Free-Solid-900.otf | Bin .../Font Awesome 6 Free-Solid-900.otf | Bin .../Font Awesome 7 Free-Solid-900.otf | Bin .../Resources/Fonts/FontAwesome/LICENSE.txt | 0 .../Resources/Fonts/Nunito/Nunito-Black.ttf | Bin .../Fonts/Nunito/Nunito-BlackItalic.ttf | Bin .../Resources/Fonts/Nunito/Nunito-Bold.ttf | Bin .../Fonts/Nunito/Nunito-BoldItalic.ttf | Bin .../Fonts/Nunito/Nunito-ExtraBold.ttf | Bin .../Fonts/Nunito/Nunito-ExtraBoldItalic.ttf | Bin .../Fonts/Nunito/Nunito-ExtraLight.ttf | Bin .../Fonts/Nunito/Nunito-ExtraLightItalic.ttf | Bin .../Resources/Fonts/Nunito/Nunito-Italic.ttf | Bin .../Resources/Fonts/Nunito/Nunito-Light.ttf | Bin .../Fonts/Nunito/Nunito-LightItalic.ttf | Bin .../Resources/Fonts/Nunito/Nunito-Regular.ttf | Bin .../Fonts/Nunito/Nunito-SemiBold.ttf | Bin .../Fonts/Nunito/Nunito-SemiBoldItalic.ttf | Bin .../Gui/Resources/Fonts/Nunito/OFL.txt | 0 .../Gui/Resources/Fonts/Nunito/download.txt | 0 .../Gui/Resources/Fonts/PT_Mono/OFL.txt | 0 .../Fonts/PT_Mono/PTMono-Regular.ttf | Bin .../Gui/Resources/Fonts/PT_Mono/download.txt | 0 .../Gui/Resources/Fonts/PT_Sans/OFL.txt | 0 .../Resources/Fonts/PT_Sans/PTSans-Bold.ttf | Bin .../Fonts/PT_Sans/PTSans-BoldItalic.ttf | Bin .../Resources/Fonts/PT_Sans/PTSans-Italic.ttf | Bin .../Fonts/PT_Sans/PTSans-Regular.ttf | Bin .../Gui/Resources/Fonts/PT_Sans/download.txt | 0 .../Gui/Resources/Icons/description.txt | 0 .../Gui/Style/Colors.qml | 0 .../Gui/Style/Fonts.qml | 0 .../Gui/Style/Sizes.qml | 0 .../Gui/Style/Times.qml | 0 .../Gui/Style/qmldir | 0 .../Logic/Logging.py | 0 .../Logic/Maintenance.py | 0 .../Logic/Maintenance/Updater.qml | 0 .../Logic/Maintenance/qmldir | 0 .../Logic/Translate.py | 0 .../Logic/Utils/Utils.py | 0 src/easyapplication/Logic/Utils/__init__.py | 0 src/easyapplication/Logic/__init__.py | 0 src/easyapplication/__init__.py | 1 - 195 files changed, 66 deletions(-) delete mode 100644 resources/images/ea_logo.svg delete mode 100644 resources/images/ea_logo_wfont.svg delete mode 100644 resources/images/vscode_debug.jpg delete mode 100644 src/EasyApp/Logic/Utils/__init__.py delete mode 100644 src/EasyApp/Logic/__init__.py delete mode 100644 src/EasyApp/__init__.py rename src/{EasyApp => easyapplication}/Gui/Animations/ColorReset.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Animations/ThemeChange.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Animations/TranslationChange.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Animations/qmldir (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/ChartTemplateHeatmap2dPlotly.html (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/ChartTemplateSimple1dPlotly.html (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/ChartViewHeatmap2dPlotly.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/ChartViewSimple1dPlotly.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/Plotly1dBarPlot.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/Plotly1dLine.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/Plotly1dMeasVsCalc.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/Plotly2dHeatmap.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/Plotly2dPolarHeatmap.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/Plotly3dScatter.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/Plotly3dSurface.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/QtCharts1dBase 2.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/QtCharts1dBase.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/QtCharts1dMeasVsCalc.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/QtCharts1dValueAxis.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Charts/qmldir (100%) rename src/{EasyApp => easyapplication}/Gui/Components/AboutDialog.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/AppBarCentralTabs.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/AppBarLeftButtons.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/AppBarRightButtons.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/ApplicationWindow.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/BasicReport.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/ContentArea.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/ContentPage.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/GuideWindow.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/GuideWindowContainer.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/JsonListModel.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/MainContent.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/PreferencesDialog.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/ProjectDescriptionDialog.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/SideBar.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/SideBarColumn.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/TableView.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/TableViewAdvancedLabel.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/TableViewButton.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/TableViewCheckBox.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/TableViewComboBox.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/TableViewDelegate.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/TableViewHeader.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/TableViewLabel.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/TableViewLabelControl.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/TableViewParameter.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/TableViewTextInput.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/TableViewTwoRowsAdvancedLabel.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Components/qmldir (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/AppBarTabButton.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/ApplicationWindow.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/Button.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/CheckBox.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/CheckIndicator.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/ComboBox.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/CursorDelegate.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/Dialog.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/DialogButtonBox.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/GroupBox.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/GroupButton.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/GroupColumn.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/GroupRow.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/Label.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/LinkedImage.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/Menu.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/MenuItem.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/ParamComboBox.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/ParamTextField.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/Parameter.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/RadioButton.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/RadioIndicator.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/RemoteController.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/RemotePointer.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/RunningLabel.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/ScrollBar.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/ScrollIndicator.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/SideBarButton.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/Slider.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/SliderHandle.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/SpinBox.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/SplashScreen.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/StatusBar.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/StatusBarItem.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/TabBar.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/TabButton.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/TextArea.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/TextField.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/TextInput.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/ToolButton.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/ToolTip.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/ToolTipShadow.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Elements/qmldir (100%) rename src/{EasyApp => easyapplication}/Gui/Globals/Vars.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Globals/qmldir (100%) rename src/{EasyApp => easyapplication}/Gui/Html/BasicReport.html (100%) rename src/{EasyApp => easyapplication}/Gui/Html/BlankPage.html (100%) rename src/{EasyApp => easyapplication}/Gui/Html/PTSans-Bold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Html/PTSans-Regular.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Html/Plotly1dBarPlot.html (100%) rename src/{EasyApp => easyapplication}/Gui/Html/Plotly1dLine.html (100%) rename src/{EasyApp => easyapplication}/Gui/Html/Plotly1dMeasVsCalc.html (100%) rename src/{EasyApp => easyapplication}/Gui/Html/Plotly2dHeatmap.html (100%) rename src/{EasyApp => easyapplication}/Gui/Html/Plotly2dPolarHeatmap.html (100%) rename src/{EasyApp => easyapplication}/Gui/Html/Plotly3dMesh.html (100%) rename src/{EasyApp => easyapplication}/Gui/Html/Plotly3dScatter.html (100%) rename src/{EasyApp => easyapplication}/Gui/Html/Plotly3dSurface.html (100%) rename src/{EasyApp => easyapplication}/Gui/Html/plotly-2.18.0.min.js (100%) rename src/{EasyApp => easyapplication}/Gui/Logic/Plotting.js (100%) rename src/{EasyApp => easyapplication}/Gui/Logic/ProjectConfig.js (100%) rename src/{EasyApp => easyapplication}/Gui/Logic/Translate.js (100%) rename src/{EasyApp => easyapplication}/Gui/Logic/Utils.js (100%) rename src/{EasyApp => easyapplication}/Gui/Logic/qmldir (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Black.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Bold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-ExtraBold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-ExtraLight.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Light.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Medium.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Regular.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-SemiBold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Thin.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans/OFL.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans/download.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Black.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Bold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-ExtraBold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-ExtraLight.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Light.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Medium.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Regular.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-SemiBold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Thin.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/OFL.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/download.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Black.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Bold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-ExtraBold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-ExtraLight.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Light.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Medium.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Regular.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-SemiBold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Thin.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/OFL.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/download.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/FontAwesome/Font Awesome 5 Free-Solid-900.otf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/FontAwesome/Font Awesome 6 Free-Solid-900.otf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/FontAwesome/Font Awesome 7 Free-Solid-900.otf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/FontAwesome/LICENSE.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-Black.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-BlackItalic.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-Bold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-BoldItalic.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-ExtraBold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-ExtraBoldItalic.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-ExtraLight.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-ExtraLightItalic.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-Italic.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-Light.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-LightItalic.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-Regular.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-SemiBold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/Nunito-SemiBoldItalic.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/OFL.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/Nunito/download.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/PT_Mono/OFL.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/PT_Mono/PTMono-Regular.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/PT_Mono/download.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/PT_Sans/OFL.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/PT_Sans/PTSans-Bold.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/PT_Sans/PTSans-BoldItalic.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/PT_Sans/PTSans-Italic.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/PT_Sans/PTSans-Regular.ttf (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Fonts/PT_Sans/download.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Resources/Icons/description.txt (100%) rename src/{EasyApp => easyapplication}/Gui/Style/Colors.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Style/Fonts.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Style/Sizes.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Style/Times.qml (100%) rename src/{EasyApp => easyapplication}/Gui/Style/qmldir (100%) rename src/{EasyApp => easyapplication}/Logic/Logging.py (100%) rename src/{EasyApp => easyapplication}/Logic/Maintenance.py (100%) rename src/{EasyApp => easyapplication}/Logic/Maintenance/Updater.qml (100%) rename src/{EasyApp => easyapplication}/Logic/Maintenance/qmldir (100%) rename src/{EasyApp => easyapplication}/Logic/Translate.py (100%) rename src/{EasyApp => easyapplication}/Logic/Utils/Utils.py (100%) create mode 100644 src/easyapplication/Logic/Utils/__init__.py create mode 100644 src/easyapplication/Logic/__init__.py diff --git a/resources/images/ea_logo.svg b/resources/images/ea_logo.svg deleted file mode 100644 index bae13662..00000000 --- a/resources/images/ea_logo.svg +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/resources/images/ea_logo_wfont.svg b/resources/images/ea_logo_wfont.svg deleted file mode 100644 index ccab6452..00000000 --- a/resources/images/ea_logo_wfont.svg +++ /dev/null @@ -1,34 +0,0 @@ - - - - - - - - - - easyapp - - - - - - - - - - - \ No newline at end of file diff --git a/resources/images/vscode_debug.jpg b/resources/images/vscode_debug.jpg deleted file mode 100644 index 4adc396db7ace44ce52221aec30bec97ea1bd54f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50517 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 # SPDX-License-Identifier: BSD-3-Clause -"""EasyApplication library.""" From 11e20b5d4126777e523501c9e13f01017f8bf61d Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 29 Apr 2026 16:03:47 +0200 Subject: [PATCH 3/7] Apply formatting --- .github/workflows/wasm.yml | 8 +- .prettierignore | 4 + CONTRIBUTING.md | 4 +- EXAMPLES.md | 144 +- README.md | 27 +- docs/docs/assets/images/favicon.png | Bin 0 -> 34184 bytes docs/docs/assets/images/logo_dark.svg | 44 + docs/docs/assets/images/logo_light.svg | 44 + docs/docs/index.md | 27 +- docs/docs/installation-and-setup/index.md | 34 +- docs/docs/introduction/index.md | 20 +- docs/docs/user-guide/index.md | 4 +- docs/overrides/.icons/easyapplication.svg | 3 + docs/overrides/.icons/easyscience.svg | 20 + examples/AdvancedPy/README.md | 48 +- examples/AdvancedPy/pyproject.toml | 18 +- pixi.lock | 8395 ++++++++++++++--- pixi.toml | 5 +- pyproject.toml | 16 +- .../Charts/ChartTemplateHeatmap2dPlotly.html | 80 +- .../Charts/ChartTemplateSimple1dPlotly.html | 228 +- src/easyapplication/Gui/Html/BasicReport.html | 727 +- src/easyapplication/Gui/Html/BlankPage.html | 5 +- .../Gui/Html/Plotly1dBarPlot.html | 275 +- .../Gui/Html/Plotly1dLine.html | 300 +- .../Gui/Html/Plotly1dMeasVsCalc.html | 578 +- .../Gui/Html/Plotly2dHeatmap.html | 314 +- .../Gui/Html/Plotly2dPolarHeatmap.html | 286 +- .../Gui/Html/Plotly3dMesh.html | 265 +- .../Gui/Html/Plotly3dScatter.html | 483 +- .../Gui/Html/Plotly3dSurface.html | 336 +- src/easyapplication/Gui/Logic/Plotting.js | 1365 +-- .../Gui/Logic/ProjectConfig.js | 58 +- src/easyapplication/Gui/Logic/Translate.js | 27 +- src/easyapplication/Gui/Logic/Utils.js | 214 +- src/easyapplication/Logic/Logging.py | 52 +- src/easyapplication/Logic/Maintenance.py | 100 +- src/easyapplication/Logic/Translate.py | 17 +- src/easyapplication/Logic/Utils/Utils.py | 11 +- src/easyapplication/Logic/Utils/__init__.py | 2 + src/easyapplication/Logic/__init__.py | 2 + tests/functional/test_dummy.py | 3 +- tests/integration/fitting/test_dummy.py | 4 +- .../integration/scipp-analysis/test_dummy.py | 4 +- tests/unit/test_dummy.py | 3 +- 45 files changed, 10182 insertions(+), 4422 deletions(-) create mode 100644 docs/docs/assets/images/favicon.png create mode 100644 docs/docs/assets/images/logo_dark.svg create mode 100644 docs/docs/assets/images/logo_light.svg create mode 100644 docs/overrides/.icons/easyapplication.svg create mode 100644 docs/overrides/.icons/easyscience.svg diff --git a/.github/workflows/wasm.yml b/.github/workflows/wasm.yml index 38d48eb2..a5b4578c 100644 --- a/.github/workflows/wasm.yml +++ b/.github/workflows/wasm.yml @@ -125,9 +125,9 @@ jobs: - name: Upload the zipped wasm folder uses: actions/upload-artifact@v4 with: - name: wasm # name of the zip file to be created and uploaded - path: build/wasm # directory whose content is to be zipped - compression-level: 0 # no compression + name: wasm # name of the zip file to be created and uploaded + path: build/wasm # directory whose content is to be zipped + compression-level: 0 # no compression if-no-files-found: error - name: Push the built wasm to the webapp branch @@ -136,4 +136,4 @@ jobs: GITHUB_TOKEN: ${{ secrets.GH_API_PERSONAL_ACESS_TOKEN }} REPO: self BRANCH: webapp - FOLDER: build/wasm # directory whose content is to be pushed + FOLDER: build/wasm # directory whose content is to be pushed diff --git a/.prettierignore b/.prettierignore index a08c3c48..71d6c650 100644 --- a/.prettierignore +++ b/.prettierignore @@ -25,6 +25,10 @@ docs/docs/assets/ # Node node_modules +# Minified files +*.min.js +*.min.css + # Misc .benchmarks .cache diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index fc3e7312..1c16a65b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -42,8 +42,8 @@ Please make sure you follow the EasyScience organization-wide If you are not planning to contribute code, you may want to: - 🐞 Report a bug — see [Reporting Issues](#11-reporting-issues) -- 🛡 Report a security issue — - see [Security Issues](#12-security-issues) +- 🛡 Report a security issue — see + [Security Issues](#12-security-issues) - 💬 Ask a question or start a discussion at [Project Discussions](https://github.com/easyscience/easyapp/discussions) diff --git a/EXAMPLES.md b/EXAMPLES.md index 14f2bf93..6c59df26 100644 --- a/EXAMPLES.md +++ b/EXAMPLES.md @@ -1,6 +1,9 @@ ## Types of examples -Different types of examples are given here. All examples have a graphical frontend implemented in QML, but differ in the way the backend logic is implemented and the runtime used to run them. These examples can be categorised as shown in the following table: +Different types of examples are given here. All examples have a +graphical frontend implemented in QML, but differ in the way the backend +logic is implemented and the runtime used to run them. These examples +can be categorised as shown in the following table: | Example | Type | Frontend | Backend | Runtime | | -------------- | ---- | -------- | -------- | -------------------- | @@ -14,29 +17,29 @@ Different types of examples are given here. All examples have a graphical fronte ### Python environment -* Create and activate a python environment (_optional_) +- Create and activate a python environment (_optional_) - ***macOS and Linux*** + **_macOS and Linux_** ``` python3 -m venv .venv source .venv/bin/activate ``` - ***Windows*** + **_Windows_** ``` python3 -m venv .venv .venv\Scripts\activate ``` -* Upgrade PIP, the package installer for Python (_optional_) +- Upgrade PIP, the package installer for Python (_optional_) ``` pip install --upgrade pip ``` -* Install the Qt for Python `PySide6` package using PIP +- Install the Qt for Python `PySide6` package using PIP ``` pip install 'PySide6>=6.8,<6.9' @@ -46,65 +49,98 @@ Different types of examples are given here. All examples have a graphical fronte #### Qt Creator -Qt Creator is a prefered IDE for developing the GUI in QML. It allows to run QML code in debug mode with breakpoints, preview changes to QML code in live mode, has build in documentation for QML modules, has QML code auto-completion, and more unique feature related to the QML code. - -* Download Qt Online Installer from [qt.io](https://www.qt.io/download-qt-installer-oss). More info at [doc.qt.io](https://doc.qt.io/qt-6/qt-online-installation.html). -* Install Qt for desktop development using a custom installation that includes the following components: - * Qt - * [ ] Qt 6.8.z - * [x] Desktop (***macOS***) or MSVC 2019 64-bit (***Windows***) - * [ ] Additional Libraries - * [x] Qt Charts - * [x] Qt Shader Tools - * [ ] Developer and Designer Tools - * [x] Qt Creator x.y.z +Qt Creator is a prefered IDE for developing the GUI in QML. It allows to +run QML code in debug mode with breakpoints, preview changes to QML code +in live mode, has build in documentation for QML modules, has QML code +auto-completion, and more unique feature related to the QML code. + +- Download Qt Online Installer from + [qt.io](https://www.qt.io/download-qt-installer-oss). More info at + [doc.qt.io](https://doc.qt.io/qt-6/qt-online-installation.html). +- Install Qt for desktop development using a custom installation that + includes the following components: + - Qt + - [ ] Qt 6.8.z + - [x] Desktop (**_macOS_**) or MSVC 2019 64-bit (**_Windows_**) + - [ ] Additional Libraries + - [x] Qt Charts + - [x] Qt Shader Tools + - [ ] Developer and Designer Tools + - [x] Qt Creator x.y.z #### VS Code (_alternative to Qt Creator_) -VS Code is an alternative IDE to Qt Creator. It has a syntax highlighting plugin for QML code, but lacks some of the unique features of Qt Creator mentioned above to make QML development easier. +VS Code is an alternative IDE to Qt Creator. It has a syntax +highlighting plugin for QML code, but lacks some of the unique features +of Qt Creator mentioned above to make QML development easier. -* Download an install VS Code -* Add the python extension -* The initial launch configuration is in the `.vscode/launch.json` file, which should be automatically read by VS Code -* Select any python file in the repo and choose the desired python environment +- Download an install VS Code +- Add the python extension +- The initial launch configuration is in the `.vscode/launch.json` file, + which should be automatically read by VS Code +- Select any python file in the repo and choose the desired python + environment ## How to run ### Type I Examples: BasicQml (QML runtime with QML backend) -This example is located in `examples/BasicQml` and the source code is in the subfolder `src/BasicQml`. The example consists of a graphical QML frontend (`Gui/Application.qml`) and a QML backend (`Logic/Mock/BackendProxy.qml`). It is considered a mock backend as it only returns hardcoded values rather than providing the required functionality. The entry point for Qt is `main.qml`, which can be displayed using Qt `qml` viewer. +This example is located in `examples/BasicQml` and the source code is in +the subfolder `src/BasicQml`. The example consists of a graphical QML +frontend (`Gui/Application.qml`) and a QML backend +(`Logic/Mock/BackendProxy.qml`). It is considered a mock backend as it +only returns hardcoded values rather than providing the required +functionality. The entry point for Qt is `main.qml`, which can be +displayed using Qt `qml` viewer. #### Run using the QML Runtime ##### Run via the Qt Creator IDE -* Run Qt Creator -* Open the qml project file from the example folder `examples/BasicQml/src/BasicQml.qmlproject` -* Click Run (Green play button) +- Run Qt Creator +- Open the qml project file from the example folder + `examples/BasicQml/src/BasicQml.qmlproject` +- Click Run (Green play button) #### How to edit GUI elements in live mode -* In Qt Creator, select the `*.qml` file to be edited in live mode -* Click the `Design` button at the top of the left sidebar of `Qt Creator` - * _Note: If this button is disabled, find and click `About plugins...` in the `Qt Creator` menu, scroll down to the `Qt Quick` section and enable `QmlDesigner`._ -* In the `Design` window, click the `Show Live Preview` button in the top panel of the application (small play button in a circle). - * _Note: Showing the entire `main.qml` application window in live mode works best when the open `main.qml` is moved to another monitor and does not overlap with the `Qt Creator` window_. -* When the desired GUI component appears, you can click the `Edit` button at the top of the left sidebar of `Qt Creator` to return to the source code of that qml component and still see it live in a separate window. +- In Qt Creator, select the `*.qml` file to be edited in live mode +- Click the `Design` button at the top of the left sidebar of + `Qt Creator` + - _Note: If this button is disabled, find and click `About plugins...` + in the `Qt Creator` menu, scroll down to the `Qt Quick` section and + enable `QmlDesigner`._ +- In the `Design` window, click the `Show Live Preview` button in the + top panel of the application (small play button in a circle). + - _Note: Showing the entire `main.qml` application window in live mode + works best when the open `main.qml` is moved to another monitor and + does not overlap with the `Qt Creator` window_. +- When the desired GUI component appears, you can click the `Edit` + button at the top of the left sidebar of `Qt Creator` to return to the + source code of that qml component and still see it live in a separate + window. ### Type II Examples: BasicPy (Python runtime with QML backend) -This example is in the `examples/BasicPy` folder, and the source code is in the `examples/BasicPy/src/BasicPy` folder. This example serves to demonstrate how an application with a QML frontend and QML backend (similar to the Type I example) can be run from Python. The entry point for the Python application is the `main.py` file. To run it, follow the steps below: +This example is in the `examples/BasicPy` folder, and the source code is +in the `examples/BasicPy/src/BasicPy` folder. This example serves to +demonstrate how an application with a QML frontend and QML backend +(similar to the Type I example) can be run from Python. The entry point +for the Python application is the `main.py` file. To run it, follow the +steps below: #### Run using the Python interpreter ##### Run from the terminal -* Go to the example folder, e.g., +- Go to the example folder, e.g., ```sh $ cd examples/BasicPy/src/BasicPy ``` -* Run using Python (provided that the required python environment is activated as explained above) + +- Run using Python (provided that the required python environment is + activated as explained above) ```sh $ python main.py @@ -112,27 +148,41 @@ This example is in the `examples/BasicPy` folder, and the source code is in the ##### Run via the Qt Creator IDE (_alternative to run from the terminal_) -* Run Qt Creator -* Open the python project file from the example folder `examples/BasicPy/src/BasicPy.pyproject` -* Select the desired python environment with the Qt `PySide6` module installed -* Click Run (Green play button) +- Run Qt Creator +- Open the python project file from the example folder + `examples/BasicPy/src/BasicPy.pyproject` +- Select the desired python environment with the Qt `PySide6` module + installed +- Click Run (Green play button) ##### Run via the VS Code IDE (_alternative to run from the terminal or via the Qt Creator IDE_) -* Open the repo in VS Code -* Click on the debug extension and select which example to execute -![Debug dropdown window](resources/images/vscode_debug.jpg) +- Open the repo in VS Code +- Click on the debug extension and select which example to execute + ![Debug dropdown window](resources/images/vscode_debug.jpg) ### Type III Examples: IntermediatePy and AdvancedPy (Python runtime with Python backend) -These examples demonstrate how to use the Python runtime to run the QML GUI binded with the Python backend located in `Backends/real_backend.py`. These examples can be run through Python in the same way as Type II described above. In these examples, the Python-based backend is registered in `main.py` and then imported into QML. The Qt QML GUI then accesses the backend by calling the properties and methods of the classes defined in the `Backends/real_py` folder. +These examples demonstrate how to use the Python runtime to run the QML +GUI binded with the Python backend located in +`Backends/real_backend.py`. These examples can be run through Python in +the same way as Type II described above. In these examples, the +Python-based backend is registered in `main.py` and then imported into +QML. The Qt QML GUI then accesses the backend by calling the properties +and methods of the classes defined in the `Backends/real_py` folder. #### Possible Issues -* If in Qt Creator some components are highlighted and marked as "Unknown component. (M300)", try resetting via "Tools > QML/JS > Reset Code Model". +- If in Qt Creator some components are highlighted and marked as + "Unknown component. (M300)", try resetting via "Tools > QML/JS > Reset + Code Model". ### Type IV Examples: BasicC++ (QML backend) -This example can be run after compilation into an executable program. It only has a mock backend in QML (the C++ backend is not implemented). The minimum configuration requires a base `main.cpp` file and, if Qt Creator is used as the IDE, a `*.pro` file. +This example can be run after compilation into an executable program. It +only has a mock backend in QML (the C++ backend is not implemented). The +minimum configuration requires a base `main.cpp` file and, if Qt Creator +is used as the IDE, a `*.pro` file. -This example is currently used to create a WebAssembly application that can be run inside a web browser. +This example is currently used to create a WebAssembly application that +can be run inside a web browser. diff --git a/README.md b/README.md index cd6601ba..fd0efda4 100644 --- a/README.md +++ b/README.md @@ -9,33 +9,36 @@

-**EasyApplication** is a collection of Qt/QML graphical components to create cross-platform applications with intuitive graphical interface based on the EasyScience framework. +**EasyApplication** is a collection of Qt/QML graphical components to +create cross-platform applications with intuitive graphical interface +based on the EasyScience framework. - **EasyApplication** is developed as a Python library. - - -License: [BSD 3-Clause](https://github.com/easyscience/easyapp/blob/master/LICENSE) +License: +[BSD 3-Clause](https://github.com/easyscience/easyapp/blob/master/LICENSE) ## Useful Links ### For Users - 📖 [Documentation](https://easyscience.github.io/easyapp/latest) -- 🚀 [Getting Started](https://easyscience.github.io/easyapp/latest/introduction) +- 🚀 + [Getting Started](https://easyscience.github.io/easyapp/latest/introduction) - 🧪 [Tutorials](https://easyscience.github.io/easyapp/latest/tutorials) -- 💬 [Get in Touch](https://easyscience.github.io/easyapp/latest/introduction/#get-in-touch) -- 🧾 [Citation](https://easyscience.github.io/easyapp/latest/introduction/#citation) +- 💬 + [Get in Touch](https://easyscience.github.io/easyapp/latest/introduction/#get-in-touch) +- 🧾 + [Citation](https://easyscience.github.io/easyapp/latest/introduction/#citation) ### For Contributors - 🧑‍💻 [Source Code](https://github.com/easyscience/easyapp) - 🐞 [Issue Tracker](https://github.com/easyscience/easyapp/issues) - 💡 [Discussions](https://github.com/easyscience/easyapp/discussions) -- 🤝 [Contributing Guide](https://github.com/easyscience/easyapp/blob/master/CONTRIBUTING.md) -- 🛡 [Code of Conduct](https://github.com/easyscience/.github/blob/master/CODE_OF_CONDUCT.md) - - +- 🤝 + [Contributing Guide](https://github.com/easyscience/easyapp/blob/master/CONTRIBUTING.md) +- 🛡 + [Code of Conduct](https://github.com/easyscience/.github/blob/master/CODE_OF_CONDUCT.md) diff --git a/docs/docs/assets/images/favicon.png b/docs/docs/assets/images/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..27628988594ba1ed4a6ece48d766e31457481367 GIT binary patch literal 34184 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 literal 0 HcmV?d00001 diff --git a/docs/docs/assets/images/logo_dark.svg b/docs/docs/assets/images/logo_dark.svg new file mode 100644 index 00000000..766608a9 --- /dev/null +++ b/docs/docs/assets/images/logo_dark.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + Logo + + + Main circle + + + + + + + + + + + + + + + + Text + + + + + \ No newline at end of file diff --git a/docs/docs/assets/images/logo_light.svg b/docs/docs/assets/images/logo_light.svg new file mode 100644 index 00000000..ab2547d9 --- /dev/null +++ b/docs/docs/assets/images/logo_light.svg @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + Logo + + + Main circle + + + + + + + + + + + + + + + + Text + + + + + \ No newline at end of file diff --git a/docs/docs/index.md b/docs/docs/index.md index 03176c8c..5f267d9f 100644 --- a/docs/docs/index.md +++ b/docs/docs/index.md @@ -4,17 +4,18 @@ Here is a brief overview of the main documentation sections: -- [:material-information-slab-circle: Introduction](introduction/index.md) – - Provides a description of EasyApplication, including its purpose, licensing, - latest release details, and contact information. -- [:material-cog-box: Installation & Setup](installation-and-setup/index.md) – - Guides users through system requirements, environment configuration, and the - installation process. -- [:material-book-open-variant: User Guide](user-guide/index.md) – Covers core - concepts, key terminology, workflow steps, and essential parameters for - effective use of EasyApplication. +- [:material-information-slab-circle: Introduction](introduction/index.md) + – Provides a description of EasyApplication, including its purpose, + licensing, latest release details, and contact information. +- [:material-cog-box: Installation & Setup](installation-and-setup/index.md) + – Guides users through system requirements, environment configuration, + and the installation process. +- [:material-book-open-variant: User Guide](user-guide/index.md) – + Covers core concepts, key terminology, workflow steps, and essential + parameters for effective use of EasyApplication. - [:material-school: Tutorials](tutorials/index.md) – Offers practical, - step-by-step examples demonstrating common workflows and data analysis tasks. -- [:material-code-braces-box: API Reference](api-reference/index.md) – An - auto-generated reference detailing the available functions and modules in - EasyApplication. + step-by-step examples demonstrating common workflows and data analysis + tasks. +- [:material-code-braces-box: API Reference](api-reference/index.md) – + An auto-generated reference detailing the available functions and + modules in EasyApplication. diff --git a/docs/docs/installation-and-setup/index.md b/docs/docs/installation-and-setup/index.md index 23719f0a..86cc9d05 100644 --- a/docs/docs/installation-and-setup/index.md +++ b/docs/docs/installation-and-setup/index.md @@ -8,8 +8,8 @@ icon: material/cog-box **Python 3.11** through **3.14**. To install and set up EasyApplication, we recommend using -[**Pixi**](https://pixi.prefix.dev), a modern package manager for Windows, -macOS, and Linux. +[**Pixi**](https://pixi.prefix.dev), a modern package manager for +Windows, macOS, and Linux. ??? note "Main benefits of using Pixi" @@ -77,7 +77,8 @@ This section describes the simplest way to set up EasyApplication using #### Updating Pixi and EasyApplication -- To update all packages in the Pixi environment, including EasyApplication: +- To update all packages in the Pixi environment, including + EasyApplication: ```txt pixi update ``` @@ -93,9 +94,9 @@ This section describes the simplest way to set up EasyApplication using ## Classical Installation -This section describes how to install EasyApplication using the traditional -method with **pip**. It is assumed that you are familiar with Python package -management and virtual environments. +This section describes how to install EasyApplication using the +traditional method with **pip**. It is assumed that you are familiar +with Python package management and virtual environments. ### Environment Setup optional { #environment-setup data-toc-label="Environment Setup" } @@ -160,8 +161,8 @@ simply delete and recreate the environment. ### Installing from PyPI { #from-pypi } -EasyApplication is available on **PyPI (Python Package Index)** and can be -installed using `pip`. To do so, use the following command: +EasyApplication is available on **PyPI (Python Package Index)** and can +be installed using `pip`. To do so, use the following command: ```txt pip install easyapplication @@ -197,7 +198,8 @@ pip show easyapplication Installing unreleased versions is generally not recommended but may be useful for testing. -To install EasyApplication from the `develop` branch of GitHub, for example: +To install EasyApplication from the `develop` branch of GitHub, for +example: ```txt pip install git+https://github.com/easyscience/easyapp@develop @@ -211,10 +213,10 @@ pip install 'easyapplication[dev] @ git+https://github.com/easyscience/easyapp@d ## How to Run Tutorials -EasyApplication includes a collection of **Jupyter Notebook examples** that -demonstrate key functionality. These tutorials serve as **step-by-step -guides** to help users understand the data analysis workflow. They are -available as **static HTML pages** in the +EasyApplication includes a collection of **Jupyter Notebook examples** +that demonstrate key functionality. These tutorials serve as +**step-by-step guides** to help users understand the data analysis +workflow. They are available as **static HTML pages** in the [:material-school: Tutorials](../tutorials/index.md) section. In the next sections, we explain how to set up Jupyter and run the @@ -234,7 +236,8 @@ once using the command line, as shown below. ```txt pixi add --pypi jupyterlab pixi-kernel ``` -- Download all the EasyApplication tutorials to the `tutorials/` directory: +- Download all the EasyApplication tutorials to the `tutorials/` + directory: ```txt pixi run easyapplication download-all-tutorials ``` @@ -256,7 +259,8 @@ once using the command line, as shown below. ```txt python -m ipykernel install --user --name=venv --display-name "EasyApplication Python kernel" ``` -- Download all the EasyApplication tutorials to the `tutorials/` directory: +- Download all the EasyApplication tutorials to the `tutorials/` + directory: ```txt python -m easyapplication download-all-tutorials ``` diff --git a/docs/docs/introduction/index.md b/docs/docs/introduction/index.md index ab5be286..ef276e64 100644 --- a/docs/docs/introduction/index.md +++ b/docs/docs/introduction/index.md @@ -6,13 +6,12 @@ icon: material/information-slab-circle ## Description -**EasyApplication** is a collection of Qt/QML graphical components to create cross-platform applications with intuitive graphical interface based on the EasyScience framework. - +**EasyApplication** is a collection of Qt/QML graphical components to +create cross-platform applications with intuitive graphical interface +based on the EasyScience framework. **EasyApplication** is developed as a Python library. - - + + - - - - -
- -

Summary report

- -
-

Project

-

- Name: Slanting line
- Description: Straight line, negative slope, Siglent SDS1202X-E
- Model: Line
- Experimental data: Siglent SDS1202X-E
- Last modified: 09.02.2023 14:13
-

-
- -
-

Software

-

- Analysis: EasyExample v0.8.4-beta
- Charts: Plotly JavaScript v2.18.0
- Calculation engine: Custom
- Minimization engine: Custom
-

-
- -
-

Model

- - -
Line function: y = M•x + C
-
- -
-

Parameters

- - - - -
No. Parameter Value Units
1 Slope M 3.0000
2 y-Intercept C 1.0000
-
- -
-

Fitting

-
-
- -
- - + ///////// + // Plotly + ///////// + + // Measured data trace + let measuredTrace = { + name: 'Measured', + x: data.x, + y: data.measured.y, + mode: 'lines+markers', + marker: { symbol: 'circle', size: 5 }, + line: { width: 1 }, + } + + // Total calculated data trace + let calculatedTrace = { + name: 'Calculated', + x: data.x, + y: data.calculated.y, + mode: 'lines', + line: { width: 1 }, + } + + // Plot dataset + let plotTraces = [measuredTrace, calculatedTrace] + + // Plot layout + let plotLayout = { + autosize: true, + margin: { l: 43, r: 3, b: 50, t: 40, pad: 0 }, + showlegend: true, + legend: { + x: 0.98, + y: 0.98, + xanchor: 'right', + font: { family: 'ChartFont' }, + }, + xaxis: { + title: 'x-axis', + automargin: true, + autorange: true, + showgrid: true, + zeroline: false, + showline: true, + linewidth: 1, + mirror: 'ticks', + titlefont: { family: 'ChartFont' }, + tickfont: { family: 'ChartFont' }, + }, + yaxis: { + title: 'y-axis', + automargin: true, + autorange: true, + showgrid: true, + zeroline: false, + showline: true, + linewidth: 1, + mirror: 'ticks', + titlefont: { family: 'ChartFont' }, + tickfont: { family: 'ChartFont' }, + }, + } + + // Plot config + let plotConfig = { + displayModeBar: true, + displaylogo: false, + } + + // Create plot + Plotly.newPlot('plotDiv', plotTraces, plotLayout, plotConfig) + + ///////////////////////////////////////// + // Functionality to be accesable from QML + ///////////////////////////////////////// + + // need to be called before setChartColors to init axis.titlefont + function setChartSizes(sizes) { + plotLayout.legend.font.size = sizes.fontPixelSize + + plotLayout.xaxis.tickfont.size = sizes.fontPixelSize + plotLayout.yaxis.tickfont.size = sizes.fontPixelSize + + if (typeof plotLayout.xaxis.titlefont !== 'undefined') { + plotLayout.xaxis.titlefont.size = sizes.fontPixelSize + } else { + plotLayout.xaxis.titlefont = { size: sizes.fontPixelSize } + } + if (typeof plotLayout.yaxis.titlefont !== 'undefined') { + plotLayout.yaxis.titlefont.size = sizes.fontPixelSize + } else { + plotLayout.yaxis.titlefont = { size: sizes.fontPixelSize } + } + + measuredTrace.marker.size = sizes.measuredScatterSize + measuredTrace.line.width = sizes.measuredLineWidth + calculatedTrace.line.width = sizes.calculatedLineWidth + + return 'setChartSizes is finished.' + } + + function setChartColors(colors) { + //////////document.getElementById('plotContainer').style.backgroundColor = colors.chartBackground + + plotLayout.paper_bgcolor = colors.chartBackground + plotLayout.plot_bgcolor = colors.chartPlotAreaBackground + plotLayout.legend.bgcolor = colors.chartBackground + 'cc' // add transparency + //plotLayout.legend.bordercolor = colors.chartGrid + plotLayout.legend.font.color = colors.chartForeground + + plotLayout.xaxis.linecolor = colors.chartAxis + plotLayout.yaxis.linecolor = colors.chartAxis + plotLayout.xaxis.gridcolor = colors.chartGrid + plotLayout.yaxis.gridcolor = colors.chartGrid + + if (typeof plotLayout.xaxis.titlefont !== 'undefined') { + plotLayout.xaxis.titlefont.color = colors.chartForeground + } else { + plotLayout.xaxis.title.font.color = colors.chartForeground + } + if (typeof plotLayout.yaxis.titlefont !== 'undefined') { + plotLayout.yaxis.titlefont.color = colors.chartForeground + } else { + plotLayout.yaxis.title.font.color = colors.chartForeground + } + + plotLayout.xaxis.tickfont.color = colors.chartForeground + plotLayout.yaxis.tickfont.color = colors.chartForeground + + measuredTrace.marker.color = colors.measuredScatter + measuredTrace.line.color = colors.measuredLine + calculatedTrace.line.color = colors.calculatedLine + + return 'setChartColors is finished.' + } + + //Plotly.restyle('plotDiv', {opacity: 0.5}) + + function relayoutPlot() { + Plotly.relayout('plotDiv', plotLayout) + } + + function redrawPlot() { + Plotly.redraw('plotDiv') + } + + function setXAxisTitle(newTitle) { + plotLayout.xaxis.title = newTitle + } - + function setYAxisTitle(newTitle) { + plotLayout.yaxis.title = newTitle + } + function setMeasuredXYData(newData) { + measuredTrace.x = newData.x + measuredTrace.y = newData.y + } + + function setCalculatedXYData(newData) { + calculatedTrace.x = newData.x + calculatedTrace.y = newData.y + } + + diff --git a/src/easyapplication/Gui/Html/BlankPage.html b/src/easyapplication/Gui/Html/BlankPage.html index bd54434b..4b3b4339 100644 --- a/src/easyapplication/Gui/Html/BlankPage.html +++ b/src/easyapplication/Gui/Html/BlankPage.html @@ -1,3 +1,2 @@ - - - + + diff --git a/src/easyapplication/Gui/Html/Plotly1dBarPlot.html b/src/easyapplication/Gui/Html/Plotly1dBarPlot.html index 7cae1eb0..d2e31eed 100644 --- a/src/easyapplication/Gui/Html/Plotly1dBarPlot.html +++ b/src/easyapplication/Gui/Html/Plotly1dBarPlot.html @@ -1,148 +1,139 @@ - + - - - - - - - - - - - - - -
-
-
- - + + + + + +
+
+
+ + - - - + } + + diff --git a/src/easyapplication/Gui/Html/Plotly1dLine.html b/src/easyapplication/Gui/Html/Plotly1dLine.html index b0974ffa..9c8afda3 100644 --- a/src/easyapplication/Gui/Html/Plotly1dLine.html +++ b/src/easyapplication/Gui/Html/Plotly1dLine.html @@ -1,161 +1,151 @@ - + - - - - - - - - - - - - - -
-
-
- - + + + + + +
+
+
+ + - - - + } + + diff --git a/src/easyapplication/Gui/Html/Plotly1dMeasVsCalc.html b/src/easyapplication/Gui/Html/Plotly1dMeasVsCalc.html index 9874b1a7..a75f8d27 100644 --- a/src/easyapplication/Gui/Html/Plotly1dMeasVsCalc.html +++ b/src/easyapplication/Gui/Html/Plotly1dMeasVsCalc.html @@ -1,300 +1,356 @@ - + + + + + + + - - - - - -
-
-
- - + function setCalculatedYData(newData) { + calculatedTrace.y = newData + } - + /////// + // Misc + /////// + window.addEventListener('resize', relayoutPlot) + + diff --git a/src/easyapplication/Gui/Html/Plotly2dHeatmap.html b/src/easyapplication/Gui/Html/Plotly2dHeatmap.html index e1ce435a..a683db0c 100644 --- a/src/easyapplication/Gui/Html/Plotly2dHeatmap.html +++ b/src/easyapplication/Gui/Html/Plotly2dHeatmap.html @@ -1,162 +1,156 @@ - + - - - - - - - - - - - - - -
-
-
- - - - - + + + + + + + + + +
+
+
+ + + diff --git a/src/easyapplication/Gui/Html/Plotly2dPolarHeatmap.html b/src/easyapplication/Gui/Html/Plotly2dPolarHeatmap.html index 6ddac6d1..01bef54c 100644 --- a/src/easyapplication/Gui/Html/Plotly2dPolarHeatmap.html +++ b/src/easyapplication/Gui/Html/Plotly2dPolarHeatmap.html @@ -1,158 +1,148 @@ - + + + + + + + - - - - - -
-
-
- - - - - + } + + diff --git a/src/easyapplication/Gui/Html/Plotly3dMesh.html b/src/easyapplication/Gui/Html/Plotly3dMesh.html index 8d3ceac5..585ddb03 100644 --- a/src/easyapplication/Gui/Html/Plotly3dMesh.html +++ b/src/easyapplication/Gui/Html/Plotly3dMesh.html @@ -1,139 +1,130 @@ - + - - - - - - - - - - - - - -
-
-
- - - - + + + + + + + + + +
+
+
+ + + diff --git a/src/easyapplication/Gui/Html/Plotly3dScatter.html b/src/easyapplication/Gui/Html/Plotly3dScatter.html index 0e48da28..8e6d69c3 100644 --- a/src/easyapplication/Gui/Html/Plotly3dScatter.html +++ b/src/easyapplication/Gui/Html/Plotly3dScatter.html @@ -1,262 +1,249 @@ - + - - - - - - - - - - - - - -
-
-
- - + + + + + +
+
+
+ + - - + z: 0, + }, + }, + }, + } + + // Plot config + let plotConfig = { + displayModeBar: true, + displaylogo: false, + } + + // Create plot + Plotly.newPlot('plotDiv', plotTraces, plotLayout, plotConfig) + + ///////////////////////////////////////// + // Functionality to be accesable from QML + ///////////////////////////////////////// + + function redrawPlot() { + Plotly.redraw('plotDiv') + } + + function setXAxisTitle(newTitle) { + plotLayout.scene.xaxis.title = newTitle + } + + function setYAxisTitle(newTitle) { + plotLayout.scene.yaxis.title = newTitle + } + + function setZAxisTitle(newTitle) { + plotLayout.scene.zaxis.title = newTitle + } + + diff --git a/src/easyapplication/Gui/Html/Plotly3dSurface.html b/src/easyapplication/Gui/Html/Plotly3dSurface.html index 18f82394..9bb844e2 100644 --- a/src/easyapplication/Gui/Html/Plotly3dSurface.html +++ b/src/easyapplication/Gui/Html/Plotly3dSurface.html @@ -1,170 +1,170 @@ - + - - - - - - - - - - - - - -
-
-
- - - - + + + + + + + + + +
+
+
+ + + diff --git a/src/easyapplication/Gui/Logic/Plotting.js b/src/easyapplication/Gui/Logic/Plotting.js index e12f77f7..f20ab7b8 100644 --- a/src/easyapplication/Gui/Logic/Plotting.js +++ b/src/easyapplication/Gui/Logic/Plotting.js @@ -1,642 +1,723 @@ -///////// -// Common -///////// - -function headCommon() { - const list = [ - '', - '', - '' - ] - return list.join('\n') -} - -function chartHtml(head, chart, toolbar='') { - const list = [ - '', - '', - '', - head, - '', - '', - toolbar, - '', - '', - '' - ] - return list.join('\n') -} - -//////// -// Bokeh -//////// - -function bokehInfo() { - const version = '2.4.3' - return { - version: version, - url: `https://docs.bokeh.org/en/${version}` - } -} - -function bokehHtml(data, specs) { - const head = bokehHead(specs) - const chart = bokehChart(data, specs) - const html = chartHtml(head, chart) - return html -} - -function bokehHeadScripts() { - const baseSrc = 'https://cdn.pydata.org/bokeh/release' - const version = bokehInfo().version - const list = [ - ``, - ``, - ``, - `` - ] - return list.join('\n') -} - -function bokehHeadStyle(specs) { - const list = [ - '' - ] - return list.join('\n') -} - -function bokehHead(specs) { - const list = [ - headCommon(), - bokehHeadScripts(), - bokehHeadStyle(specs) - ] - return list.join('\n') -} - -function bokehChart(data, specs) { - if (!data.hasMeasured && !data.hasCalculated && !data.hasPlotRanges) { - return "" - } - // List of strings to be filled below - let chart = [] - - // Tooltips - chart.push(bokehAddMainTooltip(data, specs)) - chart.push(bokehAddBraggTooltip(specs)) - - // Data sources - chart.push('const main_source = new Bokeh.ColumnDataSource()') - // Charts array - chart.push('const charts = []') - - // Main chart (top) - chart.push(...bokehCreateMainChart(data, specs)) - chart.push(...bokehAddMainTools('main_chart')) - chart.push(...bokehAddHiddenXAxis('main_chart', specs)) - chart.push(...bokehAddVisibleYAxis('main_chart', specs)) - if (data.hasBackground) { - chart.push(...bokehAddBackgroundDataToMainChart(data, specs)) - } - if (data.hasMeasured) { - chart.push(...bokehAddMeasuredDataToMainChart(data, specs)) - } - if (data.hasPhase) { - chart.push(...bokehAddPhaseDataToMainChart(data, specs)) - } - if (data.hasCalculated) { - chart.push(...bokehAddCalculatedDataToMainChart(data, specs)) - } - chart.push(`charts.push([main_chart])`) - - // Bragg peaks chart (middle) - if (data.hasBragg) { - chart.push(...bokehCreateBraggChart(data, specs)) - chart.push(...bokehAddBraggTools()) - chart.push(...bokehAddHiddenXAxis('bragg_chart', specs)) - chart.push(...bokehAddHiddenYAxis('bragg_chart')) - chart.push(...bokehAddDataToBraggChart(data, specs)) - chart.push(`charts.push([bragg_chart])`) - } - - // Difference chart (bottom) - if (data.hasDifference) { - chart.push(...bokehCreateDiffChart(data, specs)) - chart.push(...bokehAddMainTools('diff_chart')) - chart.push(...bokehAddHiddenXAxis('diff_chart', specs)) - chart.push(...bokehAddVisibleYAxis('diff_chart', specs)) - chart.push(...bokehAddDataToDiffChart(data, specs)) - chart.push(...adjustDifferenceYRange()) - chart.push(`diff_chart.ygrid[0].ticker.desired_num_ticks = 3`) - chart.push(`charts.push([diff_chart])`) - } - - // xAxis chart (very bottom) - chart.push(...bokehCreateXAxisChart(data, specs)) - chart.push(...bokehAddVisibleXAxis('xaxis_chart', specs)) - chart.push(...bokehAddHiddenYAxis('xaxis_chart')) - chart.push(`charts.push([xaxis_chart])`) - - // Charts array grid layout - chart.push(`const grid_options = {toolbar_location: "above"}`) - chart.push(`const gridplot = new Bokeh.Plotting.gridplot(charts, grid_options)`) - - chart.push(...[ - 'function OnClick() {', - 'main_chart.reset.emit()', - '//console.log("AAA", document.querySelector(".bk-tool-icon-reset"))', - '//console.log("BBB", document.getElementById("bk-tool-icon-reset"))', - ' //document.getElementById("bk-tool-icon-reset").click()', - ' document.querySelector(".bk-tool-icon-reset").click()', - '}' - ]) - - // Show charts - if (typeof specs.containerId !== 'undefined') { - chart.push(`Bokeh.Plotting.show(gridplot, "#${specs.containerId}")`) - } else { - chart.push(`Bokeh.Plotting.show(gridplot)`) - } - - // Return as string - return chart.join('\n') -} - -// Bokeh charts - -function bokehCreateMainChart(data, specs) { - return [`const main_chart = new Bokeh.Plotting.figure({`, - ` tools: "reset,undo,redo",`, - - ` height: ${specs.mainChartHeight},`, - ` width: ${specs.chartWidth},`, - - ` x_range: new Bokeh.Range1d({`, - ` start: ${data.ranges.min_x},`, - ` end: ${data.ranges.max_x}`, - ` }),`, - ` y_range: new Bokeh.Range1d({`, - ` start: ${data.ranges.min_y},`, - ` end: ${data.ranges.max_y}`, - ` }),`, - - ` y_axis_label: "${specs.yMainAxisTitle}",`, - - ` outline_line_color: "${EaStyle.Colors.chartAxis}",`, - ` background: "${specs.chartBackgroundColor}",`, - ` background_fill_color: "${specs.chartBackgroundColor}",`, - ` border_fill_color: "${specs.chartBackgroundColor}",`, - ` //border_fill_color: "red",`, - - ` min_border_right: ${1.5 * specs.fontPixelSize},`, - ` min_border_top: ${0.5 * specs.fontPixelSize},`, - ` min_border_bottom: ${0.5 * specs.fontPixelSize}`, - `})`] -} - -function bokehCreateBraggChart(data, specs) { - const num_phases = Object.keys(data.bragg).length - const y_margin = 0.65 - const y_min = -num_phases + 1 - y_margin - const y_max = y_margin - return [`const bragg_chart = new Bokeh.Plotting.figure({`, - ` tools: "",`, - - ` height: ${specs.braggChartHeight},`, - ` width: ${specs.chartWidth},`, - - ` x_range: main_chart.x_range,`, - ` y_range: new Bokeh.Range1d({ start: ${y_min}, end: ${y_max} }),`, - - ` outline_line_color: "${EaStyle.Colors.chartAxis}",`, - ` background: "${specs.chartBackgroundColor}",`, - ` background_fill_color: "${specs.chartBackgroundColor}",`, - ` border_fill_color: "${specs.chartBackgroundColor}",`, - ` //border_fill_color: "green",`, - - ` min_border_top: ${0.5 * specs.fontPixelSize},`, - ` min_border_bottom: ${0.5 * specs.fontPixelSize}`, - `})`] -} - -function bokehCreateDiffChart(data, specs) { - return [`const diff_chart = new Bokeh.Plotting.figure({`, - ` tools: "reset",`, - - ` height: ${specs.differenceChartHeight},`, - ` width: ${specs.chartWidth},`, - - ` x_range: main_chart.x_range,`, - - ` y_axis_label: "${specs.yDifferenceAxisTitle}",`, - - ` outline_line_color: "${EaStyle.Colors.chartAxis}",`, - ` background: "${specs.chartBackgroundColor}",`, - ` background_fill_color: "${specs.chartBackgroundColor}",`, - ` border_fill_color: "${specs.chartBackgroundColor}",`, - ` //border_fill_color: "blue",`, - - ` min_border_top: ${0.5 * specs.fontPixelSize},`, - ` min_border_bottom: ${0.5 * specs.fontPixelSize}`, - `})`] -} - -function bokehCreateXAxisChart(data, specs) { - return [`const xaxis_chart = new Bokeh.Plotting.figure({`, - ` tools: "",`, - - ` height: ${specs.xAxisChartHeight},`, - ` width: ${specs.chartWidth},`, - - ` x_range: main_chart.x_range,`, - ` y_range: new Bokeh.Range1d({ start: 0, end: 1 }),`, - - ` x_axis_label: "${specs.xAxisTitle}",`, - - ` outline_line_color: null,`, - ` background: "${specs.chartBackgroundColor}",`, - ` background_fill_color: "${specs.chartBackgroundColor}",`, - ` border_fill_color: "${specs.chartBackgroundColor}",`, - ` //border_fill_color: "orange",`, - - ` min_border_top: 0,`, - ` min_border_bottom: 0`, - `})`] -} - -// Misc - -function adjustDifferenceYRange() { - return [`function differenceChartMeanY() {`, - ` let ySum = 0, yCount = 0`, - ` for (let i in main_source.data.x_diff) {`, - ` if (diff_chart.x_range.start <= main_source.data.x_diff[i] && main_source.data.x_diff[i] <= diff_chart.x_range.end) {`, - ` ySum += main_source.data.y_diff[i]`, - ` yCount += 1`, - ` }`, - ` }`, - ` if (yCount > 0) {`, - ` return ySum / yCount`, - ` }`, - ` return 0`, - `}`, - - `function differenceChartHalfRangeY() {`, - ` const mainChartRangeY = main_chart.y_range.end - main_chart.y_range.start`, - ` const mainChartAxesHeight = main_chart.height - main_chart.min_border_top - main_chart.min_border_bottom`, - ` const differenceChartAxesHeight = diff_chart.height - diff_chart.min_border_top - diff_chart.min_border_bottom`, - ` const differenceToMainChartHeightRatio = differenceChartAxesHeight / mainChartAxesHeight`, - ` const differenceChartRangeY = mainChartRangeY * differenceToMainChartHeightRatio`, - ` return 0.5 * differenceChartRangeY`, - `}`, - - `diff_chart.y_range = new Bokeh.Range1d({`, - ` start: differenceChartMeanY() - differenceChartHalfRangeY(),`, - ` end: differenceChartMeanY() + differenceChartHalfRangeY()`, - `})`, - - `main_chart.y_range.change.connect(function() {`, - ` diff_chart.y_range.start = differenceChartMeanY() - differenceChartHalfRangeY()`, - ` diff_chart.y_range.end = differenceChartMeanY() + differenceChartHalfRangeY()`, - `})`] -} - -// Bokeh tools - -function bokehAddMainTools(chart) { - return [`${chart}.add_tools(new Bokeh.HoverTool({tooltips:main_tooltip, point_policy:"snap_to_data", mode:"mouse"}))`, - `${chart}.add_tools(new Bokeh.BoxZoomTool())`, - `${chart}.toolbar.active_drag = "box_zoom"`, - `${chart}.add_tools(new Bokeh.PanTool())`] -} - -function bokehAddBraggTools() { - return [`bragg_chart.add_tools(new Bokeh.HoverTool({tooltips:bragg_tooltip, point_policy:"snap_to_data", mode:"mouse"}))`] -} - -// Bokeh axes - -function bokehAddVisibleXAxis(chart, specs) { - return [`${chart}.xaxis[0].axis_label_text_font = "PT Sans"`, - `${chart}.xaxis[0].axis_label_text_font_style = "normal"`, - `${chart}.xaxis[0].axis_label_text_font_size = "${specs.fontPixelSize}px"`, - `${chart}.xaxis[0].axis_label_text_color = "${specs.chartForegroundColor}"`, - `${chart}.xaxis[0].axis_label_standoff = ${specs.fontPixelSize - 5}`, - `${chart}.xaxis[0].axis_line_color = null`, - - `${chart}.xaxis[0].major_label_text_font = "PT Sans"`, - `${chart}.xaxis[0].major_label_text_font_size = "${specs.fontPixelSize}px"`, - `${chart}.xaxis[0].major_label_text_color = "${specs.chartForegroundColor}"`, - `${chart}.xaxis[0].major_label_standoff = 0`, - `${chart}.xaxis[0].major_tick_line_color = null`, - `${chart}.xaxis[0].major_tick_in = 0`, - `${chart}.xaxis[0].major_tick_out = 0`, - `${chart}.xaxis[0].minor_tick_line_color = null`, - `${chart}.xaxis[0].minor_tick_out = 0`, - - `${chart}.xgrid[0].grid_line_color = null`] -} - -function bokehAddHiddenXAxis(chart, specs) { - return [`${chart}.xaxis[0].axis_label_text_font_size = "0px"`, - `${chart}.xaxis[0].axis_line_color = null`, - - `${chart}.xaxis[0].major_label_text_font_size = "0px"`, - `${chart}.xaxis[0].major_tick_line_color = "${specs.chartGridLineColor}"`, - `${chart}.xaxis[0].major_tick_in = 0`, - `${chart}.xaxis[0].major_tick_out = 0`, - `${chart}.xaxis[0].minor_tick_line_color = "${specs.chartMinorGridLineColor}"`, - `${chart}.xaxis[0].minor_tick_out = 0`, - - `${chart}.xgrid[0].grid_line_color = "${specs.chartGridLineColor}"`] -} - -function bokehAddVisibleYAxis(chart, specs) { - return [`${chart}.yaxis[0].axis_label_text_font = "PT Sans"`, - `${chart}.yaxis[0].axis_label_text_font_style = "normal"`, - `${chart}.yaxis[0].axis_label_text_font_size = "${specs.fontPixelSize}px"`, - `${chart}.yaxis[0].axis_label_text_color = "${specs.chartForegroundColor}"`, - `${chart}.yaxis[0].axis_label_standoff = ${specs.fontPixelSize}`, - `${chart}.yaxis[0].axis_line_color = null`, - - `${chart}.yaxis[0].major_label_text_font = "PT Sans"`, - `${chart}.yaxis[0].major_label_text_font_size = "${specs.fontPixelSize}px"`, - `${chart}.yaxis[0].major_label_text_color = "${specs.chartForegroundColor}"`, - `${chart}.yaxis[0].major_tick_line_color = "${specs.chartGridLineColor}"`, - `${chart}.yaxis[0].major_tick_in = 0`, - `${chart}.yaxis[0].major_tick_out = 0`, - `${chart}.yaxis[0].minor_tick_line_color = "${specs.chartMinorGridLineColor}"`, - `${chart}.yaxis[0].minor_tick_out = 0`, - - `${chart}.ygrid[0].grid_line_color = "${specs.chartGridLineColor}"`] -} - -function bokehAddHiddenYAxis(chart) { - return [`${chart}.yaxis[0].axis_line_color = null`, - - `${chart}.yaxis[0].major_tick_in = 0`, - `${chart}.yaxis[0].major_tick_out = 0`, - `${chart}.yaxis[0].minor_tick_out = 0`, - `${chart}.yaxis[0].major_label_text_font_size = "0px"`, - - `${chart}.ygrid[0].grid_line_color = null`] -} - -// Bokeh data - -function bokehAddBackgroundDataToMainChart(data, specs) { - return [`main_source.data.x_bkg = [${data.background.x}]`, - `main_source.data.y_bkg = [${data.background.y}]`, - - 'const bkgLine = new Bokeh.Line({', - ' x: { field: "x_bkg" },', - ' y: { field: "y_bkg" },', - ` line_color: "${specs.backgroundLineColor}",`, - ` line_alpha: 0.5,`, - ` line_dash: [4, 2],`, - ` line_width: ${specs.backgroundLineWidth},`, - '})', - - 'main_chart.add_glyph(bkgLine, main_source)'] -} - -function bokehAddMeasuredDataToMainChart(data, specs) { - - - return [`main_source.data.x_meas = [${data.measured.x}]`, - `main_source.data.y_meas = [${data.measured.y}]`, - `main_source.data.sy_meas = [${data.measured.sy}]`, - `main_source.data.y_meas_upper = [${data.measured.y_upper}]`, - `main_source.data.y_meas_lower = [${data.measured.y_lower}]`, - - `const measLineTop = new Bokeh.Line({`, - ` x: { field: "x_meas" },`, - ` y: { field: "y_meas_upper" },`, - ` line_color: "${specs.measuredLineColor}",`, - ` line_alpha: 0.4,`, - ` line_width: ${specs.measuredLineWidth}`, - `})`, - `const measLineBottom = new Bokeh.Line({`, - ` x: { field: "x_meas" },`, - ` y: { field: "y_meas_lower" },`, - ` line_color: "${specs.measuredLineColor}",`, - ` line_width: ${specs.measuredLineWidth},`, - ` line_alpha: 0.4`, - `})`, - `const measArea = new Bokeh.VArea({`, - ` x: { field: "x_meas" },`, - ` y1: { field: "y_meas_upper" },`, - ` y2: { field: "y_meas_lower" },`, - ` fill_color: "${specs.measuredAreaColor}",`, - ` fill_alpha: 0.35`, - `})`, - - `main_chart.add_glyph(measArea, main_source)`, - `main_chart.add_glyph(measLineTop, main_source)`, - `main_chart.add_glyph(measLineBottom, main_source)`] - - -} - -function bokehAddCalculatedDataToMainChart(data, specs) { - - return [`main_source.data.x_calc = [${data.calculated.x}]`, - `main_source.data.y_calc = [${data.calculated.y}]`, - - 'const calcLine = new Bokeh.Line({', - ' x: { field: "x_calc" },', - ' y: { field: "y_calc" },', - ` line_color: "${specs.calculatedLineColor}",`, - ` line_width: ${specs.calculatedLineWidth},`, - ` line_alpha: 0.8`, - '})', - - 'main_chart.add_glyph(calcLine, main_source)'] -} - -function bokehAddPhaseDataToMainChart(data, specs) { - let out = [] - out.push('let phaseLine = new Bokeh.Line()') - for (const phase_index in data.phase) { - out.push(`main_source.data.x_phase_${phase_index} = [${data.phase[phase_index].x}]`) - out.push(`main_source.data.y_phase_${phase_index}_upper = [${data.phase[phase_index].y_upper}]`) - out.push(`main_source.data.y_phase_${phase_index}_lower = [${data.phase[phase_index].y_lower}]`) - out.push(`const phaseLine_${phase_index} = new Bokeh.Line({`) - out.push(` x: { field: "x_phase_${phase_index}" },`) - out.push(` y: { field: "y_phase_${phase_index}_upper" },`) - out.push(` line_color: "${specs.phaseLineColor[phase_index]}",`) - out.push(` line_width: ${specs.phaseLineWidth},`) - out.push(` line_alpha: 0.6,`) - out.push('})') - out.push(`const phaseArea_${phase_index} = new Bokeh.VArea({`) - out.push(` x: { field: "x_phase_${phase_index}" },`) - out.push(` y1: { field: "y_phase_${phase_index}_upper" },`) - out.push(` y2: { field: "y_phase_${phase_index}_lower" },`) - out.push(` fill_color: "${specs.phaseLineColor[phase_index]}",`) - out.push(` fill_alpha: 0.1`) - out.push('})') - out.push(`main_chart.add_glyph(phaseArea_${phase_index}, main_source)`) - out.push(`main_chart.add_glyph(phaseLine_${phase_index}, main_source)`) - - } - return out -} - -function bokehAddDataToBraggChart(data, specs) { - - - let out = [] - for (const phase_index in data.bragg) { - console.log("---", phase_index, data.bragg[phase_index].x.slice(0,200) ) - out.push(`const bragg_source_${phase_index} = new Bokeh.ColumnDataSource()`) - - out.push(`bragg_source_${phase_index}.data.x_bragg = [${data.bragg[phase_index].x}]`) - out.push(`bragg_source_${phase_index}.data.y_bragg = [${data.bragg[phase_index].y}]`) - out.push(`bragg_source_${phase_index}.data.h_bragg = [${data.bragg[phase_index].h}]`) - out.push(`bragg_source_${phase_index}.data.k_bragg = [${data.bragg[phase_index].k}]`) - out.push(`bragg_source_${phase_index}.data.l_bragg = [${data.bragg[phase_index].l}]`) - - out.push(`const braggTicks_${phase_index} = new Bokeh.Scatter({`) - out.push(` x: { field: "x_bragg" },`) - out.push(` y: { field: "y_bragg" },`) - out.push(` marker: "dash",`) - out.push(` size: ${1.5 * specs.fontPixelSize},`) - out.push(` line_color: "${specs.braggTicksColor[phase_index]}",`) - out.push(` angle: ${Math.PI / 2.}`) - out.push('})') - out.push(`bragg_chart.add_glyph(braggTicks_${phase_index}, bragg_source_${phase_index})`) - } - return out -} - -function bokehAddDataToDiffChart(data, specs) { - - - - - return [`main_source.data.x_diff = [${data.difference.x}]`, - `main_source.data.y_diff = [${data.difference.y}]`, - `main_source.data.y_diff_upper = [${data.difference.y_upper}]`, - `main_source.data.y_diff_lower = [${data.difference.y_lower}]`, - - `const diffLineTop = new Bokeh.Line({`, - ` x: { field: "x_diff" },`, - ` y: { field: "y_diff_upper" },`, - ` line_color: "${specs.differenceLineColor}",`, - ` line_alpha: 0.4,`, - ` line_width: ${specs.differenceLineWidth}`, - `})`, - `const diffLineBottom = new Bokeh.Line({`, - ` x: { field: "x_diff" },`, - ` y: { field: "y_diff_lower" },`, - ` line_color: "${specs.differenceLineColor}",`, - ` line_alpha: 0.4,`, - ` line_width: ${specs.differenceLineWidth}`, - `})`, - `const diffArea = new Bokeh.VArea({`, - ` x: { field: "x_diff" },`, - ` y1: { field: "y_diff_upper" },`, - ` y2: { field: "y_diff_lower" },`, - ` fill_color: "${specs.differenceAreaColor}",`, - ` fill_alpha: 0.35`, - `})`, - - `diff_chart.add_glyph(diffArea, main_source)`, - `diff_chart.add_glyph(diffLineTop, main_source)`, - `diff_chart.add_glyph(diffLineBottom, main_source)`] -} - -// Bokeh tooltips - -function bokehMainTooltipRow(color, label, value, sigma='') { - return [``, - ` ${label}: `, - ` ${value}`, - ` ${sigma}`, - ``] -} - -function bokehAddMainTooltip(data, specs) { - const x_meas = bokehMainTooltipRow(EaStyle.Colors.themeForegroundDisabled, 'x', '@x_meas{0.00}') - const x_calc = bokehMainTooltipRow(EaStyle.Colors.themeForegroundDisabled, 'x', '@x_calc{0.00}') - const y_meas = bokehMainTooltipRow(specs.measuredLineColor, 'meas', '@y_meas{0.0}', '± @sy_meas{0.0}') - const y_calc = bokehMainTooltipRow(specs.calculatedLineColor, 'calc', '@y_calc{0.0}') - const y_bkg = bokehMainTooltipRow(specs.backgroundLineColor, 'bkg', '@y_bkg{0.0}') - const y_diff = bokehMainTooltipRow(specs.differenceLineColor, 'diff', '@y_diff{0.0}') - let y_phases = [] - for (const phase_index in data.phase) { - if (typeof specs.phaseLineColor !== 'undefined') { - y_phases.push(bokehMainTooltipRow(specs.phaseLineColor[phase_index], `phase ${phase_index}`, `@y_phase_${phase_index}_upper{0.0}`)) - } - } - - let table = [] - table.push(...[`
`, ``, ``]) - // x - if (data.hasMeasured) { - table.push(...x_meas) - } else if (data.hasCalculated) { - table.push(...x_calc) - } - // y - if (data.hasMeasured) { - table.push(...y_meas) - } - if (data.hasCalculated) { - table.push(...y_calc) - } - for (const phase_index in data.phase) { - if (typeof y_phases[phase_index] !== 'undefined') { - table.push(...y_phases[phase_index]) - } - } - if (data.hasBackground) { - table.push(...y_bkg) - } - if (data.hasDifference) { - table.push(...y_diff) - } - table.push(...[``, `
`, `
`]) - - const tooltip = JSON.stringify(table.join('\n')) - return `const main_tooltip = (${tooltip})` -} - -function bokehBraggTooltipSpan(color, label, value) { - return `${label}: ${value}` -} - -function bokehAddBraggTooltip(specs) { - const x_bragg = bokehBraggTooltipSpan(EaStyle.Colors.themeForegroundDisabled, 'x', '@x_bragg{0.00}') - const hkl_bragg = bokehBraggTooltipSpan(specs.calculatedLineColor, 'hkl', '(@h_bragg @k_bragg @l_bragg)') - - const table = [`
`, - x_bragg, - ' ', - hkl_bragg, - `
`] - - const tooltip = JSON.stringify(table.join('\n')) - return `const bragg_tooltip = (${tooltip})` -} +///////// +// Common +///////// + +function headCommon() { + const list = [ + '', + '', + '', + ] + return list.join('\n') +} + +function chartHtml(head, chart, toolbar = '') { + const list = [ + '', + '', + '', + head, + '', + '', + toolbar, + '', + '', + '', + ] + return list.join('\n') +} + +//////// +// Bokeh +//////// + +function bokehInfo() { + const version = '2.4.3' + return { + version: version, + url: `https://docs.bokeh.org/en/${version}`, + } +} + +function bokehHtml(data, specs) { + const head = bokehHead(specs) + const chart = bokehChart(data, specs) + const html = chartHtml(head, chart) + return html +} + +function bokehHeadScripts() { + const baseSrc = 'https://cdn.pydata.org/bokeh/release' + const version = bokehInfo().version + const list = [ + ``, + ``, + ``, + ``, + ] + return list.join('\n') +} + +function bokehHeadStyle(specs) { + const list = [ + '', + ] + return list.join('\n') +} + +function bokehHead(specs) { + const list = [headCommon(), bokehHeadScripts(), bokehHeadStyle(specs)] + return list.join('\n') +} + +function bokehChart(data, specs) { + if (!data.hasMeasured && !data.hasCalculated && !data.hasPlotRanges) { + return '' + } + // List of strings to be filled below + let chart = [] + + // Tooltips + chart.push(bokehAddMainTooltip(data, specs)) + chart.push(bokehAddBraggTooltip(specs)) + + // Data sources + chart.push('const main_source = new Bokeh.ColumnDataSource()') + // Charts array + chart.push('const charts = []') + + // Main chart (top) + chart.push(...bokehCreateMainChart(data, specs)) + chart.push(...bokehAddMainTools('main_chart')) + chart.push(...bokehAddHiddenXAxis('main_chart', specs)) + chart.push(...bokehAddVisibleYAxis('main_chart', specs)) + if (data.hasBackground) { + chart.push(...bokehAddBackgroundDataToMainChart(data, specs)) + } + if (data.hasMeasured) { + chart.push(...bokehAddMeasuredDataToMainChart(data, specs)) + } + if (data.hasPhase) { + chart.push(...bokehAddPhaseDataToMainChart(data, specs)) + } + if (data.hasCalculated) { + chart.push(...bokehAddCalculatedDataToMainChart(data, specs)) + } + chart.push(`charts.push([main_chart])`) + + // Bragg peaks chart (middle) + if (data.hasBragg) { + chart.push(...bokehCreateBraggChart(data, specs)) + chart.push(...bokehAddBraggTools()) + chart.push(...bokehAddHiddenXAxis('bragg_chart', specs)) + chart.push(...bokehAddHiddenYAxis('bragg_chart')) + chart.push(...bokehAddDataToBraggChart(data, specs)) + chart.push(`charts.push([bragg_chart])`) + } + + // Difference chart (bottom) + if (data.hasDifference) { + chart.push(...bokehCreateDiffChart(data, specs)) + chart.push(...bokehAddMainTools('diff_chart')) + chart.push(...bokehAddHiddenXAxis('diff_chart', specs)) + chart.push(...bokehAddVisibleYAxis('diff_chart', specs)) + chart.push(...bokehAddDataToDiffChart(data, specs)) + chart.push(...adjustDifferenceYRange()) + chart.push(`diff_chart.ygrid[0].ticker.desired_num_ticks = 3`) + chart.push(`charts.push([diff_chart])`) + } + + // xAxis chart (very bottom) + chart.push(...bokehCreateXAxisChart(data, specs)) + chart.push(...bokehAddVisibleXAxis('xaxis_chart', specs)) + chart.push(...bokehAddHiddenYAxis('xaxis_chart')) + chart.push(`charts.push([xaxis_chart])`) + + // Charts array grid layout + chart.push(`const grid_options = {toolbar_location: "above"}`) + chart.push( + `const gridplot = new Bokeh.Plotting.gridplot(charts, grid_options)`, + ) + + chart.push( + ...[ + 'function OnClick() {', + 'main_chart.reset.emit()', + '//console.log("AAA", document.querySelector(".bk-tool-icon-reset"))', + '//console.log("BBB", document.getElementById("bk-tool-icon-reset"))', + ' //document.getElementById("bk-tool-icon-reset").click()', + ' document.querySelector(".bk-tool-icon-reset").click()', + '}', + ], + ) + + // Show charts + if (typeof specs.containerId !== 'undefined') { + chart.push(`Bokeh.Plotting.show(gridplot, "#${specs.containerId}")`) + } else { + chart.push(`Bokeh.Plotting.show(gridplot)`) + } + + // Return as string + return chart.join('\n') +} + +// Bokeh charts + +function bokehCreateMainChart(data, specs) { + return [ + `const main_chart = new Bokeh.Plotting.figure({`, + ` tools: "reset,undo,redo",`, + + ` height: ${specs.mainChartHeight},`, + ` width: ${specs.chartWidth},`, + + ` x_range: new Bokeh.Range1d({`, + ` start: ${data.ranges.min_x},`, + ` end: ${data.ranges.max_x}`, + ` }),`, + ` y_range: new Bokeh.Range1d({`, + ` start: ${data.ranges.min_y},`, + ` end: ${data.ranges.max_y}`, + ` }),`, + + ` y_axis_label: "${specs.yMainAxisTitle}",`, + + ` outline_line_color: "${EaStyle.Colors.chartAxis}",`, + ` background: "${specs.chartBackgroundColor}",`, + ` background_fill_color: "${specs.chartBackgroundColor}",`, + ` border_fill_color: "${specs.chartBackgroundColor}",`, + ` //border_fill_color: "red",`, + + ` min_border_right: ${1.5 * specs.fontPixelSize},`, + ` min_border_top: ${0.5 * specs.fontPixelSize},`, + ` min_border_bottom: ${0.5 * specs.fontPixelSize}`, + `})`, + ] +} + +function bokehCreateBraggChart(data, specs) { + const num_phases = Object.keys(data.bragg).length + const y_margin = 0.65 + const y_min = -num_phases + 1 - y_margin + const y_max = y_margin + return [ + `const bragg_chart = new Bokeh.Plotting.figure({`, + ` tools: "",`, + + ` height: ${specs.braggChartHeight},`, + ` width: ${specs.chartWidth},`, + + ` x_range: main_chart.x_range,`, + ` y_range: new Bokeh.Range1d({ start: ${y_min}, end: ${y_max} }),`, + + ` outline_line_color: "${EaStyle.Colors.chartAxis}",`, + ` background: "${specs.chartBackgroundColor}",`, + ` background_fill_color: "${specs.chartBackgroundColor}",`, + ` border_fill_color: "${specs.chartBackgroundColor}",`, + ` //border_fill_color: "green",`, + + ` min_border_top: ${0.5 * specs.fontPixelSize},`, + ` min_border_bottom: ${0.5 * specs.fontPixelSize}`, + `})`, + ] +} + +function bokehCreateDiffChart(data, specs) { + return [ + `const diff_chart = new Bokeh.Plotting.figure({`, + ` tools: "reset",`, + + ` height: ${specs.differenceChartHeight},`, + ` width: ${specs.chartWidth},`, + + ` x_range: main_chart.x_range,`, + + ` y_axis_label: "${specs.yDifferenceAxisTitle}",`, + + ` outline_line_color: "${EaStyle.Colors.chartAxis}",`, + ` background: "${specs.chartBackgroundColor}",`, + ` background_fill_color: "${specs.chartBackgroundColor}",`, + ` border_fill_color: "${specs.chartBackgroundColor}",`, + ` //border_fill_color: "blue",`, + + ` min_border_top: ${0.5 * specs.fontPixelSize},`, + ` min_border_bottom: ${0.5 * specs.fontPixelSize}`, + `})`, + ] +} + +function bokehCreateXAxisChart(data, specs) { + return [ + `const xaxis_chart = new Bokeh.Plotting.figure({`, + ` tools: "",`, + + ` height: ${specs.xAxisChartHeight},`, + ` width: ${specs.chartWidth},`, + + ` x_range: main_chart.x_range,`, + ` y_range: new Bokeh.Range1d({ start: 0, end: 1 }),`, + + ` x_axis_label: "${specs.xAxisTitle}",`, + + ` outline_line_color: null,`, + ` background: "${specs.chartBackgroundColor}",`, + ` background_fill_color: "${specs.chartBackgroundColor}",`, + ` border_fill_color: "${specs.chartBackgroundColor}",`, + ` //border_fill_color: "orange",`, + + ` min_border_top: 0,`, + ` min_border_bottom: 0`, + `})`, + ] +} + +// Misc + +function adjustDifferenceYRange() { + return [ + `function differenceChartMeanY() {`, + ` let ySum = 0, yCount = 0`, + ` for (let i in main_source.data.x_diff) {`, + ` if (diff_chart.x_range.start <= main_source.data.x_diff[i] && main_source.data.x_diff[i] <= diff_chart.x_range.end) {`, + ` ySum += main_source.data.y_diff[i]`, + ` yCount += 1`, + ` }`, + ` }`, + ` if (yCount > 0) {`, + ` return ySum / yCount`, + ` }`, + ` return 0`, + `}`, + + `function differenceChartHalfRangeY() {`, + ` const mainChartRangeY = main_chart.y_range.end - main_chart.y_range.start`, + ` const mainChartAxesHeight = main_chart.height - main_chart.min_border_top - main_chart.min_border_bottom`, + ` const differenceChartAxesHeight = diff_chart.height - diff_chart.min_border_top - diff_chart.min_border_bottom`, + ` const differenceToMainChartHeightRatio = differenceChartAxesHeight / mainChartAxesHeight`, + ` const differenceChartRangeY = mainChartRangeY * differenceToMainChartHeightRatio`, + ` return 0.5 * differenceChartRangeY`, + `}`, + + `diff_chart.y_range = new Bokeh.Range1d({`, + ` start: differenceChartMeanY() - differenceChartHalfRangeY(),`, + ` end: differenceChartMeanY() + differenceChartHalfRangeY()`, + `})`, + + `main_chart.y_range.change.connect(function() {`, + ` diff_chart.y_range.start = differenceChartMeanY() - differenceChartHalfRangeY()`, + ` diff_chart.y_range.end = differenceChartMeanY() + differenceChartHalfRangeY()`, + `})`, + ] +} + +// Bokeh tools + +function bokehAddMainTools(chart) { + return [ + `${chart}.add_tools(new Bokeh.HoverTool({tooltips:main_tooltip, point_policy:"snap_to_data", mode:"mouse"}))`, + `${chart}.add_tools(new Bokeh.BoxZoomTool())`, + `${chart}.toolbar.active_drag = "box_zoom"`, + `${chart}.add_tools(new Bokeh.PanTool())`, + ] +} + +function bokehAddBraggTools() { + return [ + `bragg_chart.add_tools(new Bokeh.HoverTool({tooltips:bragg_tooltip, point_policy:"snap_to_data", mode:"mouse"}))`, + ] +} + +// Bokeh axes + +function bokehAddVisibleXAxis(chart, specs) { + return [ + `${chart}.xaxis[0].axis_label_text_font = "PT Sans"`, + `${chart}.xaxis[0].axis_label_text_font_style = "normal"`, + `${chart}.xaxis[0].axis_label_text_font_size = "${specs.fontPixelSize}px"`, + `${chart}.xaxis[0].axis_label_text_color = "${specs.chartForegroundColor}"`, + `${chart}.xaxis[0].axis_label_standoff = ${specs.fontPixelSize - 5}`, + `${chart}.xaxis[0].axis_line_color = null`, + + `${chart}.xaxis[0].major_label_text_font = "PT Sans"`, + `${chart}.xaxis[0].major_label_text_font_size = "${specs.fontPixelSize}px"`, + `${chart}.xaxis[0].major_label_text_color = "${specs.chartForegroundColor}"`, + `${chart}.xaxis[0].major_label_standoff = 0`, + `${chart}.xaxis[0].major_tick_line_color = null`, + `${chart}.xaxis[0].major_tick_in = 0`, + `${chart}.xaxis[0].major_tick_out = 0`, + `${chart}.xaxis[0].minor_tick_line_color = null`, + `${chart}.xaxis[0].minor_tick_out = 0`, + + `${chart}.xgrid[0].grid_line_color = null`, + ] +} + +function bokehAddHiddenXAxis(chart, specs) { + return [ + `${chart}.xaxis[0].axis_label_text_font_size = "0px"`, + `${chart}.xaxis[0].axis_line_color = null`, + + `${chart}.xaxis[0].major_label_text_font_size = "0px"`, + `${chart}.xaxis[0].major_tick_line_color = "${specs.chartGridLineColor}"`, + `${chart}.xaxis[0].major_tick_in = 0`, + `${chart}.xaxis[0].major_tick_out = 0`, + `${chart}.xaxis[0].minor_tick_line_color = "${specs.chartMinorGridLineColor}"`, + `${chart}.xaxis[0].minor_tick_out = 0`, + + `${chart}.xgrid[0].grid_line_color = "${specs.chartGridLineColor}"`, + ] +} + +function bokehAddVisibleYAxis(chart, specs) { + return [ + `${chart}.yaxis[0].axis_label_text_font = "PT Sans"`, + `${chart}.yaxis[0].axis_label_text_font_style = "normal"`, + `${chart}.yaxis[0].axis_label_text_font_size = "${specs.fontPixelSize}px"`, + `${chart}.yaxis[0].axis_label_text_color = "${specs.chartForegroundColor}"`, + `${chart}.yaxis[0].axis_label_standoff = ${specs.fontPixelSize}`, + `${chart}.yaxis[0].axis_line_color = null`, + + `${chart}.yaxis[0].major_label_text_font = "PT Sans"`, + `${chart}.yaxis[0].major_label_text_font_size = "${specs.fontPixelSize}px"`, + `${chart}.yaxis[0].major_label_text_color = "${specs.chartForegroundColor}"`, + `${chart}.yaxis[0].major_tick_line_color = "${specs.chartGridLineColor}"`, + `${chart}.yaxis[0].major_tick_in = 0`, + `${chart}.yaxis[0].major_tick_out = 0`, + `${chart}.yaxis[0].minor_tick_line_color = "${specs.chartMinorGridLineColor}"`, + `${chart}.yaxis[0].minor_tick_out = 0`, + + `${chart}.ygrid[0].grid_line_color = "${specs.chartGridLineColor}"`, + ] +} + +function bokehAddHiddenYAxis(chart) { + return [ + `${chart}.yaxis[0].axis_line_color = null`, + + `${chart}.yaxis[0].major_tick_in = 0`, + `${chart}.yaxis[0].major_tick_out = 0`, + `${chart}.yaxis[0].minor_tick_out = 0`, + `${chart}.yaxis[0].major_label_text_font_size = "0px"`, + + `${chart}.ygrid[0].grid_line_color = null`, + ] +} + +// Bokeh data + +function bokehAddBackgroundDataToMainChart(data, specs) { + return [ + `main_source.data.x_bkg = [${data.background.x}]`, + `main_source.data.y_bkg = [${data.background.y}]`, + + 'const bkgLine = new Bokeh.Line({', + ' x: { field: "x_bkg" },', + ' y: { field: "y_bkg" },', + ` line_color: "${specs.backgroundLineColor}",`, + ` line_alpha: 0.5,`, + ` line_dash: [4, 2],`, + ` line_width: ${specs.backgroundLineWidth},`, + '})', + + 'main_chart.add_glyph(bkgLine, main_source)', + ] +} + +function bokehAddMeasuredDataToMainChart(data, specs) { + return [ + `main_source.data.x_meas = [${data.measured.x}]`, + `main_source.data.y_meas = [${data.measured.y}]`, + `main_source.data.sy_meas = [${data.measured.sy}]`, + `main_source.data.y_meas_upper = [${data.measured.y_upper}]`, + `main_source.data.y_meas_lower = [${data.measured.y_lower}]`, + + `const measLineTop = new Bokeh.Line({`, + ` x: { field: "x_meas" },`, + ` y: { field: "y_meas_upper" },`, + ` line_color: "${specs.measuredLineColor}",`, + ` line_alpha: 0.4,`, + ` line_width: ${specs.measuredLineWidth}`, + `})`, + `const measLineBottom = new Bokeh.Line({`, + ` x: { field: "x_meas" },`, + ` y: { field: "y_meas_lower" },`, + ` line_color: "${specs.measuredLineColor}",`, + ` line_width: ${specs.measuredLineWidth},`, + ` line_alpha: 0.4`, + `})`, + `const measArea = new Bokeh.VArea({`, + ` x: { field: "x_meas" },`, + ` y1: { field: "y_meas_upper" },`, + ` y2: { field: "y_meas_lower" },`, + ` fill_color: "${specs.measuredAreaColor}",`, + ` fill_alpha: 0.35`, + `})`, + + `main_chart.add_glyph(measArea, main_source)`, + `main_chart.add_glyph(measLineTop, main_source)`, + `main_chart.add_glyph(measLineBottom, main_source)`, + ] +} + +function bokehAddCalculatedDataToMainChart(data, specs) { + return [ + `main_source.data.x_calc = [${data.calculated.x}]`, + `main_source.data.y_calc = [${data.calculated.y}]`, + + 'const calcLine = new Bokeh.Line({', + ' x: { field: "x_calc" },', + ' y: { field: "y_calc" },', + ` line_color: "${specs.calculatedLineColor}",`, + ` line_width: ${specs.calculatedLineWidth},`, + ` line_alpha: 0.8`, + '})', + + 'main_chart.add_glyph(calcLine, main_source)', + ] +} + +function bokehAddPhaseDataToMainChart(data, specs) { + let out = [] + out.push('let phaseLine = new Bokeh.Line()') + for (const phase_index in data.phase) { + out.push( + `main_source.data.x_phase_${phase_index} = [${data.phase[phase_index].x}]`, + ) + out.push( + `main_source.data.y_phase_${phase_index}_upper = [${data.phase[phase_index].y_upper}]`, + ) + out.push( + `main_source.data.y_phase_${phase_index}_lower = [${data.phase[phase_index].y_lower}]`, + ) + out.push(`const phaseLine_${phase_index} = new Bokeh.Line({`) + out.push(` x: { field: "x_phase_${phase_index}" },`) + out.push(` y: { field: "y_phase_${phase_index}_upper" },`) + out.push(` line_color: "${specs.phaseLineColor[phase_index]}",`) + out.push(` line_width: ${specs.phaseLineWidth},`) + out.push(` line_alpha: 0.6,`) + out.push('})') + out.push(`const phaseArea_${phase_index} = new Bokeh.VArea({`) + out.push(` x: { field: "x_phase_${phase_index}" },`) + out.push(` y1: { field: "y_phase_${phase_index}_upper" },`) + out.push(` y2: { field: "y_phase_${phase_index}_lower" },`) + out.push(` fill_color: "${specs.phaseLineColor[phase_index]}",`) + out.push(` fill_alpha: 0.1`) + out.push('})') + out.push(`main_chart.add_glyph(phaseArea_${phase_index}, main_source)`) + out.push(`main_chart.add_glyph(phaseLine_${phase_index}, main_source)`) + } + return out +} + +function bokehAddDataToBraggChart(data, specs) { + let out = [] + for (const phase_index in data.bragg) { + console.log('---', phase_index, data.bragg[phase_index].x.slice(0, 200)) + out.push( + `const bragg_source_${phase_index} = new Bokeh.ColumnDataSource()`, + ) + + out.push( + `bragg_source_${phase_index}.data.x_bragg = [${data.bragg[phase_index].x}]`, + ) + out.push( + `bragg_source_${phase_index}.data.y_bragg = [${data.bragg[phase_index].y}]`, + ) + out.push( + `bragg_source_${phase_index}.data.h_bragg = [${data.bragg[phase_index].h}]`, + ) + out.push( + `bragg_source_${phase_index}.data.k_bragg = [${data.bragg[phase_index].k}]`, + ) + out.push( + `bragg_source_${phase_index}.data.l_bragg = [${data.bragg[phase_index].l}]`, + ) + + out.push(`const braggTicks_${phase_index} = new Bokeh.Scatter({`) + out.push(` x: { field: "x_bragg" },`) + out.push(` y: { field: "y_bragg" },`) + out.push(` marker: "dash",`) + out.push(` size: ${1.5 * specs.fontPixelSize},`) + out.push(` line_color: "${specs.braggTicksColor[phase_index]}",`) + out.push(` angle: ${Math.PI / 2}`) + out.push('})') + out.push( + `bragg_chart.add_glyph(braggTicks_${phase_index}, bragg_source_${phase_index})`, + ) + } + return out +} + +function bokehAddDataToDiffChart(data, specs) { + return [ + `main_source.data.x_diff = [${data.difference.x}]`, + `main_source.data.y_diff = [${data.difference.y}]`, + `main_source.data.y_diff_upper = [${data.difference.y_upper}]`, + `main_source.data.y_diff_lower = [${data.difference.y_lower}]`, + + `const diffLineTop = new Bokeh.Line({`, + ` x: { field: "x_diff" },`, + ` y: { field: "y_diff_upper" },`, + ` line_color: "${specs.differenceLineColor}",`, + ` line_alpha: 0.4,`, + ` line_width: ${specs.differenceLineWidth}`, + `})`, + `const diffLineBottom = new Bokeh.Line({`, + ` x: { field: "x_diff" },`, + ` y: { field: "y_diff_lower" },`, + ` line_color: "${specs.differenceLineColor}",`, + ` line_alpha: 0.4,`, + ` line_width: ${specs.differenceLineWidth}`, + `})`, + `const diffArea = new Bokeh.VArea({`, + ` x: { field: "x_diff" },`, + ` y1: { field: "y_diff_upper" },`, + ` y2: { field: "y_diff_lower" },`, + ` fill_color: "${specs.differenceAreaColor}",`, + ` fill_alpha: 0.35`, + `})`, + + `diff_chart.add_glyph(diffArea, main_source)`, + `diff_chart.add_glyph(diffLineTop, main_source)`, + `diff_chart.add_glyph(diffLineBottom, main_source)`, + ] +} + +// Bokeh tooltips + +function bokehMainTooltipRow(color, label, value, sigma = '') { + return [ + ``, + ` ${label}: `, + ` ${value}`, + ` ${sigma}`, + ``, + ] +} + +function bokehAddMainTooltip(data, specs) { + const x_meas = bokehMainTooltipRow( + EaStyle.Colors.themeForegroundDisabled, + 'x', + '@x_meas{0.00}', + ) + const x_calc = bokehMainTooltipRow( + EaStyle.Colors.themeForegroundDisabled, + 'x', + '@x_calc{0.00}', + ) + const y_meas = bokehMainTooltipRow( + specs.measuredLineColor, + 'meas', + '@y_meas{0.0}', + '± @sy_meas{0.0}', + ) + const y_calc = bokehMainTooltipRow( + specs.calculatedLineColor, + 'calc', + '@y_calc{0.0}', + ) + const y_bkg = bokehMainTooltipRow( + specs.backgroundLineColor, + 'bkg', + '@y_bkg{0.0}', + ) + const y_diff = bokehMainTooltipRow( + specs.differenceLineColor, + 'diff', + '@y_diff{0.0}', + ) + let y_phases = [] + for (const phase_index in data.phase) { + if (typeof specs.phaseLineColor !== 'undefined') { + y_phases.push( + bokehMainTooltipRow( + specs.phaseLineColor[phase_index], + `phase ${phase_index}`, + `@y_phase_${phase_index}_upper{0.0}`, + ), + ) + } + } + + let table = [] + table.push(...[`
`, ``, ``]) + // x + if (data.hasMeasured) { + table.push(...x_meas) + } else if (data.hasCalculated) { + table.push(...x_calc) + } + // y + if (data.hasMeasured) { + table.push(...y_meas) + } + if (data.hasCalculated) { + table.push(...y_calc) + } + for (const phase_index in data.phase) { + if (typeof y_phases[phase_index] !== 'undefined') { + table.push(...y_phases[phase_index]) + } + } + if (data.hasBackground) { + table.push(...y_bkg) + } + if (data.hasDifference) { + table.push(...y_diff) + } + table.push(...[``, `
`, `
`]) + + const tooltip = JSON.stringify(table.join('\n')) + return `const main_tooltip = (${tooltip})` +} + +function bokehBraggTooltipSpan(color, label, value) { + return `${label}: ${value}` +} + +function bokehAddBraggTooltip(specs) { + const x_bragg = bokehBraggTooltipSpan( + EaStyle.Colors.themeForegroundDisabled, + 'x', + '@x_bragg{0.00}', + ) + const hkl_bragg = bokehBraggTooltipSpan( + specs.calculatedLineColor, + 'hkl', + '(@h_bragg @k_bragg @l_bragg)', + ) + + const table = [ + `
`, + x_bragg, + ' ', + hkl_bragg, + `
`, + ] + + const tooltip = JSON.stringify(table.join('\n')) + return `const bragg_tooltip = (${tooltip})` +} diff --git a/src/easyapplication/Gui/Logic/ProjectConfig.js b/src/easyapplication/Gui/Logic/ProjectConfig.js index 0c88b8e5..41e01d8f 100644 --- a/src/easyapplication/Gui/Logic/ProjectConfig.js +++ b/src/easyapplication/Gui/Logic/ProjectConfig.js @@ -1,29 +1,29 @@ -function projectConfig() { - return { - tool: { - poetry: { - name: "easyDiffraction", - version: "0.8.0-beta.1" - } - }, - ci: { - app: { - info: { - date: '01.01.2001', - branch_name: 'master', - commit_sha_short: 'undefined' - }, - tutorials: { - video: { - fps: 15 - } - } - }, - project: { - subdirs: { - screenshots: "" - } - } - } - } -} +function projectConfig() { + return { + tool: { + poetry: { + name: 'easyDiffraction', + version: '0.8.0-beta.1', + }, + }, + ci: { + app: { + info: { + date: '01.01.2001', + branch_name: 'master', + commit_sha_short: 'undefined', + }, + tutorials: { + video: { + fps: 15, + }, + }, + }, + project: { + subdirs: { + screenshots: '', + }, + }, + }, + } +} diff --git a/src/easyapplication/Gui/Logic/Translate.js b/src/easyapplication/Gui/Logic/Translate.js index 6739f42b..1620c1ab 100644 --- a/src/easyapplication/Gui/Logic/Translate.js +++ b/src/easyapplication/Gui/Logic/Translate.js @@ -1,14 +1,13 @@ -class Translator { - - // Functions - - languagesAsXml() { - return "enEnglish" - } - - defaultLanguageIndex() { - return 0 - } - - selectLanguage(index) {} -} +class Translator { + // Functions + + languagesAsXml() { + return 'enEnglish' + } + + defaultLanguageIndex() { + return 0 + } + + selectLanguage(index) {} +} diff --git a/src/easyapplication/Gui/Logic/Utils.js b/src/easyapplication/Gui/Logic/Utils.js index ecfb6e96..e80310a3 100644 --- a/src/easyapplication/Gui/Logic/Utils.js +++ b/src/easyapplication/Gui/Logic/Utils.js @@ -1,154 +1,137 @@ -function prettyXml(xml, tab = ' ') -{ - let formatted = '' - let indent = '' +function prettyXml(xml, tab = ' ') { + let formatted = '' + let indent = '' - xml.split(/>\s*\s*\r\n' + formatted += indent + '<' + node + '>\r\n' - if (node.match( /^]*[^\/]$/ )) - indent += tab - }) + if (node.match(/^]*[^\/]$/)) indent += tab + }) - return formatted.substring(1, formatted.length-3); + return formatted.substring(1, formatted.length - 3) } -function prettyJson(json, tab = ' ') -{ - return JSON.stringify(json, null, tab.length) +function prettyJson(json, tab = ' ') { + return JSON.stringify(json, null, tab.length) } - function osPathSep() { - if (Qt.platform.os === "windows") { - return '\\' - } - return '/' -} + if (Qt.platform.os === 'windows') { + return '\\' + } + return '/' +} // converts a URL to a local file path -function urlToLocalFile(url) -{ - if (Qt.platform.os === "windows") - { - return url.replace('file:///', '').split('/').join('\\') - } - else if (Qt.platform.os === "osx" || Qt.platform.os === "linux" || Qt.platform.os === "unix") - { - return url.replace('file://', '') - } - else - { - return url - } +function urlToLocalFile(url) { + if (Qt.platform.os === 'windows') { + return url.replace('file:///', '').split('/').join('\\') + } else if ( + Qt.platform.os === 'osx' || + Qt.platform.os === 'linux' || + Qt.platform.os === 'unix' + ) { + return url.replace('file://', '') + } else { + return url + } } -function isQmlScene() -{ - const runnerPath = Qt.application.arguments[0] - if (runnerPath.includes('qmlscene')) - { - return true - } - return false +function isQmlScene() { + const runnerPath = Qt.application.arguments[0] + if (runnerPath.includes('qmlscene')) { + return true + } + return false } // XMLHttpRequest: Using PUT on a local file is disabled by default. // Set QML_XHR_ALLOW_FILE_WRITE to 1 to enable this feature. // E.g., 'QML_XHR_ALLOW_FILE_WRITE=1 qml main.qml' function writeFile(url, content) { - const method = "PUT" - const async = false - const request = new XMLHttpRequest() - request.onreadystatechange = () => { - if (request.readyState === XMLHttpRequest.DONE) { - if (request.status === 200) { - print(`Succeeded to write file: '${url}'`) - } else { - print(`Failed to write file: '${url}'. Status: ${request.status}`) - } - } + const method = 'PUT' + const async = false + const request = new XMLHttpRequest() + request.onreadystatechange = () => { + if (request.readyState === XMLHttpRequest.DONE) { + if (request.status === 200) { + print(`Succeeded to write file: '${url}'`) + } else { + print(`Failed to write file: '${url}'. Status: ${request.status}`) + } } - request.open(method, url, async) - request.send(content) + } + request.open(method, url, async) + request.send(content) } // XMLHttpRequest: Using GET on a local file is disabled by default. // Set QML_XHR_ALLOW_FILE_READ to 1 to enable this feature. // E.g., 'QML_XHR_ALLOW_FILE_READ=1 qml main.qml' function readFile(url) { - const method = "GET" - const async = false - const request = new XMLHttpRequest() - request.onreadystatechange = () => { - if (request.readyState === XMLHttpRequest.DONE) { - if (request.status === 200) { - print(`Succeeded to read file: '${url}'`) - } else { - print(`Failed to read file: '${url}'. Status: ${request.status}`) - } - } + const method = 'GET' + const async = false + const request = new XMLHttpRequest() + request.onreadystatechange = () => { + if (request.readyState === XMLHttpRequest.DONE) { + if (request.status === 200) { + print(`Succeeded to read file: '${url}'`) + } else { + print(`Failed to read file: '${url}'. Status: ${request.status}`) + } } - request.open(method, url, async) - request.send() - return request.responseText + } + request.open(method, url, async) + request.send() + return request.responseText } - - - -function toFixed(value, digitsCount = 4) -{ - if (typeof value === 'undefined') - { - return "" - } - else if (typeof value == 'number') - { - return value.toFixed(digitsCount) - } - else - { - return value - } +function toFixed(value, digitsCount = 4) { + if (typeof value === 'undefined') { + return '' + } else if (typeof value == 'number') { + return value.toFixed(digitsCount) + } else { + return value + } } function toMaxPrecision(value, digitsCount = 5) { - if (typeof value === 'number') { - return Number(value.toPrecision(digitsCount)).toString() - } else if (typeof value === 'string') { - return value - } else if (typeof value === 'undefined') { - return "" - } else { - console.error(`Value ${value} with type '${typeof value}' is not supported`) - return "" - } + if (typeof value === 'number') { + return Number(value.toPrecision(digitsCount)).toString() + } else if (typeof value === 'string') { + return value + } else if (typeof value === 'undefined') { + return '' + } else { + console.error( + `Value ${value} with type '${typeof value}' is not supported`, + ) + return '' + } } function toDefaultPrecision(x) { - const defaultPrecision = 3 - return Number(x.toPrecision(defaultPrecision)).toString() + const defaultPrecision = 3 + return Number(x.toPrecision(defaultPrecision)).toString() } function toSinglePrecision(x) { - if (x === 0) { - return '' - } - return Number(x.toPrecision(1)).toString() + if (x === 0) { + return '' + } + return Number(x.toPrecision(1)).toString() } function toErrSinglePrecision(x, dx) { - dx = Number(dx.toPrecision(1)) - return toFixedUncertainty(x, dx) + dx = Number(dx.toPrecision(1)) + return toFixedUncertainty(x, dx) } function toSamePrecision(x, y) { - return toFixedUncertainty(x, y) + return toFixedUncertainty(x, y) } // https://gist.github.com/davidselassie/3838522 @@ -193,8 +176,8 @@ function toFixedUncertainty(x, dx) { // If there is no uncertainty, return the entire number if (dx === undefined || dx === 0) { - ////return x.toString(); - return toDefaultPrecision(x) + ////return x.toString(); + return toDefaultPrecision(x) } // Find out the least significant digit using the uncertainty @@ -209,14 +192,14 @@ function toFixedUncertainty(x, dx) { // Find out if we want more significant digits that we have var overshot = roundingIndex - roundedString.length + 1 if (overshot <= 0) { - return roundedString; - // If so, add more 0s on the beginning. + return roundedString + // If so, add more 0s on the beginning. } else { - return Array(overshot + 1).join('0') + roundedString; + return Array(overshot + 1).join('0') + roundedString } - // If we're rounding to a decimal place + // If we're rounding to a decimal place } else { - var decimalIndex = roundedString.indexOf('.'); + var decimalIndex = roundedString.indexOf('.') // If there isn't a '.', we're rounding to 0 if we got here. if (decimalIndex < 0) { roundedString = '0.' @@ -228,14 +211,13 @@ function toFixedUncertainty(x, dx) { var overshot = lastIndex - roundedString.length if (overshot <= 0) { return roundedString.slice(0, lastIndex) - // If so, add more 0s to show the precision we have. + // If so, add more 0s to show the precision we have. } else { return roundedString + Array(overshot + 1).join('0') } } } - // Consider using python backend // Either // from uncertainties import ufloat diff --git a/src/easyapplication/Logic/Logging.py b/src/easyapplication/Logic/Logging.py index 7b0b28b2..3b3527e5 100644 --- a/src/easyapplication/Logic/Logging.py +++ b/src/easyapplication/Logic/Logging.py @@ -1,29 +1,30 @@ -# SPDX-FileCopyrightText: 2024 EasyApp contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause -# © 2024 Contributors to the EasyApp project - -# https://docs.python.org/3/library/logging.html -# https://docs.python.org/3/howto/logging-cookbook.html -# https://stackoverflow.com/questions/384076/how-can-i-color-python-logging-output -# https://stackoverflow.com/questions/42936810/python-logging-module-set-formatter-dynamically -# https://doc.qt.io/qt-6/qtquick-debugging.html -# https://raymii.org/s/articles/Disable_logging_in_QT_and_QML.html +import inspect +import logging import os +import pathlib import sys import time -import pathlib -import inspect -import logging - -from PySide6.QtCore import QObject, Signal, Property, QtMsgType, QUrl, QSettings +from PySide6.QtCore import Property +from PySide6.QtCore import QObject +from PySide6.QtCore import QSettings +from PySide6.QtCore import QtMsgType +from PySide6.QtCore import QUrl +from PySide6.QtCore import Signal LOGGER_LEVELS = { 'disabled': 40, - 'error': 30, # logging.CRITICAL, logging.ERROR, QtMsgType.QtSystemMsg, QtMsgType.QtCriticalMsg, QtMsgType.QtFatalMsg - 'info': 20, # logging.INFO, logging.WARNING, QtMsgType.QtInfoMsg, QtMsgType.QtWarningMsg - 'debug': 10 # logging.NOTSET, logging.DEBUG, QtMsgType.QtDebugMsg + # logging.CRITICAL, logging.ERROR, QtMsgType.QtSystemMsg, + # QtMsgType.QtCriticalMsg, QtMsgType.QtFatalMsg + 'error': 30, + # logging.INFO, logging.WARNING, QtMsgType.QtInfoMsg, + # QtMsgType.QtWarningMsg + 'info': 20, + # logging.NOTSET, logging.DEBUG, QtMsgType.QtDebugMsg + 'debug': 10, } COUNT_WIDTH = 5 @@ -32,15 +33,14 @@ FUNC_NAME_WIDTH = 34 MAIN_MSG_WIDTH = 150 -class Logger: +class Logger: def __init__(self): self._count = 0 self._level = self._getLevelFromSettings() self._getLevelFromSettings() self._startTime = time.time() - #self._consoleFormat = logging.Formatter('{asctime}.{msecs:03.0f} {message}', datefmt='%H:%M:%S', style='{') self._consoleFormat = logging.Formatter('{message}', datefmt='%H:%M:%S', style='{') self._consoleHandler = logging.StreamHandler() self._consoleHandler.setFormatter(self._consoleFormat) @@ -98,7 +98,7 @@ def _getLevelFromSettings(self): settingsIniFileName = 'settings.ini' settingsIniFilePath = str(homeDirPath.joinpath(f'.{appName}', settingsIniFileName)) settings = QSettings(settingsIniFilePath, QSettings.IniFormat) - level = settings.value("Preferences.Develop/loggingLevel", 'debug') + level = settings.value('Preferences.Develop/loggingLevel', 'debug') level = level.lower() return level @@ -150,9 +150,13 @@ def _formattedConsoleMsg(self, msg, level, category, funcName, filePath, lineNo) relativePath = os.path.relpath(filePath, start) fileUrl = f'file:///{relativePath}' sourceUrl = f'{fileUrl}:{lineNo}' - except: + except: # noqa pass - txt = f'{self._count:>{COUNT_WIDTH}d} {self._timing()} {category:>{CATEGORY_WIDTH}} {level:<{LEVEL_WIDTH}} {msg:<{MAIN_MSG_WIDTH}.{MAIN_MSG_WIDTH}} {funcName:<{FUNC_NAME_WIDTH}.{FUNC_NAME_WIDTH}} {sourceUrl}' + txt = ( + f'{self._count:>{COUNT_WIDTH}d} {self._timing()} {category:>{CATEGORY_WIDTH}} ' + f'{level:<{LEVEL_WIDTH}} {msg:<{MAIN_MSG_WIDTH}.{MAIN_MSG_WIDTH}} ' + f'{funcName:<{FUNC_NAME_WIDTH}.{FUNC_NAME_WIDTH}} {sourceUrl}' + ) txt = self._colorize(txt, level, category) return txt @@ -164,12 +168,12 @@ def qtMsgTypeToCustomLevel(msgType): QtMsgType.QtWarningMsg: 'info', QtMsgType.QtCriticalMsg: 'error', QtMsgType.QtSystemMsg: 'error', - QtMsgType.QtFatalMsg: 'error' + QtMsgType.QtFatalMsg: 'error', }[msgType] @staticmethod def rest(s, n): - splitted = [' '*28*bool(i) + s[i: i + n] for i in range(0, len(s), n)] + splitted = [' ' * 28 * bool(i) + s[i : i + n] for i in range(0, len(s), n)] joined = '\n'.join(splitted[1:]) return joined diff --git a/src/easyapplication/Logic/Maintenance.py b/src/easyapplication/Logic/Maintenance.py index 76aca379..567b2e9a 100644 --- a/src/easyapplication/Logic/Maintenance.py +++ b/src/easyapplication/Logic/Maintenance.py @@ -1,17 +1,22 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause + import os import pathlib import re -import urllib.request import sys - -from PySide6.QtCore import QObject, QProcess, Property, Signal, Slot -from PySide6.QtWidgets import QApplication +import urllib.request from EasyApp.Logic.Logging import console +from PySide6.QtCore import Property +from PySide6.QtCore import QObject +from PySide6.QtCore import QProcess +from PySide6.QtCore import Signal +from PySide6.QtCore import Slot +from PySide6.QtWidgets import QApplication class Updater(QObject): - # SIGNALS updateFound = Signal() @@ -31,10 +36,10 @@ def __init__(self, parent=None): # private members self._silent_check = True - self._error_message = "" - self._web_version = "" - self._web_date = "" - self._release_notes = "" + self._error_message = '' + self._web_version = '' + self._web_date = '' + self._release_notes = '' self._process = self._createUpdaterProcess() # connections @@ -46,12 +51,12 @@ def __init__(self, parent=None): @Slot() def checkUpdate(self): - console.debug("Updater checkUpdate called") + console.debug('Updater checkUpdate called') if self._process.state() == QProcess.Running: return - self._process.setArguments(["--checkupdates", "--verbose"]) + self._process.setArguments(['--checkupdates', '--verbose']) self._process.start() @Slot() @@ -59,13 +64,13 @@ def installUpdate(self): """ Start the external maintenance tool as detached process """ - console.debug("Updater installUpdate called") + console.debug('Updater installUpdate called') if self._process.state() == QProcess.Running: return program = os.path.join(self._process.workingDirectory(), self._process.program()) - arguments = ["--updater", "--verbose"] + arguments = ['--updater', '--verbose'] updater_started = QProcess.startDetached(program, arguments) @@ -111,16 +116,19 @@ def _createUpdaterProcess(self): return process def _onStarted(self): - console.debug("Updater process started") - self._web_version = "" - self._web_date = "" - self._error_message = "" + console.debug('Updater process started') + self._web_version = '' + self._web_date = '' + self._error_message = '' self.webVersionChanged.emit() self.webDateChanged.emit() self.errorMessageChanged.emit() def _onFinished(self, exit_code: int, exit_status: QProcess.ExitStatus): - console.debug(f"Updater process finished with exit code: '{exit_code}' and exit status: '{exit_status}'") + console.debug( + f'Updater process finished with exit code: ' + f"'{exit_code}' and exit status: '{exit_status}'" + ) # Get updater process output and error, if any std_out = self._process.readAllStandardOutput().data().decode('utf-8') @@ -128,21 +136,24 @@ def _onFinished(self, exit_code: int, exit_status: QProcess.ExitStatus): # Debug printing if std_out: - console.debug(f"Updater standard output:\n{std_out}") + console.debug(f'Updater standard output:\n{std_out}') if std_err: - console.debug(f"Updater standard error:\n{std_err}") + console.debug(f'Updater standard error:\n{std_err}') # Something went wrong if exit_code != 0 or exit_status != QProcess.ExitStatus.NormalExit: - console.debug("Updater process failed") - self._error_message = f"Updater process finished with\n* exit code: {exit_code} \n* exit status: {exit_status}" + console.debug('Updater process failed') + self._error_message = ( + f'Updater process finished with\n* exit code: ' + f'{exit_code} \n* exit status: {exit_status}' + ) self.errorMessageChanged.emit() if not self.silentCheck: self.updateFailed.emit() return # Process finished succesfully - console.debug("Updater process succeeded; checking for updates...") + console.debug('Updater process succeeded; checking for updates...') # Check if a new version of any of the app component is found pattern = r'' @@ -150,13 +161,13 @@ def _onFinished(self, exit_code: int, exit_status: QProcess.ExitStatus): # No new versions are found if not matches: - console.debug("Updater did not find any updates") + console.debug('Updater did not find any updates') if not self.silentCheck: self.updateNotFound.emit() return # New version is found - console.debug(f"Updater found component(s) with new version(s): {matches}") + console.debug(f'Updater found component(s) with new version(s): {matches}') self._web_version = matches[0] # TODO: Update this if multiple components are available self._web_date = self._getWebDate() self._release_notes = self._getReleaseNotes() @@ -179,27 +190,28 @@ def _getAppChangelog(self): try: return pathlib.Path(path).read_text() except Exception as exception: - console.debug(f"Failed to read local file {path} with exception {exception}") - return "" + console.debug(f'Failed to read local file {path} with exception {exception}') + return '' def _getWebChangelog(self): url = Updater.webChangelogUrl() try: - with urllib.request.urlopen(url) as f: + with urllib.request.urlopen(url) as f: # noqa return f.read().decode('utf-8') except Exception as exception: - console.debug(f"Failed to read web file {url} with exception {exception}") - return "" + console.debug(f'Failed to read web file {url} with exception {exception}') + return '' def _getReleaseNotes(self): app_changelog = self._getAppChangelog() web_changelog = self._getWebChangelog() # remove overlapping part - release_notes = web_changelog.replace(app_changelog, "") - # TODO: Temporary solution to change default headers size and and empty lines (QML Text.MarkdownText) - release_notes = re.sub(r'\n### ', "\n____\n#### ", release_notes) - release_notes = re.sub(r'\n# ', "\n____\n____\n### ", release_notes) - release_notes = re.sub(r'^# ', "### ", release_notes) + release_notes = web_changelog.replace(app_changelog, '') + # TODO: Temporary solution to change default headers size and + # empty lines (QML Text.MarkdownText) + release_notes = re.sub(r'\n### ', '\n____\n#### ', release_notes) + release_notes = re.sub(r'\n# ', '\n____\n____\n### ', release_notes) + release_notes = re.sub(r'^# ', '### ', release_notes) return release_notes def _getWebDate(self): @@ -214,30 +226,30 @@ def _getWebDate(self): @staticmethod def exeRelativePath(): if sys.platform.startswith('win'): - return "..\\MaintenanceTool.exe" + return '..\\MaintenanceTool.exe' elif sys.platform.startswith('darwin'): - return "../../../MaintenanceTool.app/Contents/MacOS/MaintenanceTool" + return '../../../MaintenanceTool.app/Contents/MacOS/MaintenanceTool' else: - return "../MaintenanceTool" + return '../MaintenanceTool' @staticmethod def appChangelogPath(): if sys.platform.startswith('win'): - relative_path = "..\\CHANGELOG.md" + relative_path = '..\\CHANGELOG.md' elif sys.platform.startswith('darwin'): - relative_path = "../../../CHANGELOG.md" + relative_path = '../../../CHANGELOG.md' else: - relative_path = "../CHANGELOG.md" + relative_path = '../CHANGELOG.md' path = os.path.join(QApplication.applicationDirPath(), relative_path) return path @staticmethod def webChangelogUrl(): if sys.platform.startswith('win'): - os_dir = "Windows" + os_dir = 'Windows' elif sys.platform.startswith('darwin'): - os_dir = "macOS" + os_dir = 'macOS' else: - os_dir = "Linux" + os_dir = 'Linux' url = f'https://download.easydiffraction.org/onlineRepository/{os_dir}/CHANGELOG.md' return url diff --git a/src/easyapplication/Logic/Translate.py b/src/easyapplication/Logic/Translate.py index 21a0da69..717b0a43 100644 --- a/src/easyapplication/Logic/Translate.py +++ b/src/easyapplication/Logic/Translate.py @@ -1,11 +1,18 @@ -__author__ = "github.com/AndrewSazonov" -__version__ = "0.0.1" +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause + import os -from PySide6.QtCore import QObject, QTranslator, QLocale, Property, Slot -#from PySide6.QtWidgets import QApplication -#from PySide6.QtQml import QQmlApplicationEngine +from PySide6.QtCore import Property +from PySide6.QtCore import QLocale +from PySide6.QtCore import QObject +from PySide6.QtCore import QTranslator +from PySide6.QtCore import Slot + +# from PySide6.QtWidgets import QApplication +# from PySide6.QtQml import QQmlApplicationEngine + class Translator(QObject): def __init__(self, app, engine, translations_path, languages): diff --git a/src/easyapplication/Logic/Utils/Utils.py b/src/easyapplication/Logic/Utils/Utils.py index 698e76ec..a32c959f 100644 --- a/src/easyapplication/Logic/Utils/Utils.py +++ b/src/easyapplication/Logic/Utils/Utils.py @@ -1,19 +1,22 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause + import os import sys from urllib.parse import urlparse - # Utils + def generalize_path(fpath: str) -> str: """ - Generalize the filepath to be platform-specific, so all file operations - can be performed. + Generalize the filepath to be platform-specific, so all file + operations can be performed. :param URI fpath: URI to the file :return URI filename: platform specific URI """ filename = urlparse(fpath).path - if not sys.platform.startswith("win"): + if not sys.platform.startswith('win'): return filename if filename[0] == '/': filename = filename[1:].replace('/', os.path.sep) diff --git a/src/easyapplication/Logic/Utils/__init__.py b/src/easyapplication/Logic/Utils/__init__.py index e69de29b..4e798e20 100644 --- a/src/easyapplication/Logic/Utils/__init__.py +++ b/src/easyapplication/Logic/Utils/__init__.py @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause diff --git a/src/easyapplication/Logic/__init__.py b/src/easyapplication/Logic/__init__.py index e69de29b..4e798e20 100644 --- a/src/easyapplication/Logic/__init__.py +++ b/src/easyapplication/Logic/__init__.py @@ -0,0 +1,2 @@ +# SPDX-FileCopyrightText: 2026 EasyScience contributors +# SPDX-License-Identifier: BSD-3-Clause diff --git a/tests/functional/test_dummy.py b/tests/functional/test_dummy.py index 32360eee..6927fe89 100644 --- a/tests/functional/test_dummy.py +++ b/tests/functional/test_dummy.py @@ -1,6 +1,7 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyApplication contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause + def test_dummy(): calculated = 2 + 2 expected = 4 diff --git a/tests/integration/fitting/test_dummy.py b/tests/integration/fitting/test_dummy.py index 78ed65c9..2256189c 100644 --- a/tests/integration/fitting/test_dummy.py +++ b/tests/integration/fitting/test_dummy.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyApplication contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import pytest @@ -14,4 +14,4 @@ def test_dummy_fast(): def test_dummy_slow(): calculated = sum(i * j for i in range(10000) for j in range(10000)) expected = 2499500025000000 - assert calculated == expected \ No newline at end of file + assert calculated == expected diff --git a/tests/integration/scipp-analysis/test_dummy.py b/tests/integration/scipp-analysis/test_dummy.py index 78ed65c9..2256189c 100644 --- a/tests/integration/scipp-analysis/test_dummy.py +++ b/tests/integration/scipp-analysis/test_dummy.py @@ -1,4 +1,4 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyApplication contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause import pytest @@ -14,4 +14,4 @@ def test_dummy_fast(): def test_dummy_slow(): calculated = sum(i * j for i in range(10000) for j in range(10000)) expected = 2499500025000000 - assert calculated == expected \ No newline at end of file + assert calculated == expected diff --git a/tests/unit/test_dummy.py b/tests/unit/test_dummy.py index 32360eee..6927fe89 100644 --- a/tests/unit/test_dummy.py +++ b/tests/unit/test_dummy.py @@ -1,6 +1,7 @@ -# SPDX-FileCopyrightText: 2021-2026 EasyApplication contributors +# SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause + def test_dummy(): calculated = 2 + 2 expected = 4 From 691f6d2abe7d20b33f6f579356fd5f51f2a09550 Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 29 Apr 2026 16:17:37 +0200 Subject: [PATCH 4/7] Capitalize name --- pixi.lock | 4 ++-- pyproject.toml | 4 ++-- .../Gui/Animations/ColorReset.qml | 0 .../Gui/Animations/ThemeChange.qml | 0 .../Gui/Animations/TranslationChange.qml | 0 .../Gui/Animations/qmldir | 0 .../Gui/Charts/ChartTemplateHeatmap2dPlotly.html | 0 .../Gui/Charts/ChartTemplateSimple1dPlotly.html | 0 .../Gui/Charts/ChartViewHeatmap2dPlotly.qml | 4 ---- .../Gui/Charts/ChartViewSimple1dPlotly.qml | 4 ---- .../Gui/Charts/Plotly1dBarPlot.qml | 0 .../Gui/Charts/Plotly1dLine.qml | 0 .../Gui/Charts/Plotly1dMeasVsCalc.qml | 0 .../Gui/Charts/Plotly2dHeatmap.qml | 0 .../Gui/Charts/Plotly2dPolarHeatmap.qml | 0 .../Gui/Charts/Plotly3dScatter.qml | 0 .../Gui/Charts/Plotly3dSurface.qml | 0 .../Gui/Charts/QtCharts1dBase 2.qml | 0 .../Gui/Charts/QtCharts1dBase.qml | 0 .../Gui/Charts/QtCharts1dMeasVsCalc.qml | 0 .../Gui/Charts/QtCharts1dValueAxis.qml | 0 .../Gui/Charts/qmldir | 0 .../Gui/Components/AboutDialog.qml | 0 .../Gui/Components/AppBarCentralTabs.qml | 0 .../Gui/Components/AppBarLeftButtons.qml | 0 .../Gui/Components/AppBarRightButtons.qml | 0 .../Gui/Components/ApplicationWindow.qml | 0 .../Gui/Components/BasicReport.qml | 0 .../Gui/Components/ContentArea.qml | 0 .../Gui/Components/ContentPage.qml | 0 .../Gui/Components/GuideWindow.qml | 0 .../Gui/Components/GuideWindowContainer.qml | 0 .../Gui/Components/JsonListModel.qml | 0 .../Gui/Components/MainContent.qml | 0 .../Gui/Components/PreferencesDialog.qml | 0 .../Gui/Components/ProjectDescriptionDialog.qml | 0 .../Gui/Components/SideBar.qml | 0 .../Gui/Components/SideBarColumn.qml | 0 .../Gui/Components/TableView.qml | 0 .../Gui/Components/TableViewAdvancedLabel.qml | 0 .../Gui/Components/TableViewButton.qml | 0 .../Gui/Components/TableViewCheckBox.qml | 0 .../Gui/Components/TableViewComboBox.qml | 0 .../Gui/Components/TableViewDelegate.qml | 0 .../Gui/Components/TableViewHeader.qml | 0 .../Gui/Components/TableViewLabel.qml | 0 .../Gui/Components/TableViewLabelControl.qml | 0 .../Gui/Components/TableViewParameter.qml | 0 .../Gui/Components/TableViewTextInput.qml | 0 .../Components/TableViewTwoRowsAdvancedLabel.qml | 0 .../Gui/Components/qmldir | 0 .../Gui/Elements/AppBarTabButton.qml | 0 .../Gui/Elements/ApplicationWindow.qml | 0 .../Gui/Elements/Button.qml | 0 .../Gui/Elements/CheckBox.qml | 0 .../Gui/Elements/CheckIndicator.qml | 0 .../Gui/Elements/ComboBox.qml | 0 .../Gui/Elements/CursorDelegate.qml | 0 .../Gui/Elements/Dialog.qml | 0 .../Gui/Elements/DialogButtonBox.qml | 0 .../Gui/Elements/GroupBox.qml | 0 .../Gui/Elements/GroupButton.qml | 0 .../Gui/Elements/GroupColumn.qml | 0 .../Gui/Elements/GroupRow.qml | 0 .../Gui/Elements/Label.qml | 0 .../Gui/Elements/LinkedImage.qml | 0 .../Gui/Elements/Menu.qml | 0 .../Gui/Elements/MenuItem.qml | 0 .../Gui/Elements/ParamComboBox.qml | 0 .../Gui/Elements/ParamTextField.qml | 0 .../Gui/Elements/Parameter.qml | 0 .../Gui/Elements/RadioButton.qml | 0 .../Gui/Elements/RadioIndicator.qml | 0 .../Gui/Elements/RemoteController.qml | 0 .../Gui/Elements/RemotePointer.qml | 0 .../Gui/Elements/RunningLabel.qml | 0 .../Gui/Elements/ScrollBar.qml | 0 .../Gui/Elements/ScrollIndicator.qml | 0 .../Gui/Elements/SideBarButton.qml | 0 .../Gui/Elements/Slider.qml | 0 .../Gui/Elements/SliderHandle.qml | 0 .../Gui/Elements/SpinBox.qml | 0 .../Gui/Elements/SplashScreen.qml | 0 .../Gui/Elements/StatusBar.qml | 0 .../Gui/Elements/StatusBarItem.qml | 0 .../Gui/Elements/TabBar.qml | 0 .../Gui/Elements/TabButton.qml | 0 .../Gui/Elements/TextArea.qml | 0 .../Gui/Elements/TextField.qml | 0 .../Gui/Elements/TextInput.qml | 0 .../Gui/Elements/ToolButton.qml | 0 .../Gui/Elements/ToolTip.qml | 0 .../Gui/Elements/ToolTipShadow.qml | 0 .../Gui/Elements/qmldir | 0 .../Gui/Globals/Vars.qml | 0 .../Gui/Globals/qmldir | 0 .../Gui/Html/BasicReport.html | 0 .../Gui/Html/BlankPage.html | 0 .../Gui/Html/PTSans-Bold.ttf | Bin .../Gui/Html/PTSans-Regular.ttf | Bin .../Gui/Html/Plotly1dBarPlot.html | 0 .../Gui/Html/Plotly1dLine.html | 0 .../Gui/Html/Plotly1dMeasVsCalc.html | 0 .../Gui/Html/Plotly2dHeatmap.html | 0 .../Gui/Html/Plotly2dPolarHeatmap.html | 0 .../Gui/Html/Plotly3dMesh.html | 0 .../Gui/Html/Plotly3dScatter.html | 0 .../Gui/Html/Plotly3dSurface.html | 0 .../Gui/Html/plotly-2.18.0.min.js | 0 .../Gui/Logic/Plotting.js | 0 .../Gui/Logic/ProjectConfig.js | 0 .../Gui/Logic/Translate.js | 0 .../Gui/Logic/Utils.js | 0 .../Gui/Logic/qmldir | 0 .../Fonts/Encode_Sans/EncodeSans-Black.ttf | Bin .../Resources/Fonts/Encode_Sans/EncodeSans-Bold.ttf | Bin .../Fonts/Encode_Sans/EncodeSans-ExtraBold.ttf | Bin .../Fonts/Encode_Sans/EncodeSans-ExtraLight.ttf | Bin .../Fonts/Encode_Sans/EncodeSans-Light.ttf | Bin .../Fonts/Encode_Sans/EncodeSans-Medium.ttf | Bin .../Fonts/Encode_Sans/EncodeSans-Regular.ttf | Bin .../Fonts/Encode_Sans/EncodeSans-SemiBold.ttf | Bin .../Resources/Fonts/Encode_Sans/EncodeSans-Thin.ttf | Bin .../Gui/Resources/Fonts/Encode_Sans/OFL.txt | 0 .../Gui/Resources/Fonts/Encode_Sans/download.txt | 0 .../EncodeSansCondensed-Black.ttf | Bin .../EncodeSansCondensed-Bold.ttf | Bin .../EncodeSansCondensed-ExtraBold.ttf | Bin .../EncodeSansCondensed-ExtraLight.ttf | Bin .../EncodeSansCondensed-Light.ttf | Bin .../EncodeSansCondensed-Medium.ttf | Bin .../EncodeSansCondensed-Regular.ttf | Bin .../EncodeSansCondensed-SemiBold.ttf | Bin .../EncodeSansCondensed-Thin.ttf | Bin .../Resources/Fonts/Encode_Sans_Condensed/OFL.txt | 0 .../Fonts/Encode_Sans_Condensed/download.txt | 0 .../EncodeSansExpanded-Black.ttf | Bin .../EncodeSansExpanded-Bold.ttf | Bin .../EncodeSansExpanded-ExtraBold.ttf | Bin .../EncodeSansExpanded-ExtraLight.ttf | Bin .../EncodeSansExpanded-Light.ttf | Bin .../EncodeSansExpanded-Medium.ttf | Bin .../EncodeSansExpanded-Regular.ttf | Bin .../EncodeSansExpanded-SemiBold.ttf | Bin .../EncodeSansExpanded-Thin.ttf | Bin .../Resources/Fonts/Encode_Sans_Expanded/OFL.txt | 0 .../Fonts/Encode_Sans_Expanded/download.txt | 0 .../FontAwesome/Font Awesome 5 Free-Solid-900.otf | Bin .../FontAwesome/Font Awesome 6 Free-Solid-900.otf | Bin .../FontAwesome/Font Awesome 7 Free-Solid-900.otf | Bin .../Gui/Resources/Fonts/FontAwesome/LICENSE.txt | 0 .../Gui/Resources/Fonts/Nunito/Nunito-Black.ttf | Bin .../Resources/Fonts/Nunito/Nunito-BlackItalic.ttf | Bin .../Gui/Resources/Fonts/Nunito/Nunito-Bold.ttf | Bin .../Resources/Fonts/Nunito/Nunito-BoldItalic.ttf | Bin .../Gui/Resources/Fonts/Nunito/Nunito-ExtraBold.ttf | Bin .../Fonts/Nunito/Nunito-ExtraBoldItalic.ttf | Bin .../Resources/Fonts/Nunito/Nunito-ExtraLight.ttf | Bin .../Fonts/Nunito/Nunito-ExtraLightItalic.ttf | Bin .../Gui/Resources/Fonts/Nunito/Nunito-Italic.ttf | Bin .../Gui/Resources/Fonts/Nunito/Nunito-Light.ttf | Bin .../Resources/Fonts/Nunito/Nunito-LightItalic.ttf | Bin .../Gui/Resources/Fonts/Nunito/Nunito-Regular.ttf | Bin .../Gui/Resources/Fonts/Nunito/Nunito-SemiBold.ttf | Bin .../Fonts/Nunito/Nunito-SemiBoldItalic.ttf | Bin .../Gui/Resources/Fonts/Nunito/OFL.txt | 0 .../Gui/Resources/Fonts/Nunito/download.txt | 0 .../Gui/Resources/Fonts/PT_Mono/OFL.txt | 0 .../Gui/Resources/Fonts/PT_Mono/PTMono-Regular.ttf | Bin .../Gui/Resources/Fonts/PT_Mono/download.txt | 0 .../Gui/Resources/Fonts/PT_Sans/OFL.txt | 0 .../Gui/Resources/Fonts/PT_Sans/PTSans-Bold.ttf | Bin .../Resources/Fonts/PT_Sans/PTSans-BoldItalic.ttf | Bin .../Gui/Resources/Fonts/PT_Sans/PTSans-Italic.ttf | Bin .../Gui/Resources/Fonts/PT_Sans/PTSans-Regular.ttf | Bin .../Gui/Resources/Fonts/PT_Sans/download.txt | 0 .../Gui/Resources/Icons/description.txt | 0 .../Gui/Style/Colors.qml | 0 .../Gui/Style/Fonts.qml | 0 .../Gui/Style/Sizes.qml | 0 .../Gui/Style/Times.qml | 0 .../Gui/Style/qmldir | 0 .../Logic/Logging.py | 0 .../Logic/Maintenance.py | 0 .../Logic/Maintenance/Updater.qml | 0 .../Logic/Maintenance/qmldir | 0 .../Logic/Translate.py | 0 .../Logic/Utils/Utils.py | 0 .../Logic/Utils/__init__.py | 0 .../Logic/__init__.py | 0 .../__init__.py | 0 191 files changed, 4 insertions(+), 12 deletions(-) rename src/{easyapplication => EasyApplication}/Gui/Animations/ColorReset.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Animations/ThemeChange.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Animations/TranslationChange.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Animations/qmldir (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/ChartTemplateHeatmap2dPlotly.html (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/ChartTemplateSimple1dPlotly.html (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/ChartViewHeatmap2dPlotly.qml (86%) rename src/{easyapplication => EasyApplication}/Gui/Charts/ChartViewSimple1dPlotly.qml (91%) rename src/{easyapplication => EasyApplication}/Gui/Charts/Plotly1dBarPlot.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/Plotly1dLine.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/Plotly1dMeasVsCalc.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/Plotly2dHeatmap.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/Plotly2dPolarHeatmap.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/Plotly3dScatter.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/Plotly3dSurface.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/QtCharts1dBase 2.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/QtCharts1dBase.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/QtCharts1dMeasVsCalc.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/QtCharts1dValueAxis.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Charts/qmldir (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/AboutDialog.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/AppBarCentralTabs.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/AppBarLeftButtons.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/AppBarRightButtons.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/ApplicationWindow.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/BasicReport.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/ContentArea.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/ContentPage.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/GuideWindow.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/GuideWindowContainer.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/JsonListModel.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/MainContent.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/PreferencesDialog.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/ProjectDescriptionDialog.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/SideBar.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/SideBarColumn.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/TableView.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/TableViewAdvancedLabel.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/TableViewButton.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/TableViewCheckBox.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/TableViewComboBox.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/TableViewDelegate.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/TableViewHeader.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/TableViewLabel.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/TableViewLabelControl.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/TableViewParameter.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/TableViewTextInput.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/TableViewTwoRowsAdvancedLabel.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Components/qmldir (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/AppBarTabButton.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/ApplicationWindow.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/Button.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/CheckBox.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/CheckIndicator.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/ComboBox.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/CursorDelegate.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/Dialog.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/DialogButtonBox.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/GroupBox.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/GroupButton.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/GroupColumn.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/GroupRow.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/Label.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/LinkedImage.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/Menu.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/MenuItem.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/ParamComboBox.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/ParamTextField.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/Parameter.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/RadioButton.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/RadioIndicator.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/RemoteController.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/RemotePointer.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/RunningLabel.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/ScrollBar.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/ScrollIndicator.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/SideBarButton.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/Slider.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/SliderHandle.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/SpinBox.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/SplashScreen.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/StatusBar.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/StatusBarItem.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/TabBar.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/TabButton.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/TextArea.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/TextField.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/TextInput.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/ToolButton.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/ToolTip.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/ToolTipShadow.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Elements/qmldir (100%) rename src/{easyapplication => EasyApplication}/Gui/Globals/Vars.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Globals/qmldir (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/BasicReport.html (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/BlankPage.html (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/PTSans-Bold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/PTSans-Regular.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/Plotly1dBarPlot.html (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/Plotly1dLine.html (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/Plotly1dMeasVsCalc.html (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/Plotly2dHeatmap.html (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/Plotly2dPolarHeatmap.html (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/Plotly3dMesh.html (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/Plotly3dScatter.html (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/Plotly3dSurface.html (100%) rename src/{easyapplication => EasyApplication}/Gui/Html/plotly-2.18.0.min.js (100%) rename src/{easyapplication => EasyApplication}/Gui/Logic/Plotting.js (100%) rename src/{easyapplication => EasyApplication}/Gui/Logic/ProjectConfig.js (100%) rename src/{easyapplication => EasyApplication}/Gui/Logic/Translate.js (100%) rename src/{easyapplication => EasyApplication}/Gui/Logic/Utils.js (100%) rename src/{easyapplication => EasyApplication}/Gui/Logic/qmldir (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Black.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Bold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-ExtraBold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-ExtraLight.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Light.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Medium.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Regular.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-SemiBold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Thin.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans/OFL.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans/download.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Black.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Bold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-ExtraBold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-ExtraLight.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Light.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Medium.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Regular.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-SemiBold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Thin.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/OFL.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Condensed/download.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Black.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Bold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-ExtraBold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-ExtraLight.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Light.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Medium.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Regular.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-SemiBold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Thin.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/OFL.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Encode_Sans_Expanded/download.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/FontAwesome/Font Awesome 5 Free-Solid-900.otf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/FontAwesome/Font Awesome 6 Free-Solid-900.otf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/FontAwesome/Font Awesome 7 Free-Solid-900.otf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/FontAwesome/LICENSE.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-Black.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-BlackItalic.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-Bold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-BoldItalic.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-ExtraBold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-ExtraBoldItalic.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-ExtraLight.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-ExtraLightItalic.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-Italic.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-Light.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-LightItalic.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-Regular.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-SemiBold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/Nunito-SemiBoldItalic.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/OFL.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/Nunito/download.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/PT_Mono/OFL.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/PT_Mono/PTMono-Regular.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/PT_Mono/download.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/PT_Sans/OFL.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/PT_Sans/PTSans-Bold.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/PT_Sans/PTSans-BoldItalic.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/PT_Sans/PTSans-Italic.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/PT_Sans/PTSans-Regular.ttf (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Fonts/PT_Sans/download.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Resources/Icons/description.txt (100%) rename src/{easyapplication => EasyApplication}/Gui/Style/Colors.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Style/Fonts.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Style/Sizes.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Style/Times.qml (100%) rename src/{easyapplication => EasyApplication}/Gui/Style/qmldir (100%) rename src/{easyapplication => EasyApplication}/Logic/Logging.py (100%) rename src/{easyapplication => EasyApplication}/Logic/Maintenance.py (100%) rename src/{easyapplication => EasyApplication}/Logic/Maintenance/Updater.qml (100%) rename src/{easyapplication => EasyApplication}/Logic/Maintenance/qmldir (100%) rename src/{easyapplication => EasyApplication}/Logic/Translate.py (100%) rename src/{easyapplication => EasyApplication}/Logic/Utils/Utils.py (100%) rename src/{easyapplication => EasyApplication}/Logic/Utils/__init__.py (100%) rename src/{easyapplication => EasyApplication}/Logic/__init__.py (100%) rename src/{easyapplication => EasyApplication}/__init__.py (100%) diff --git a/pixi.lock b/pixi.lock index b4c3fb2b..9cb6bbc4 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3148,8 +3148,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easyapplication - version: 0.8.0+dev35 - sha256: d7118f70de032721845ac67bab3d4930b185d0ea95fc74a05cd33e0d28f4d369 + version: 0.8.0+devdirty35 + sha256: 3109fcc04be06e53cf1c182ec11f092a566460ecf1f85e2e4d56d1663c38cc2a requires_dist: - pooch - build ; extra == 'dev' diff --git a/pyproject.toml b/pyproject.toml index 9d9ac531..d12376fe 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ ############################### [project] -name = 'easyapplication' +name = 'EasyApplication' dynamic = ['version'] # Use versioningit to manage the version description = 'Qt/QML components for building graphical applications' authors = [{ name = 'EasyScience contributors' }] @@ -85,7 +85,7 @@ requires = ['hatchling', 'versioningit'] # https://hatch.pypa.io/ [tool.hatch.build.targets.wheel] -packages = ['src/easyapplication'] +packages = ['src/EasyApplication'] [tool.hatch.metadata] allow-direct-references = true diff --git a/src/easyapplication/Gui/Animations/ColorReset.qml b/src/EasyApplication/Gui/Animations/ColorReset.qml similarity index 100% rename from src/easyapplication/Gui/Animations/ColorReset.qml rename to src/EasyApplication/Gui/Animations/ColorReset.qml diff --git a/src/easyapplication/Gui/Animations/ThemeChange.qml b/src/EasyApplication/Gui/Animations/ThemeChange.qml similarity index 100% rename from src/easyapplication/Gui/Animations/ThemeChange.qml rename to src/EasyApplication/Gui/Animations/ThemeChange.qml diff --git a/src/easyapplication/Gui/Animations/TranslationChange.qml b/src/EasyApplication/Gui/Animations/TranslationChange.qml similarity index 100% rename from src/easyapplication/Gui/Animations/TranslationChange.qml rename to src/EasyApplication/Gui/Animations/TranslationChange.qml diff --git a/src/easyapplication/Gui/Animations/qmldir b/src/EasyApplication/Gui/Animations/qmldir similarity index 100% rename from src/easyapplication/Gui/Animations/qmldir rename to src/EasyApplication/Gui/Animations/qmldir diff --git a/src/easyapplication/Gui/Charts/ChartTemplateHeatmap2dPlotly.html b/src/EasyApplication/Gui/Charts/ChartTemplateHeatmap2dPlotly.html similarity index 100% rename from src/easyapplication/Gui/Charts/ChartTemplateHeatmap2dPlotly.html rename to src/EasyApplication/Gui/Charts/ChartTemplateHeatmap2dPlotly.html diff --git a/src/easyapplication/Gui/Charts/ChartTemplateSimple1dPlotly.html b/src/EasyApplication/Gui/Charts/ChartTemplateSimple1dPlotly.html similarity index 100% rename from src/easyapplication/Gui/Charts/ChartTemplateSimple1dPlotly.html rename to src/EasyApplication/Gui/Charts/ChartTemplateSimple1dPlotly.html diff --git a/src/easyapplication/Gui/Charts/ChartViewHeatmap2dPlotly.qml b/src/EasyApplication/Gui/Charts/ChartViewHeatmap2dPlotly.qml similarity index 86% rename from src/easyapplication/Gui/Charts/ChartViewHeatmap2dPlotly.qml rename to src/EasyApplication/Gui/Charts/ChartViewHeatmap2dPlotly.qml index 58104a32..7cd7b88a 100644 --- a/src/easyapplication/Gui/Charts/ChartViewHeatmap2dPlotly.qml +++ b/src/EasyApplication/Gui/Charts/ChartViewHeatmap2dPlotly.qml @@ -1,7 +1,3 @@ -// SPDX-FileCopyrightText: 2022 EasyTexture contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2022 Contributors to the EasyTexture project - import QtQuick 2.15 import QtQuick.Controls 2.15 import QtWebEngine 1.10 diff --git a/src/easyapplication/Gui/Charts/ChartViewSimple1dPlotly.qml b/src/EasyApplication/Gui/Charts/ChartViewSimple1dPlotly.qml similarity index 91% rename from src/easyapplication/Gui/Charts/ChartViewSimple1dPlotly.qml rename to src/EasyApplication/Gui/Charts/ChartViewSimple1dPlotly.qml index 4ea22fcb..b5026add 100644 --- a/src/easyapplication/Gui/Charts/ChartViewSimple1dPlotly.qml +++ b/src/EasyApplication/Gui/Charts/ChartViewSimple1dPlotly.qml @@ -1,7 +1,3 @@ -// SPDX-FileCopyrightText: 2022 EasyTexture contributors -// SPDX-License-Identifier: BSD-3-Clause -// © 2022 Contributors to the EasyTexture project - import QtQuick 2.15 import QtQuick.Controls 2.15 import QtWebEngine 1.10 diff --git a/src/easyapplication/Gui/Charts/Plotly1dBarPlot.qml b/src/EasyApplication/Gui/Charts/Plotly1dBarPlot.qml similarity index 100% rename from src/easyapplication/Gui/Charts/Plotly1dBarPlot.qml rename to src/EasyApplication/Gui/Charts/Plotly1dBarPlot.qml diff --git a/src/easyapplication/Gui/Charts/Plotly1dLine.qml b/src/EasyApplication/Gui/Charts/Plotly1dLine.qml similarity index 100% rename from src/easyapplication/Gui/Charts/Plotly1dLine.qml rename to src/EasyApplication/Gui/Charts/Plotly1dLine.qml diff --git a/src/easyapplication/Gui/Charts/Plotly1dMeasVsCalc.qml b/src/EasyApplication/Gui/Charts/Plotly1dMeasVsCalc.qml similarity index 100% rename from src/easyapplication/Gui/Charts/Plotly1dMeasVsCalc.qml rename to src/EasyApplication/Gui/Charts/Plotly1dMeasVsCalc.qml diff --git a/src/easyapplication/Gui/Charts/Plotly2dHeatmap.qml b/src/EasyApplication/Gui/Charts/Plotly2dHeatmap.qml similarity index 100% rename from src/easyapplication/Gui/Charts/Plotly2dHeatmap.qml rename to src/EasyApplication/Gui/Charts/Plotly2dHeatmap.qml diff --git a/src/easyapplication/Gui/Charts/Plotly2dPolarHeatmap.qml b/src/EasyApplication/Gui/Charts/Plotly2dPolarHeatmap.qml similarity index 100% rename from src/easyapplication/Gui/Charts/Plotly2dPolarHeatmap.qml rename to src/EasyApplication/Gui/Charts/Plotly2dPolarHeatmap.qml diff --git a/src/easyapplication/Gui/Charts/Plotly3dScatter.qml b/src/EasyApplication/Gui/Charts/Plotly3dScatter.qml similarity index 100% rename from src/easyapplication/Gui/Charts/Plotly3dScatter.qml rename to src/EasyApplication/Gui/Charts/Plotly3dScatter.qml diff --git a/src/easyapplication/Gui/Charts/Plotly3dSurface.qml b/src/EasyApplication/Gui/Charts/Plotly3dSurface.qml similarity index 100% rename from src/easyapplication/Gui/Charts/Plotly3dSurface.qml rename to src/EasyApplication/Gui/Charts/Plotly3dSurface.qml diff --git a/src/easyapplication/Gui/Charts/QtCharts1dBase 2.qml b/src/EasyApplication/Gui/Charts/QtCharts1dBase 2.qml similarity index 100% rename from src/easyapplication/Gui/Charts/QtCharts1dBase 2.qml rename to src/EasyApplication/Gui/Charts/QtCharts1dBase 2.qml diff --git a/src/easyapplication/Gui/Charts/QtCharts1dBase.qml b/src/EasyApplication/Gui/Charts/QtCharts1dBase.qml similarity index 100% rename from src/easyapplication/Gui/Charts/QtCharts1dBase.qml rename to src/EasyApplication/Gui/Charts/QtCharts1dBase.qml diff --git a/src/easyapplication/Gui/Charts/QtCharts1dMeasVsCalc.qml b/src/EasyApplication/Gui/Charts/QtCharts1dMeasVsCalc.qml similarity index 100% rename from src/easyapplication/Gui/Charts/QtCharts1dMeasVsCalc.qml rename to src/EasyApplication/Gui/Charts/QtCharts1dMeasVsCalc.qml diff --git a/src/easyapplication/Gui/Charts/QtCharts1dValueAxis.qml b/src/EasyApplication/Gui/Charts/QtCharts1dValueAxis.qml similarity index 100% rename from src/easyapplication/Gui/Charts/QtCharts1dValueAxis.qml rename to src/EasyApplication/Gui/Charts/QtCharts1dValueAxis.qml diff --git a/src/easyapplication/Gui/Charts/qmldir b/src/EasyApplication/Gui/Charts/qmldir similarity index 100% rename from src/easyapplication/Gui/Charts/qmldir rename to src/EasyApplication/Gui/Charts/qmldir diff --git a/src/easyapplication/Gui/Components/AboutDialog.qml b/src/EasyApplication/Gui/Components/AboutDialog.qml similarity index 100% rename from src/easyapplication/Gui/Components/AboutDialog.qml rename to src/EasyApplication/Gui/Components/AboutDialog.qml diff --git a/src/easyapplication/Gui/Components/AppBarCentralTabs.qml b/src/EasyApplication/Gui/Components/AppBarCentralTabs.qml similarity index 100% rename from src/easyapplication/Gui/Components/AppBarCentralTabs.qml rename to src/EasyApplication/Gui/Components/AppBarCentralTabs.qml diff --git a/src/easyapplication/Gui/Components/AppBarLeftButtons.qml b/src/EasyApplication/Gui/Components/AppBarLeftButtons.qml similarity index 100% rename from src/easyapplication/Gui/Components/AppBarLeftButtons.qml rename to src/EasyApplication/Gui/Components/AppBarLeftButtons.qml diff --git a/src/easyapplication/Gui/Components/AppBarRightButtons.qml b/src/EasyApplication/Gui/Components/AppBarRightButtons.qml similarity index 100% rename from src/easyapplication/Gui/Components/AppBarRightButtons.qml rename to src/EasyApplication/Gui/Components/AppBarRightButtons.qml diff --git a/src/easyapplication/Gui/Components/ApplicationWindow.qml b/src/EasyApplication/Gui/Components/ApplicationWindow.qml similarity index 100% rename from src/easyapplication/Gui/Components/ApplicationWindow.qml rename to src/EasyApplication/Gui/Components/ApplicationWindow.qml diff --git a/src/easyapplication/Gui/Components/BasicReport.qml b/src/EasyApplication/Gui/Components/BasicReport.qml similarity index 100% rename from src/easyapplication/Gui/Components/BasicReport.qml rename to src/EasyApplication/Gui/Components/BasicReport.qml diff --git a/src/easyapplication/Gui/Components/ContentArea.qml b/src/EasyApplication/Gui/Components/ContentArea.qml similarity index 100% rename from src/easyapplication/Gui/Components/ContentArea.qml rename to src/EasyApplication/Gui/Components/ContentArea.qml diff --git a/src/easyapplication/Gui/Components/ContentPage.qml b/src/EasyApplication/Gui/Components/ContentPage.qml similarity index 100% rename from src/easyapplication/Gui/Components/ContentPage.qml rename to src/EasyApplication/Gui/Components/ContentPage.qml diff --git a/src/easyapplication/Gui/Components/GuideWindow.qml b/src/EasyApplication/Gui/Components/GuideWindow.qml similarity index 100% rename from src/easyapplication/Gui/Components/GuideWindow.qml rename to src/EasyApplication/Gui/Components/GuideWindow.qml diff --git a/src/easyapplication/Gui/Components/GuideWindowContainer.qml b/src/EasyApplication/Gui/Components/GuideWindowContainer.qml similarity index 100% rename from src/easyapplication/Gui/Components/GuideWindowContainer.qml rename to src/EasyApplication/Gui/Components/GuideWindowContainer.qml diff --git a/src/easyapplication/Gui/Components/JsonListModel.qml b/src/EasyApplication/Gui/Components/JsonListModel.qml similarity index 100% rename from src/easyapplication/Gui/Components/JsonListModel.qml rename to src/EasyApplication/Gui/Components/JsonListModel.qml diff --git a/src/easyapplication/Gui/Components/MainContent.qml b/src/EasyApplication/Gui/Components/MainContent.qml similarity index 100% rename from src/easyapplication/Gui/Components/MainContent.qml rename to src/EasyApplication/Gui/Components/MainContent.qml diff --git a/src/easyapplication/Gui/Components/PreferencesDialog.qml b/src/EasyApplication/Gui/Components/PreferencesDialog.qml similarity index 100% rename from src/easyapplication/Gui/Components/PreferencesDialog.qml rename to src/EasyApplication/Gui/Components/PreferencesDialog.qml diff --git a/src/easyapplication/Gui/Components/ProjectDescriptionDialog.qml b/src/EasyApplication/Gui/Components/ProjectDescriptionDialog.qml similarity index 100% rename from src/easyapplication/Gui/Components/ProjectDescriptionDialog.qml rename to src/EasyApplication/Gui/Components/ProjectDescriptionDialog.qml diff --git a/src/easyapplication/Gui/Components/SideBar.qml b/src/EasyApplication/Gui/Components/SideBar.qml similarity index 100% rename from src/easyapplication/Gui/Components/SideBar.qml rename to src/EasyApplication/Gui/Components/SideBar.qml diff --git a/src/easyapplication/Gui/Components/SideBarColumn.qml b/src/EasyApplication/Gui/Components/SideBarColumn.qml similarity index 100% rename from src/easyapplication/Gui/Components/SideBarColumn.qml rename to src/EasyApplication/Gui/Components/SideBarColumn.qml diff --git a/src/easyapplication/Gui/Components/TableView.qml b/src/EasyApplication/Gui/Components/TableView.qml similarity index 100% rename from src/easyapplication/Gui/Components/TableView.qml rename to src/EasyApplication/Gui/Components/TableView.qml diff --git a/src/easyapplication/Gui/Components/TableViewAdvancedLabel.qml b/src/EasyApplication/Gui/Components/TableViewAdvancedLabel.qml similarity index 100% rename from src/easyapplication/Gui/Components/TableViewAdvancedLabel.qml rename to src/EasyApplication/Gui/Components/TableViewAdvancedLabel.qml diff --git a/src/easyapplication/Gui/Components/TableViewButton.qml b/src/EasyApplication/Gui/Components/TableViewButton.qml similarity index 100% rename from src/easyapplication/Gui/Components/TableViewButton.qml rename to src/EasyApplication/Gui/Components/TableViewButton.qml diff --git a/src/easyapplication/Gui/Components/TableViewCheckBox.qml b/src/EasyApplication/Gui/Components/TableViewCheckBox.qml similarity index 100% rename from src/easyapplication/Gui/Components/TableViewCheckBox.qml rename to src/EasyApplication/Gui/Components/TableViewCheckBox.qml diff --git a/src/easyapplication/Gui/Components/TableViewComboBox.qml b/src/EasyApplication/Gui/Components/TableViewComboBox.qml similarity index 100% rename from src/easyapplication/Gui/Components/TableViewComboBox.qml rename to src/EasyApplication/Gui/Components/TableViewComboBox.qml diff --git a/src/easyapplication/Gui/Components/TableViewDelegate.qml b/src/EasyApplication/Gui/Components/TableViewDelegate.qml similarity index 100% rename from src/easyapplication/Gui/Components/TableViewDelegate.qml rename to src/EasyApplication/Gui/Components/TableViewDelegate.qml diff --git a/src/easyapplication/Gui/Components/TableViewHeader.qml b/src/EasyApplication/Gui/Components/TableViewHeader.qml similarity index 100% rename from src/easyapplication/Gui/Components/TableViewHeader.qml rename to src/EasyApplication/Gui/Components/TableViewHeader.qml diff --git a/src/easyapplication/Gui/Components/TableViewLabel.qml b/src/EasyApplication/Gui/Components/TableViewLabel.qml similarity index 100% rename from src/easyapplication/Gui/Components/TableViewLabel.qml rename to src/EasyApplication/Gui/Components/TableViewLabel.qml diff --git a/src/easyapplication/Gui/Components/TableViewLabelControl.qml b/src/EasyApplication/Gui/Components/TableViewLabelControl.qml similarity index 100% rename from src/easyapplication/Gui/Components/TableViewLabelControl.qml rename to src/EasyApplication/Gui/Components/TableViewLabelControl.qml diff --git a/src/easyapplication/Gui/Components/TableViewParameter.qml b/src/EasyApplication/Gui/Components/TableViewParameter.qml similarity index 100% rename from src/easyapplication/Gui/Components/TableViewParameter.qml rename to src/EasyApplication/Gui/Components/TableViewParameter.qml diff --git a/src/easyapplication/Gui/Components/TableViewTextInput.qml b/src/EasyApplication/Gui/Components/TableViewTextInput.qml similarity index 100% rename from src/easyapplication/Gui/Components/TableViewTextInput.qml rename to src/EasyApplication/Gui/Components/TableViewTextInput.qml diff --git a/src/easyapplication/Gui/Components/TableViewTwoRowsAdvancedLabel.qml b/src/EasyApplication/Gui/Components/TableViewTwoRowsAdvancedLabel.qml similarity index 100% rename from src/easyapplication/Gui/Components/TableViewTwoRowsAdvancedLabel.qml rename to src/EasyApplication/Gui/Components/TableViewTwoRowsAdvancedLabel.qml diff --git a/src/easyapplication/Gui/Components/qmldir b/src/EasyApplication/Gui/Components/qmldir similarity index 100% rename from src/easyapplication/Gui/Components/qmldir rename to src/EasyApplication/Gui/Components/qmldir diff --git a/src/easyapplication/Gui/Elements/AppBarTabButton.qml b/src/EasyApplication/Gui/Elements/AppBarTabButton.qml similarity index 100% rename from src/easyapplication/Gui/Elements/AppBarTabButton.qml rename to src/EasyApplication/Gui/Elements/AppBarTabButton.qml diff --git a/src/easyapplication/Gui/Elements/ApplicationWindow.qml b/src/EasyApplication/Gui/Elements/ApplicationWindow.qml similarity index 100% rename from src/easyapplication/Gui/Elements/ApplicationWindow.qml rename to src/EasyApplication/Gui/Elements/ApplicationWindow.qml diff --git a/src/easyapplication/Gui/Elements/Button.qml b/src/EasyApplication/Gui/Elements/Button.qml similarity index 100% rename from src/easyapplication/Gui/Elements/Button.qml rename to src/EasyApplication/Gui/Elements/Button.qml diff --git a/src/easyapplication/Gui/Elements/CheckBox.qml b/src/EasyApplication/Gui/Elements/CheckBox.qml similarity index 100% rename from src/easyapplication/Gui/Elements/CheckBox.qml rename to src/EasyApplication/Gui/Elements/CheckBox.qml diff --git a/src/easyapplication/Gui/Elements/CheckIndicator.qml b/src/EasyApplication/Gui/Elements/CheckIndicator.qml similarity index 100% rename from src/easyapplication/Gui/Elements/CheckIndicator.qml rename to src/EasyApplication/Gui/Elements/CheckIndicator.qml diff --git a/src/easyapplication/Gui/Elements/ComboBox.qml b/src/EasyApplication/Gui/Elements/ComboBox.qml similarity index 100% rename from src/easyapplication/Gui/Elements/ComboBox.qml rename to src/EasyApplication/Gui/Elements/ComboBox.qml diff --git a/src/easyapplication/Gui/Elements/CursorDelegate.qml b/src/EasyApplication/Gui/Elements/CursorDelegate.qml similarity index 100% rename from src/easyapplication/Gui/Elements/CursorDelegate.qml rename to src/EasyApplication/Gui/Elements/CursorDelegate.qml diff --git a/src/easyapplication/Gui/Elements/Dialog.qml b/src/EasyApplication/Gui/Elements/Dialog.qml similarity index 100% rename from src/easyapplication/Gui/Elements/Dialog.qml rename to src/EasyApplication/Gui/Elements/Dialog.qml diff --git a/src/easyapplication/Gui/Elements/DialogButtonBox.qml b/src/EasyApplication/Gui/Elements/DialogButtonBox.qml similarity index 100% rename from src/easyapplication/Gui/Elements/DialogButtonBox.qml rename to src/EasyApplication/Gui/Elements/DialogButtonBox.qml diff --git a/src/easyapplication/Gui/Elements/GroupBox.qml b/src/EasyApplication/Gui/Elements/GroupBox.qml similarity index 100% rename from src/easyapplication/Gui/Elements/GroupBox.qml rename to src/EasyApplication/Gui/Elements/GroupBox.qml diff --git a/src/easyapplication/Gui/Elements/GroupButton.qml b/src/EasyApplication/Gui/Elements/GroupButton.qml similarity index 100% rename from src/easyapplication/Gui/Elements/GroupButton.qml rename to src/EasyApplication/Gui/Elements/GroupButton.qml diff --git a/src/easyapplication/Gui/Elements/GroupColumn.qml b/src/EasyApplication/Gui/Elements/GroupColumn.qml similarity index 100% rename from src/easyapplication/Gui/Elements/GroupColumn.qml rename to src/EasyApplication/Gui/Elements/GroupColumn.qml diff --git a/src/easyapplication/Gui/Elements/GroupRow.qml b/src/EasyApplication/Gui/Elements/GroupRow.qml similarity index 100% rename from src/easyapplication/Gui/Elements/GroupRow.qml rename to src/EasyApplication/Gui/Elements/GroupRow.qml diff --git a/src/easyapplication/Gui/Elements/Label.qml b/src/EasyApplication/Gui/Elements/Label.qml similarity index 100% rename from src/easyapplication/Gui/Elements/Label.qml rename to src/EasyApplication/Gui/Elements/Label.qml diff --git a/src/easyapplication/Gui/Elements/LinkedImage.qml b/src/EasyApplication/Gui/Elements/LinkedImage.qml similarity index 100% rename from src/easyapplication/Gui/Elements/LinkedImage.qml rename to src/EasyApplication/Gui/Elements/LinkedImage.qml diff --git a/src/easyapplication/Gui/Elements/Menu.qml b/src/EasyApplication/Gui/Elements/Menu.qml similarity index 100% rename from src/easyapplication/Gui/Elements/Menu.qml rename to src/EasyApplication/Gui/Elements/Menu.qml diff --git a/src/easyapplication/Gui/Elements/MenuItem.qml b/src/EasyApplication/Gui/Elements/MenuItem.qml similarity index 100% rename from src/easyapplication/Gui/Elements/MenuItem.qml rename to src/EasyApplication/Gui/Elements/MenuItem.qml diff --git a/src/easyapplication/Gui/Elements/ParamComboBox.qml b/src/EasyApplication/Gui/Elements/ParamComboBox.qml similarity index 100% rename from src/easyapplication/Gui/Elements/ParamComboBox.qml rename to src/EasyApplication/Gui/Elements/ParamComboBox.qml diff --git a/src/easyapplication/Gui/Elements/ParamTextField.qml b/src/EasyApplication/Gui/Elements/ParamTextField.qml similarity index 100% rename from src/easyapplication/Gui/Elements/ParamTextField.qml rename to src/EasyApplication/Gui/Elements/ParamTextField.qml diff --git a/src/easyapplication/Gui/Elements/Parameter.qml b/src/EasyApplication/Gui/Elements/Parameter.qml similarity index 100% rename from src/easyapplication/Gui/Elements/Parameter.qml rename to src/EasyApplication/Gui/Elements/Parameter.qml diff --git a/src/easyapplication/Gui/Elements/RadioButton.qml b/src/EasyApplication/Gui/Elements/RadioButton.qml similarity index 100% rename from src/easyapplication/Gui/Elements/RadioButton.qml rename to src/EasyApplication/Gui/Elements/RadioButton.qml diff --git a/src/easyapplication/Gui/Elements/RadioIndicator.qml b/src/EasyApplication/Gui/Elements/RadioIndicator.qml similarity index 100% rename from src/easyapplication/Gui/Elements/RadioIndicator.qml rename to src/EasyApplication/Gui/Elements/RadioIndicator.qml diff --git a/src/easyapplication/Gui/Elements/RemoteController.qml b/src/EasyApplication/Gui/Elements/RemoteController.qml similarity index 100% rename from src/easyapplication/Gui/Elements/RemoteController.qml rename to src/EasyApplication/Gui/Elements/RemoteController.qml diff --git a/src/easyapplication/Gui/Elements/RemotePointer.qml b/src/EasyApplication/Gui/Elements/RemotePointer.qml similarity index 100% rename from src/easyapplication/Gui/Elements/RemotePointer.qml rename to src/EasyApplication/Gui/Elements/RemotePointer.qml diff --git a/src/easyapplication/Gui/Elements/RunningLabel.qml b/src/EasyApplication/Gui/Elements/RunningLabel.qml similarity index 100% rename from src/easyapplication/Gui/Elements/RunningLabel.qml rename to src/EasyApplication/Gui/Elements/RunningLabel.qml diff --git a/src/easyapplication/Gui/Elements/ScrollBar.qml b/src/EasyApplication/Gui/Elements/ScrollBar.qml similarity index 100% rename from src/easyapplication/Gui/Elements/ScrollBar.qml rename to src/EasyApplication/Gui/Elements/ScrollBar.qml diff --git a/src/easyapplication/Gui/Elements/ScrollIndicator.qml b/src/EasyApplication/Gui/Elements/ScrollIndicator.qml similarity index 100% rename from src/easyapplication/Gui/Elements/ScrollIndicator.qml rename to src/EasyApplication/Gui/Elements/ScrollIndicator.qml diff --git a/src/easyapplication/Gui/Elements/SideBarButton.qml b/src/EasyApplication/Gui/Elements/SideBarButton.qml similarity index 100% rename from src/easyapplication/Gui/Elements/SideBarButton.qml rename to src/EasyApplication/Gui/Elements/SideBarButton.qml diff --git a/src/easyapplication/Gui/Elements/Slider.qml b/src/EasyApplication/Gui/Elements/Slider.qml similarity index 100% rename from src/easyapplication/Gui/Elements/Slider.qml rename to src/EasyApplication/Gui/Elements/Slider.qml diff --git a/src/easyapplication/Gui/Elements/SliderHandle.qml b/src/EasyApplication/Gui/Elements/SliderHandle.qml similarity index 100% rename from src/easyapplication/Gui/Elements/SliderHandle.qml rename to src/EasyApplication/Gui/Elements/SliderHandle.qml diff --git a/src/easyapplication/Gui/Elements/SpinBox.qml b/src/EasyApplication/Gui/Elements/SpinBox.qml similarity index 100% rename from src/easyapplication/Gui/Elements/SpinBox.qml rename to src/EasyApplication/Gui/Elements/SpinBox.qml diff --git a/src/easyapplication/Gui/Elements/SplashScreen.qml b/src/EasyApplication/Gui/Elements/SplashScreen.qml similarity index 100% rename from src/easyapplication/Gui/Elements/SplashScreen.qml rename to src/EasyApplication/Gui/Elements/SplashScreen.qml diff --git a/src/easyapplication/Gui/Elements/StatusBar.qml b/src/EasyApplication/Gui/Elements/StatusBar.qml similarity index 100% rename from src/easyapplication/Gui/Elements/StatusBar.qml rename to src/EasyApplication/Gui/Elements/StatusBar.qml diff --git a/src/easyapplication/Gui/Elements/StatusBarItem.qml b/src/EasyApplication/Gui/Elements/StatusBarItem.qml similarity index 100% rename from src/easyapplication/Gui/Elements/StatusBarItem.qml rename to src/EasyApplication/Gui/Elements/StatusBarItem.qml diff --git a/src/easyapplication/Gui/Elements/TabBar.qml b/src/EasyApplication/Gui/Elements/TabBar.qml similarity index 100% rename from src/easyapplication/Gui/Elements/TabBar.qml rename to src/EasyApplication/Gui/Elements/TabBar.qml diff --git a/src/easyapplication/Gui/Elements/TabButton.qml b/src/EasyApplication/Gui/Elements/TabButton.qml similarity index 100% rename from src/easyapplication/Gui/Elements/TabButton.qml rename to src/EasyApplication/Gui/Elements/TabButton.qml diff --git a/src/easyapplication/Gui/Elements/TextArea.qml b/src/EasyApplication/Gui/Elements/TextArea.qml similarity index 100% rename from src/easyapplication/Gui/Elements/TextArea.qml rename to src/EasyApplication/Gui/Elements/TextArea.qml diff --git a/src/easyapplication/Gui/Elements/TextField.qml b/src/EasyApplication/Gui/Elements/TextField.qml similarity index 100% rename from src/easyapplication/Gui/Elements/TextField.qml rename to src/EasyApplication/Gui/Elements/TextField.qml diff --git a/src/easyapplication/Gui/Elements/TextInput.qml b/src/EasyApplication/Gui/Elements/TextInput.qml similarity index 100% rename from src/easyapplication/Gui/Elements/TextInput.qml rename to src/EasyApplication/Gui/Elements/TextInput.qml diff --git a/src/easyapplication/Gui/Elements/ToolButton.qml b/src/EasyApplication/Gui/Elements/ToolButton.qml similarity index 100% rename from src/easyapplication/Gui/Elements/ToolButton.qml rename to src/EasyApplication/Gui/Elements/ToolButton.qml diff --git a/src/easyapplication/Gui/Elements/ToolTip.qml b/src/EasyApplication/Gui/Elements/ToolTip.qml similarity index 100% rename from src/easyapplication/Gui/Elements/ToolTip.qml rename to src/EasyApplication/Gui/Elements/ToolTip.qml diff --git a/src/easyapplication/Gui/Elements/ToolTipShadow.qml b/src/EasyApplication/Gui/Elements/ToolTipShadow.qml similarity index 100% rename from src/easyapplication/Gui/Elements/ToolTipShadow.qml rename to src/EasyApplication/Gui/Elements/ToolTipShadow.qml diff --git a/src/easyapplication/Gui/Elements/qmldir b/src/EasyApplication/Gui/Elements/qmldir similarity index 100% rename from src/easyapplication/Gui/Elements/qmldir rename to src/EasyApplication/Gui/Elements/qmldir diff --git a/src/easyapplication/Gui/Globals/Vars.qml b/src/EasyApplication/Gui/Globals/Vars.qml similarity index 100% rename from src/easyapplication/Gui/Globals/Vars.qml rename to src/EasyApplication/Gui/Globals/Vars.qml diff --git a/src/easyapplication/Gui/Globals/qmldir b/src/EasyApplication/Gui/Globals/qmldir similarity index 100% rename from src/easyapplication/Gui/Globals/qmldir rename to src/EasyApplication/Gui/Globals/qmldir diff --git a/src/easyapplication/Gui/Html/BasicReport.html b/src/EasyApplication/Gui/Html/BasicReport.html similarity index 100% rename from src/easyapplication/Gui/Html/BasicReport.html rename to src/EasyApplication/Gui/Html/BasicReport.html diff --git a/src/easyapplication/Gui/Html/BlankPage.html b/src/EasyApplication/Gui/Html/BlankPage.html similarity index 100% rename from src/easyapplication/Gui/Html/BlankPage.html rename to src/EasyApplication/Gui/Html/BlankPage.html diff --git a/src/easyapplication/Gui/Html/PTSans-Bold.ttf b/src/EasyApplication/Gui/Html/PTSans-Bold.ttf similarity index 100% rename from src/easyapplication/Gui/Html/PTSans-Bold.ttf rename to src/EasyApplication/Gui/Html/PTSans-Bold.ttf diff --git a/src/easyapplication/Gui/Html/PTSans-Regular.ttf b/src/EasyApplication/Gui/Html/PTSans-Regular.ttf similarity index 100% rename from src/easyapplication/Gui/Html/PTSans-Regular.ttf rename to src/EasyApplication/Gui/Html/PTSans-Regular.ttf diff --git a/src/easyapplication/Gui/Html/Plotly1dBarPlot.html b/src/EasyApplication/Gui/Html/Plotly1dBarPlot.html similarity index 100% rename from src/easyapplication/Gui/Html/Plotly1dBarPlot.html rename to src/EasyApplication/Gui/Html/Plotly1dBarPlot.html diff --git a/src/easyapplication/Gui/Html/Plotly1dLine.html b/src/EasyApplication/Gui/Html/Plotly1dLine.html similarity index 100% rename from src/easyapplication/Gui/Html/Plotly1dLine.html rename to src/EasyApplication/Gui/Html/Plotly1dLine.html diff --git a/src/easyapplication/Gui/Html/Plotly1dMeasVsCalc.html b/src/EasyApplication/Gui/Html/Plotly1dMeasVsCalc.html similarity index 100% rename from src/easyapplication/Gui/Html/Plotly1dMeasVsCalc.html rename to src/EasyApplication/Gui/Html/Plotly1dMeasVsCalc.html diff --git a/src/easyapplication/Gui/Html/Plotly2dHeatmap.html b/src/EasyApplication/Gui/Html/Plotly2dHeatmap.html similarity index 100% rename from src/easyapplication/Gui/Html/Plotly2dHeatmap.html rename to src/EasyApplication/Gui/Html/Plotly2dHeatmap.html diff --git a/src/easyapplication/Gui/Html/Plotly2dPolarHeatmap.html b/src/EasyApplication/Gui/Html/Plotly2dPolarHeatmap.html similarity index 100% rename from src/easyapplication/Gui/Html/Plotly2dPolarHeatmap.html rename to src/EasyApplication/Gui/Html/Plotly2dPolarHeatmap.html diff --git a/src/easyapplication/Gui/Html/Plotly3dMesh.html b/src/EasyApplication/Gui/Html/Plotly3dMesh.html similarity index 100% rename from src/easyapplication/Gui/Html/Plotly3dMesh.html rename to src/EasyApplication/Gui/Html/Plotly3dMesh.html diff --git a/src/easyapplication/Gui/Html/Plotly3dScatter.html b/src/EasyApplication/Gui/Html/Plotly3dScatter.html similarity index 100% rename from src/easyapplication/Gui/Html/Plotly3dScatter.html rename to src/EasyApplication/Gui/Html/Plotly3dScatter.html diff --git a/src/easyapplication/Gui/Html/Plotly3dSurface.html b/src/EasyApplication/Gui/Html/Plotly3dSurface.html similarity index 100% rename from src/easyapplication/Gui/Html/Plotly3dSurface.html rename to src/EasyApplication/Gui/Html/Plotly3dSurface.html diff --git a/src/easyapplication/Gui/Html/plotly-2.18.0.min.js b/src/EasyApplication/Gui/Html/plotly-2.18.0.min.js similarity index 100% rename from src/easyapplication/Gui/Html/plotly-2.18.0.min.js rename to src/EasyApplication/Gui/Html/plotly-2.18.0.min.js diff --git a/src/easyapplication/Gui/Logic/Plotting.js b/src/EasyApplication/Gui/Logic/Plotting.js similarity index 100% rename from src/easyapplication/Gui/Logic/Plotting.js rename to src/EasyApplication/Gui/Logic/Plotting.js diff --git a/src/easyapplication/Gui/Logic/ProjectConfig.js b/src/EasyApplication/Gui/Logic/ProjectConfig.js similarity index 100% rename from src/easyapplication/Gui/Logic/ProjectConfig.js rename to src/EasyApplication/Gui/Logic/ProjectConfig.js diff --git a/src/easyapplication/Gui/Logic/Translate.js b/src/EasyApplication/Gui/Logic/Translate.js similarity index 100% rename from src/easyapplication/Gui/Logic/Translate.js rename to src/EasyApplication/Gui/Logic/Translate.js diff --git a/src/easyapplication/Gui/Logic/Utils.js b/src/EasyApplication/Gui/Logic/Utils.js similarity index 100% rename from src/easyapplication/Gui/Logic/Utils.js rename to src/EasyApplication/Gui/Logic/Utils.js diff --git a/src/easyapplication/Gui/Logic/qmldir b/src/EasyApplication/Gui/Logic/qmldir similarity index 100% rename from src/easyapplication/Gui/Logic/qmldir rename to src/EasyApplication/Gui/Logic/qmldir diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Black.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Black.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Black.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Black.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Bold.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Bold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Bold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Bold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-ExtraBold.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-ExtraBold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-ExtraBold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-ExtraBold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-ExtraLight.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-ExtraLight.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-ExtraLight.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-ExtraLight.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Light.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Light.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Light.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Light.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Medium.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Medium.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Medium.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Medium.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Regular.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Regular.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Regular.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Regular.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-SemiBold.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-SemiBold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-SemiBold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-SemiBold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Thin.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Thin.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Thin.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/EncodeSans-Thin.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans/OFL.txt b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/OFL.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans/OFL.txt rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/OFL.txt diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans/download.txt b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/download.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans/download.txt rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans/download.txt diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Black.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Black.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Black.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Black.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Bold.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Bold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Bold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Bold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-ExtraBold.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-ExtraBold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-ExtraBold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-ExtraBold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-ExtraLight.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-ExtraLight.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-ExtraLight.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-ExtraLight.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Light.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Light.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Light.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Light.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Medium.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Medium.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Medium.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Medium.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Regular.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Regular.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Regular.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Regular.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-SemiBold.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-SemiBold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-SemiBold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-SemiBold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Thin.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Thin.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Thin.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/EncodeSansCondensed-Thin.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/OFL.txt b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/OFL.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/OFL.txt rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/OFL.txt diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/download.txt b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/download.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Condensed/download.txt rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Condensed/download.txt diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Black.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Black.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Black.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Black.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Bold.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Bold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Bold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Bold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-ExtraBold.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-ExtraBold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-ExtraBold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-ExtraBold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-ExtraLight.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-ExtraLight.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-ExtraLight.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-ExtraLight.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Light.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Light.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Light.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Light.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Medium.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Medium.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Medium.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Medium.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Regular.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Regular.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Regular.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Regular.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-SemiBold.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-SemiBold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-SemiBold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-SemiBold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Thin.ttf b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Thin.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Thin.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/EncodeSansExpanded-Thin.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/OFL.txt b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/OFL.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/OFL.txt rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/OFL.txt diff --git a/src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/download.txt b/src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/download.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Encode_Sans_Expanded/download.txt rename to src/EasyApplication/Gui/Resources/Fonts/Encode_Sans_Expanded/download.txt diff --git a/src/easyapplication/Gui/Resources/Fonts/FontAwesome/Font Awesome 5 Free-Solid-900.otf b/src/EasyApplication/Gui/Resources/Fonts/FontAwesome/Font Awesome 5 Free-Solid-900.otf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/FontAwesome/Font Awesome 5 Free-Solid-900.otf rename to src/EasyApplication/Gui/Resources/Fonts/FontAwesome/Font Awesome 5 Free-Solid-900.otf diff --git a/src/easyapplication/Gui/Resources/Fonts/FontAwesome/Font Awesome 6 Free-Solid-900.otf b/src/EasyApplication/Gui/Resources/Fonts/FontAwesome/Font Awesome 6 Free-Solid-900.otf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/FontAwesome/Font Awesome 6 Free-Solid-900.otf rename to src/EasyApplication/Gui/Resources/Fonts/FontAwesome/Font Awesome 6 Free-Solid-900.otf diff --git a/src/easyapplication/Gui/Resources/Fonts/FontAwesome/Font Awesome 7 Free-Solid-900.otf b/src/EasyApplication/Gui/Resources/Fonts/FontAwesome/Font Awesome 7 Free-Solid-900.otf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/FontAwesome/Font Awesome 7 Free-Solid-900.otf rename to src/EasyApplication/Gui/Resources/Fonts/FontAwesome/Font Awesome 7 Free-Solid-900.otf diff --git a/src/easyapplication/Gui/Resources/Fonts/FontAwesome/LICENSE.txt b/src/EasyApplication/Gui/Resources/Fonts/FontAwesome/LICENSE.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/FontAwesome/LICENSE.txt rename to src/EasyApplication/Gui/Resources/Fonts/FontAwesome/LICENSE.txt diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-Black.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-Black.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-Black.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-Black.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-BlackItalic.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-BlackItalic.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-BlackItalic.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-BlackItalic.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-Bold.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-Bold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-Bold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-Bold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-BoldItalic.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-BoldItalic.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-BoldItalic.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-BoldItalic.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraBold.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraBold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraBold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraBold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraBoldItalic.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraBoldItalic.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraBoldItalic.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraBoldItalic.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraLight.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraLight.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraLight.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraLight.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraLightItalic.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraLightItalic.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraLightItalic.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-ExtraLightItalic.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-Italic.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-Italic.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-Italic.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-Italic.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-Light.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-Light.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-Light.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-Light.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-LightItalic.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-LightItalic.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-LightItalic.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-LightItalic.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-Regular.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-Regular.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-Regular.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-Regular.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-SemiBold.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-SemiBold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-SemiBold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-SemiBold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-SemiBoldItalic.ttf b/src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-SemiBoldItalic.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/Nunito-SemiBoldItalic.ttf rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/Nunito-SemiBoldItalic.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/OFL.txt b/src/EasyApplication/Gui/Resources/Fonts/Nunito/OFL.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/OFL.txt rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/OFL.txt diff --git a/src/easyapplication/Gui/Resources/Fonts/Nunito/download.txt b/src/EasyApplication/Gui/Resources/Fonts/Nunito/download.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/Nunito/download.txt rename to src/EasyApplication/Gui/Resources/Fonts/Nunito/download.txt diff --git a/src/easyapplication/Gui/Resources/Fonts/PT_Mono/OFL.txt b/src/EasyApplication/Gui/Resources/Fonts/PT_Mono/OFL.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/PT_Mono/OFL.txt rename to src/EasyApplication/Gui/Resources/Fonts/PT_Mono/OFL.txt diff --git a/src/easyapplication/Gui/Resources/Fonts/PT_Mono/PTMono-Regular.ttf b/src/EasyApplication/Gui/Resources/Fonts/PT_Mono/PTMono-Regular.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/PT_Mono/PTMono-Regular.ttf rename to src/EasyApplication/Gui/Resources/Fonts/PT_Mono/PTMono-Regular.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/PT_Mono/download.txt b/src/EasyApplication/Gui/Resources/Fonts/PT_Mono/download.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/PT_Mono/download.txt rename to src/EasyApplication/Gui/Resources/Fonts/PT_Mono/download.txt diff --git a/src/easyapplication/Gui/Resources/Fonts/PT_Sans/OFL.txt b/src/EasyApplication/Gui/Resources/Fonts/PT_Sans/OFL.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/PT_Sans/OFL.txt rename to src/EasyApplication/Gui/Resources/Fonts/PT_Sans/OFL.txt diff --git a/src/easyapplication/Gui/Resources/Fonts/PT_Sans/PTSans-Bold.ttf b/src/EasyApplication/Gui/Resources/Fonts/PT_Sans/PTSans-Bold.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/PT_Sans/PTSans-Bold.ttf rename to src/EasyApplication/Gui/Resources/Fonts/PT_Sans/PTSans-Bold.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/PT_Sans/PTSans-BoldItalic.ttf b/src/EasyApplication/Gui/Resources/Fonts/PT_Sans/PTSans-BoldItalic.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/PT_Sans/PTSans-BoldItalic.ttf rename to src/EasyApplication/Gui/Resources/Fonts/PT_Sans/PTSans-BoldItalic.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/PT_Sans/PTSans-Italic.ttf b/src/EasyApplication/Gui/Resources/Fonts/PT_Sans/PTSans-Italic.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/PT_Sans/PTSans-Italic.ttf rename to src/EasyApplication/Gui/Resources/Fonts/PT_Sans/PTSans-Italic.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/PT_Sans/PTSans-Regular.ttf b/src/EasyApplication/Gui/Resources/Fonts/PT_Sans/PTSans-Regular.ttf similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/PT_Sans/PTSans-Regular.ttf rename to src/EasyApplication/Gui/Resources/Fonts/PT_Sans/PTSans-Regular.ttf diff --git a/src/easyapplication/Gui/Resources/Fonts/PT_Sans/download.txt b/src/EasyApplication/Gui/Resources/Fonts/PT_Sans/download.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Fonts/PT_Sans/download.txt rename to src/EasyApplication/Gui/Resources/Fonts/PT_Sans/download.txt diff --git a/src/easyapplication/Gui/Resources/Icons/description.txt b/src/EasyApplication/Gui/Resources/Icons/description.txt similarity index 100% rename from src/easyapplication/Gui/Resources/Icons/description.txt rename to src/EasyApplication/Gui/Resources/Icons/description.txt diff --git a/src/easyapplication/Gui/Style/Colors.qml b/src/EasyApplication/Gui/Style/Colors.qml similarity index 100% rename from src/easyapplication/Gui/Style/Colors.qml rename to src/EasyApplication/Gui/Style/Colors.qml diff --git a/src/easyapplication/Gui/Style/Fonts.qml b/src/EasyApplication/Gui/Style/Fonts.qml similarity index 100% rename from src/easyapplication/Gui/Style/Fonts.qml rename to src/EasyApplication/Gui/Style/Fonts.qml diff --git a/src/easyapplication/Gui/Style/Sizes.qml b/src/EasyApplication/Gui/Style/Sizes.qml similarity index 100% rename from src/easyapplication/Gui/Style/Sizes.qml rename to src/EasyApplication/Gui/Style/Sizes.qml diff --git a/src/easyapplication/Gui/Style/Times.qml b/src/EasyApplication/Gui/Style/Times.qml similarity index 100% rename from src/easyapplication/Gui/Style/Times.qml rename to src/EasyApplication/Gui/Style/Times.qml diff --git a/src/easyapplication/Gui/Style/qmldir b/src/EasyApplication/Gui/Style/qmldir similarity index 100% rename from src/easyapplication/Gui/Style/qmldir rename to src/EasyApplication/Gui/Style/qmldir diff --git a/src/easyapplication/Logic/Logging.py b/src/EasyApplication/Logic/Logging.py similarity index 100% rename from src/easyapplication/Logic/Logging.py rename to src/EasyApplication/Logic/Logging.py diff --git a/src/easyapplication/Logic/Maintenance.py b/src/EasyApplication/Logic/Maintenance.py similarity index 100% rename from src/easyapplication/Logic/Maintenance.py rename to src/EasyApplication/Logic/Maintenance.py diff --git a/src/easyapplication/Logic/Maintenance/Updater.qml b/src/EasyApplication/Logic/Maintenance/Updater.qml similarity index 100% rename from src/easyapplication/Logic/Maintenance/Updater.qml rename to src/EasyApplication/Logic/Maintenance/Updater.qml diff --git a/src/easyapplication/Logic/Maintenance/qmldir b/src/EasyApplication/Logic/Maintenance/qmldir similarity index 100% rename from src/easyapplication/Logic/Maintenance/qmldir rename to src/EasyApplication/Logic/Maintenance/qmldir diff --git a/src/easyapplication/Logic/Translate.py b/src/EasyApplication/Logic/Translate.py similarity index 100% rename from src/easyapplication/Logic/Translate.py rename to src/EasyApplication/Logic/Translate.py diff --git a/src/easyapplication/Logic/Utils/Utils.py b/src/EasyApplication/Logic/Utils/Utils.py similarity index 100% rename from src/easyapplication/Logic/Utils/Utils.py rename to src/EasyApplication/Logic/Utils/Utils.py diff --git a/src/easyapplication/Logic/Utils/__init__.py b/src/EasyApplication/Logic/Utils/__init__.py similarity index 100% rename from src/easyapplication/Logic/Utils/__init__.py rename to src/EasyApplication/Logic/Utils/__init__.py diff --git a/src/easyapplication/Logic/__init__.py b/src/EasyApplication/Logic/__init__.py similarity index 100% rename from src/easyapplication/Logic/__init__.py rename to src/EasyApplication/Logic/__init__.py diff --git a/src/easyapplication/__init__.py b/src/EasyApplication/__init__.py similarity index 100% rename from src/easyapplication/__init__.py rename to src/EasyApplication/__init__.py From 70d86db9e8152ddc007031e8cfb4f0bfae38c54b Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 29 Apr 2026 16:25:45 +0200 Subject: [PATCH 5/7] Rename EasyApp to EasyApplication in the code --- src/EasyApplication/Gui/Animations/ColorReset.qml | 2 +- src/EasyApplication/Gui/Animations/ThemeChange.qml | 2 +- .../Gui/Animations/TranslationChange.qml | 2 +- .../Gui/Charts/ChartViewHeatmap2dPlotly.qml | 2 +- .../Gui/Charts/ChartViewSimple1dPlotly.qml | 2 +- src/EasyApplication/Gui/Charts/Plotly1dBarPlot.qml | 2 +- src/EasyApplication/Gui/Charts/Plotly1dMeasVsCalc.qml | 2 +- src/EasyApplication/Gui/Charts/QtCharts1dBase 2.qml | 8 ++++---- src/EasyApplication/Gui/Charts/QtCharts1dBase.qml | 6 +++--- .../Gui/Charts/QtCharts1dMeasVsCalc.qml | 4 ++-- src/EasyApplication/Gui/Charts/QtCharts1dValueAxis.qml | 6 +++--- src/EasyApplication/Gui/Components/AboutDialog.qml | 6 +++--- .../Gui/Components/AppBarCentralTabs.qml | 4 ++-- .../Gui/Components/ApplicationWindow.qml | 10 +++++----- src/EasyApplication/Gui/Components/BasicReport.qml | 2 +- src/EasyApplication/Gui/Components/ContentPage.qml | 6 +++--- src/EasyApplication/Gui/Components/GuideWindow.qml | 6 +++--- src/EasyApplication/Gui/Components/MainContent.qml | 2 +- .../Gui/Components/PreferencesDialog.qml | 8 ++++---- .../Gui/Components/ProjectDescriptionDialog.qml | 8 ++++---- src/EasyApplication/Gui/Components/SideBar.qml | 4 ++-- src/EasyApplication/Gui/Components/SideBarColumn.qml | 4 ++-- src/EasyApplication/Gui/Components/TableView.qml | 10 +++++----- .../Gui/Components/TableViewAdvancedLabel.qml | 8 ++++---- src/EasyApplication/Gui/Components/TableViewButton.qml | 4 ++-- .../Gui/Components/TableViewCheckBox.qml | 2 +- .../Gui/Components/TableViewComboBox.qml | 2 +- .../Gui/Components/TableViewDelegate.qml | 4 ++-- src/EasyApplication/Gui/Components/TableViewHeader.qml | 4 ++-- src/EasyApplication/Gui/Components/TableViewLabel.qml | 4 ++-- .../Gui/Components/TableViewLabelControl.qml | 8 ++++---- .../Gui/Components/TableViewParameter.qml | 6 +++--- .../Gui/Components/TableViewTextInput.qml | 2 +- .../Gui/Components/TableViewTwoRowsAdvancedLabel.qml | 8 ++++---- src/EasyApplication/Gui/Elements/AppBarTabButton.qml | 6 +++--- src/EasyApplication/Gui/Elements/ApplicationWindow.qml | 10 +++++----- src/EasyApplication/Gui/Elements/Button.qml | 4 ++-- src/EasyApplication/Gui/Elements/CheckBox.qml | 8 ++++---- src/EasyApplication/Gui/Elements/CheckIndicator.qml | 8 ++++---- src/EasyApplication/Gui/Elements/ComboBox.qml | 8 ++++---- src/EasyApplication/Gui/Elements/CursorDelegate.qml | 2 +- src/EasyApplication/Gui/Elements/Dialog.qml | 8 ++++---- src/EasyApplication/Gui/Elements/DialogButtonBox.qml | 6 +++--- src/EasyApplication/Gui/Elements/GroupBox.qml | 8 ++++---- src/EasyApplication/Gui/Elements/GroupButton.qml | 2 +- src/EasyApplication/Gui/Elements/GroupColumn.qml | 2 +- src/EasyApplication/Gui/Elements/GroupRow.qml | 2 +- src/EasyApplication/Gui/Elements/Label.qml | 4 ++-- src/EasyApplication/Gui/Elements/LinkedImage.qml | 2 +- src/EasyApplication/Gui/Elements/Menu.qml | 2 +- src/EasyApplication/Gui/Elements/MenuItem.qml | 8 ++++---- src/EasyApplication/Gui/Elements/ParamComboBox.qml | 6 +++--- src/EasyApplication/Gui/Elements/ParamTextField.qml | 6 +++--- src/EasyApplication/Gui/Elements/Parameter.qml | 6 +++--- src/EasyApplication/Gui/Elements/RadioButton.qml | 8 ++++---- src/EasyApplication/Gui/Elements/RadioIndicator.qml | 2 +- src/EasyApplication/Gui/Elements/RemoteController.qml | 6 +++--- src/EasyApplication/Gui/Elements/RunningLabel.qml | 4 ++-- src/EasyApplication/Gui/Elements/ScrollBar.qml | 2 +- src/EasyApplication/Gui/Elements/ScrollIndicator.qml | 2 +- src/EasyApplication/Gui/Elements/SideBarButton.qml | 8 ++++---- src/EasyApplication/Gui/Elements/Slider.qml | 8 ++++---- src/EasyApplication/Gui/Elements/SliderHandle.qml | 4 ++-- src/EasyApplication/Gui/Elements/SpinBox.qml | 6 +++--- src/EasyApplication/Gui/Elements/SplashScreen.qml | 4 ++-- src/EasyApplication/Gui/Elements/StatusBar.qml | 6 +++--- src/EasyApplication/Gui/Elements/StatusBarItem.qml | 8 ++++---- src/EasyApplication/Gui/Elements/TabBar.qml | 4 ++-- src/EasyApplication/Gui/Elements/TabButton.qml | 8 ++++---- src/EasyApplication/Gui/Elements/TextArea.qml | 6 +++--- src/EasyApplication/Gui/Elements/TextField.qml | 6 +++--- src/EasyApplication/Gui/Elements/TextInput.qml | 6 +++--- src/EasyApplication/Gui/Elements/ToolButton.qml | 8 ++++---- src/EasyApplication/Gui/Elements/ToolTip.qml | 8 ++++---- src/EasyApplication/Gui/Globals/Vars.qml | 2 +- src/EasyApplication/Gui/Style/Colors.qml | 2 +- 76 files changed, 193 insertions(+), 193 deletions(-) diff --git a/src/EasyApplication/Gui/Animations/ColorReset.qml b/src/EasyApplication/Gui/Animations/ColorReset.qml index c31ab50e..bfeef0b7 100644 --- a/src/EasyApplication/Gui/Animations/ColorReset.qml +++ b/src/EasyApplication/Gui/Animations/ColorReset.qml @@ -1,6 +1,6 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle SequentialAnimation { property alias target: colorChangeAnimo.target diff --git a/src/EasyApplication/Gui/Animations/ThemeChange.qml b/src/EasyApplication/Gui/Animations/ThemeChange.qml index b97bd3e5..a7536897 100644 --- a/src/EasyApplication/Gui/Animations/ThemeChange.qml +++ b/src/EasyApplication/Gui/Animations/ThemeChange.qml @@ -1,6 +1,6 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle PropertyAnimation { duration: EaStyle.Times.themeChange diff --git a/src/EasyApplication/Gui/Animations/TranslationChange.qml b/src/EasyApplication/Gui/Animations/TranslationChange.qml index 1b88fece..d3a7ab61 100644 --- a/src/EasyApplication/Gui/Animations/TranslationChange.qml +++ b/src/EasyApplication/Gui/Animations/TranslationChange.qml @@ -1,6 +1,6 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle SequentialAnimation { diff --git a/src/EasyApplication/Gui/Charts/ChartViewHeatmap2dPlotly.qml b/src/EasyApplication/Gui/Charts/ChartViewHeatmap2dPlotly.qml index 7cd7b88a..89a46ff8 100644 --- a/src/EasyApplication/Gui/Charts/ChartViewHeatmap2dPlotly.qml +++ b/src/EasyApplication/Gui/Charts/ChartViewHeatmap2dPlotly.qml @@ -2,7 +2,7 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import QtWebEngine 1.10 -import easyApp.Gui.Elements 1.0 as EaElements +import EasyApplication.Gui.Elements 1.0 as EaElements Rectangle { id: container diff --git a/src/EasyApplication/Gui/Charts/ChartViewSimple1dPlotly.qml b/src/EasyApplication/Gui/Charts/ChartViewSimple1dPlotly.qml index b5026add..2f156693 100644 --- a/src/EasyApplication/Gui/Charts/ChartViewSimple1dPlotly.qml +++ b/src/EasyApplication/Gui/Charts/ChartViewSimple1dPlotly.qml @@ -2,7 +2,7 @@ import QtQuick 2.15 import QtQuick.Controls 2.15 import QtWebEngine 1.10 -import easyApp.Gui.Elements 1.0 as EaElements +import EasyApplication.Gui.Elements 1.0 as EaElements Rectangle { id: container diff --git a/src/EasyApplication/Gui/Charts/Plotly1dBarPlot.qml b/src/EasyApplication/Gui/Charts/Plotly1dBarPlot.qml index 78aac480..5bd6a36a 100644 --- a/src/EasyApplication/Gui/Charts/Plotly1dBarPlot.qml +++ b/src/EasyApplication/Gui/Charts/Plotly1dBarPlot.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Controls import QtWebEngine -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle import Gui.Globals as Globals WebEngineView { diff --git a/src/EasyApplication/Gui/Charts/Plotly1dMeasVsCalc.qml b/src/EasyApplication/Gui/Charts/Plotly1dMeasVsCalc.qml index 0db20fe9..dc284f42 100644 --- a/src/EasyApplication/Gui/Charts/Plotly1dMeasVsCalc.qml +++ b/src/EasyApplication/Gui/Charts/Plotly1dMeasVsCalc.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Controls import QtWebEngine -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle WebEngineView { diff --git a/src/EasyApplication/Gui/Charts/QtCharts1dBase 2.qml b/src/EasyApplication/Gui/Charts/QtCharts1dBase 2.qml index a1807fe0..c4e80f6a 100644 --- a/src/EasyApplication/Gui/Charts/QtCharts1dBase 2.qml +++ b/src/EasyApplication/Gui/Charts/QtCharts1dBase 2.qml @@ -2,10 +2,10 @@ import QtQuick import QtQuick.Controls import QtCharts -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Charts as EaCharts +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Charts as EaCharts Rectangle { diff --git a/src/EasyApplication/Gui/Charts/QtCharts1dBase.qml b/src/EasyApplication/Gui/Charts/QtCharts1dBase.qml index 1379eee2..0aaff222 100644 --- a/src/EasyApplication/Gui/Charts/QtCharts1dBase.qml +++ b/src/EasyApplication/Gui/Charts/QtCharts1dBase.qml @@ -2,9 +2,9 @@ import QtQuick import QtQuick.Controls import QtCharts -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Charts as EaCharts +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Charts as EaCharts ChartView { diff --git a/src/EasyApplication/Gui/Charts/QtCharts1dMeasVsCalc.qml b/src/EasyApplication/Gui/Charts/QtCharts1dMeasVsCalc.qml index 948b7085..9c5d1c84 100644 --- a/src/EasyApplication/Gui/Charts/QtCharts1dMeasVsCalc.qml +++ b/src/EasyApplication/Gui/Charts/QtCharts1dMeasVsCalc.qml @@ -2,8 +2,8 @@ import QtQuick import QtQuick.Controls import QtCharts -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Charts as EaCharts +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Charts as EaCharts EaCharts.QtCharts1dBase { diff --git a/src/EasyApplication/Gui/Charts/QtCharts1dValueAxis.qml b/src/EasyApplication/Gui/Charts/QtCharts1dValueAxis.qml index 7385f683..0aafd528 100644 --- a/src/EasyApplication/Gui/Charts/QtCharts1dValueAxis.qml +++ b/src/EasyApplication/Gui/Charts/QtCharts1dValueAxis.qml @@ -1,9 +1,9 @@ import QtQuick import QtCharts -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals 1.0 as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals 1.0 as EaGlobals ValueAxis { diff --git a/src/EasyApplication/Gui/Components/AboutDialog.qml b/src/EasyApplication/Gui/Components/AboutDialog.qml index 05928536..403f172d 100644 --- a/src/EasyApplication/Gui/Components/AboutDialog.qml +++ b/src/EasyApplication/Gui/Components/AboutDialog.qml @@ -4,9 +4,9 @@ import QtQuick.Controls.impl import QtQuick.Controls.Material import QtQuick.Controls.Material.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements EaElements.Dialog { diff --git a/src/EasyApplication/Gui/Components/AppBarCentralTabs.qml b/src/EasyApplication/Gui/Components/AppBarCentralTabs.qml index 87101edd..9bfa2a0f 100644 --- a/src/EasyApplication/Gui/Components/AppBarCentralTabs.qml +++ b/src/EasyApplication/Gui/Components/AppBarCentralTabs.qml @@ -1,8 +1,8 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements EaElements.TabBar { showBottomBorder: true diff --git a/src/EasyApplication/Gui/Components/ApplicationWindow.qml b/src/EasyApplication/Gui/Components/ApplicationWindow.qml index 4bbd05cd..fa906080 100644 --- a/src/EasyApplication/Gui/Components/ApplicationWindow.qml +++ b/src/EasyApplication/Gui/Components/ApplicationWindow.qml @@ -1,11 +1,11 @@ import QtQuick import QtCore -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents EaElements.ApplicationWindow { diff --git a/src/EasyApplication/Gui/Components/BasicReport.qml b/src/EasyApplication/Gui/Components/BasicReport.qml index 6a6fd9a0..676e1b93 100644 --- a/src/EasyApplication/Gui/Components/BasicReport.qml +++ b/src/EasyApplication/Gui/Components/BasicReport.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Controls import QtWebEngine -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle WebEngineView { diff --git a/src/EasyApplication/Gui/Components/ContentPage.qml b/src/EasyApplication/Gui/Components/ContentPage.qml index a0e22ded..7d265ee9 100644 --- a/src/EasyApplication/Gui/Components/ContentPage.qml +++ b/src/EasyApplication/Gui/Components/ContentPage.qml @@ -1,9 +1,9 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements Item { id: page diff --git a/src/EasyApplication/Gui/Components/GuideWindow.qml b/src/EasyApplication/Gui/Components/GuideWindow.qml index 19604553..916a6036 100644 --- a/src/EasyApplication/Gui/Components/GuideWindow.qml +++ b/src/EasyApplication/Gui/Components/GuideWindow.qml @@ -3,9 +3,9 @@ import QtQuick.Controls import QtQuick.Controls.impl import QtQuick.Layouts 1.12 -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements import Gui.Globals as ExGlobals diff --git a/src/EasyApplication/Gui/Components/MainContent.qml b/src/EasyApplication/Gui/Components/MainContent.qml index f45677e5..e771dce8 100644 --- a/src/EasyApplication/Gui/Components/MainContent.qml +++ b/src/EasyApplication/Gui/Components/MainContent.qml @@ -1,7 +1,7 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Elements as EaElements Item { id: mainAreaContainer diff --git a/src/EasyApplication/Gui/Components/PreferencesDialog.qml b/src/EasyApplication/Gui/Components/PreferencesDialog.qml index cc969d76..ba9a04cd 100644 --- a/src/EasyApplication/Gui/Components/PreferencesDialog.qml +++ b/src/EasyApplication/Gui/Components/PreferencesDialog.qml @@ -6,10 +6,10 @@ import QtQuick.Controls.Material.impl //import QtQuick.XmlListModel 2.15 import QtCore -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents EaElements.Dialog { diff --git a/src/EasyApplication/Gui/Components/ProjectDescriptionDialog.qml b/src/EasyApplication/Gui/Components/ProjectDescriptionDialog.qml index 371154e7..57b705bb 100644 --- a/src/EasyApplication/Gui/Components/ProjectDescriptionDialog.qml +++ b/src/EasyApplication/Gui/Components/ProjectDescriptionDialog.qml @@ -3,10 +3,10 @@ import QtQuick.Controls import QtQuick.Dialogs import QtCore -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Logic as EaLogic +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Logic as EaLogic EaElements.Dialog { id: dialog diff --git a/src/EasyApplication/Gui/Components/SideBar.qml b/src/EasyApplication/Gui/Components/SideBar.qml index b4e31031..fab6c9f7 100644 --- a/src/EasyApplication/Gui/Components/SideBar.qml +++ b/src/EasyApplication/Gui/Components/SideBar.qml @@ -2,8 +2,8 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Material -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements Item { id: sideBarContainer diff --git a/src/EasyApplication/Gui/Components/SideBarColumn.qml b/src/EasyApplication/Gui/Components/SideBarColumn.qml index 6ee0415c..f58486c2 100644 --- a/src/EasyApplication/Gui/Components/SideBarColumn.qml +++ b/src/EasyApplication/Gui/Components/SideBarColumn.qml @@ -1,8 +1,8 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements Flickable { diff --git a/src/EasyApplication/Gui/Components/TableView.qml b/src/EasyApplication/Gui/Components/TableView.qml index 72e906ef..543d3728 100644 --- a/src/EasyApplication/Gui/Components/TableView.qml +++ b/src/EasyApplication/Gui/Components/TableView.qml @@ -1,11 +1,11 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Components as EaComponents +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Components as EaComponents ListView { diff --git a/src/EasyApplication/Gui/Components/TableViewAdvancedLabel.qml b/src/EasyApplication/Gui/Components/TableViewAdvancedLabel.qml index ab602f16..7639f7f6 100644 --- a/src/EasyApplication/Gui/Components/TableViewAdvancedLabel.qml +++ b/src/EasyApplication/Gui/Components/TableViewAdvancedLabel.qml @@ -3,10 +3,10 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.Button { id: control diff --git a/src/EasyApplication/Gui/Components/TableViewButton.qml b/src/EasyApplication/Gui/Components/TableViewButton.qml index 358f3069..9f8b4185 100644 --- a/src/EasyApplication/Gui/Components/TableViewButton.qml +++ b/src/EasyApplication/Gui/Components/TableViewButton.qml @@ -1,7 +1,7 @@ import QtQuick -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle EaElements.TabButton { property int horizontalAlignment: Text.AlignHCenter diff --git a/src/EasyApplication/Gui/Components/TableViewCheckBox.qml b/src/EasyApplication/Gui/Components/TableViewCheckBox.qml index 828f85bd..803a6599 100644 --- a/src/EasyApplication/Gui/Components/TableViewCheckBox.qml +++ b/src/EasyApplication/Gui/Components/TableViewCheckBox.qml @@ -1,6 +1,6 @@ import QtQuick -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Elements as EaElements EaElements.CheckBox { property int horizontalAlignment: Text.AlignHCenter diff --git a/src/EasyApplication/Gui/Components/TableViewComboBox.qml b/src/EasyApplication/Gui/Components/TableViewComboBox.qml index fa196dd8..3b0bc055 100644 --- a/src/EasyApplication/Gui/Components/TableViewComboBox.qml +++ b/src/EasyApplication/Gui/Components/TableViewComboBox.qml @@ -1,6 +1,6 @@ import QtQuick -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Elements as EaElements EaElements.ComboBox { property int horizontalAlignment: Text.AlignHCenter diff --git a/src/EasyApplication/Gui/Components/TableViewDelegate.qml b/src/EasyApplication/Gui/Components/TableViewDelegate.qml index 07093035..8916937d 100644 --- a/src/EasyApplication/Gui/Components/TableViewDelegate.qml +++ b/src/EasyApplication/Gui/Components/TableViewDelegate.qml @@ -1,7 +1,7 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations Rectangle { id: control diff --git a/src/EasyApplication/Gui/Components/TableViewHeader.qml b/src/EasyApplication/Gui/Components/TableViewHeader.qml index caba7f03..43a63d37 100644 --- a/src/EasyApplication/Gui/Components/TableViewHeader.qml +++ b/src/EasyApplication/Gui/Components/TableViewHeader.qml @@ -1,8 +1,8 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations Rectangle { default property alias contentRowData: contentRow.data diff --git a/src/EasyApplication/Gui/Components/TableViewLabel.qml b/src/EasyApplication/Gui/Components/TableViewLabel.qml index 3aeab937..bff527a2 100644 --- a/src/EasyApplication/Gui/Components/TableViewLabel.qml +++ b/src/EasyApplication/Gui/Components/TableViewLabel.qml @@ -1,8 +1,8 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements EaElements.Label { id: control diff --git a/src/EasyApplication/Gui/Components/TableViewLabelControl.qml b/src/EasyApplication/Gui/Components/TableViewLabelControl.qml index 3846e417..cb47fbdf 100644 --- a/src/EasyApplication/Gui/Components/TableViewLabelControl.qml +++ b/src/EasyApplication/Gui/Components/TableViewLabelControl.qml @@ -3,10 +3,10 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.Button { id: control diff --git a/src/EasyApplication/Gui/Components/TableViewParameter.qml b/src/EasyApplication/Gui/Components/TableViewParameter.qml index 0b105da8..bf2b5270 100644 --- a/src/EasyApplication/Gui/Components/TableViewParameter.qml +++ b/src/EasyApplication/Gui/Components/TableViewParameter.qml @@ -1,8 +1,8 @@ import QtQuick -import EasyApp.Gui.Logic as EaLogic -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Logic as EaLogic +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements EaElements.TextInput { id: control diff --git a/src/EasyApplication/Gui/Components/TableViewTextInput.qml b/src/EasyApplication/Gui/Components/TableViewTextInput.qml index fecc3d9e..45d283ec 100644 --- a/src/EasyApplication/Gui/Components/TableViewTextInput.qml +++ b/src/EasyApplication/Gui/Components/TableViewTextInput.qml @@ -1,6 +1,6 @@ import QtQuick -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Elements as EaElements EaElements.TextInput { property string headerText: "" diff --git a/src/EasyApplication/Gui/Components/TableViewTwoRowsAdvancedLabel.qml b/src/EasyApplication/Gui/Components/TableViewTwoRowsAdvancedLabel.qml index 1ba75252..0eb081dc 100644 --- a/src/EasyApplication/Gui/Components/TableViewTwoRowsAdvancedLabel.qml +++ b/src/EasyApplication/Gui/Components/TableViewTwoRowsAdvancedLabel.qml @@ -3,10 +3,10 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.Button { id: control diff --git a/src/EasyApplication/Gui/Elements/AppBarTabButton.qml b/src/EasyApplication/Gui/Elements/AppBarTabButton.qml index 47b81027..f8a80db3 100644 --- a/src/EasyApplication/Gui/Elements/AppBarTabButton.qml +++ b/src/EasyApplication/Gui/Elements/AppBarTabButton.qml @@ -1,8 +1,8 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements EaElements.TabButton { id: control diff --git a/src/EasyApplication/Gui/Elements/ApplicationWindow.qml b/src/EasyApplication/Gui/Elements/ApplicationWindow.qml index f6de7891..f2d33f37 100644 --- a/src/EasyApplication/Gui/Elements/ApplicationWindow.qml +++ b/src/EasyApplication/Gui/Elements/ApplicationWindow.qml @@ -2,12 +2,12 @@ import QtQuick import QtQuick.Controls import QtQuick.Templates as T -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements -import EasyApp.Logic.Maintenance as EaMaintenance +import EasyApplication.Logic.Maintenance as EaMaintenance T.ApplicationWindow { diff --git a/src/EasyApplication/Gui/Elements/Button.qml b/src/EasyApplication/Gui/Elements/Button.qml index 813d1591..7c666e1c 100644 --- a/src/EasyApplication/Gui/Elements/Button.qml +++ b/src/EasyApplication/Gui/Elements/Button.qml @@ -3,8 +3,8 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations T.Button { diff --git a/src/EasyApplication/Gui/Elements/CheckBox.qml b/src/EasyApplication/Gui/Elements/CheckBox.qml index 7c9c18e1..56ca8985 100644 --- a/src/EasyApplication/Gui/Elements/CheckBox.qml +++ b/src/EasyApplication/Gui/Elements/CheckBox.qml @@ -1,10 +1,10 @@ import QtQuick import QtQuick.Templates as T -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.CheckBox { id: control diff --git a/src/EasyApplication/Gui/Elements/CheckIndicator.qml b/src/EasyApplication/Gui/Elements/CheckIndicator.qml index 6f238d6b..a7a88508 100644 --- a/src/EasyApplication/Gui/Elements/CheckIndicator.qml +++ b/src/EasyApplication/Gui/Elements/CheckIndicator.qml @@ -1,9 +1,9 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements Rectangle { id: indicatorItem diff --git a/src/EasyApplication/Gui/Elements/ComboBox.qml b/src/EasyApplication/Gui/Elements/ComboBox.qml index 30b2be16..99d76cdf 100644 --- a/src/EasyApplication/Gui/Elements/ComboBox.qml +++ b/src/EasyApplication/Gui/Elements/ComboBox.qml @@ -5,10 +5,10 @@ import QtQuick.Controls.impl import QtQuick.Controls.Material.impl import QtQuick.Templates as T -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.ComboBox { id: control diff --git a/src/EasyApplication/Gui/Elements/CursorDelegate.qml b/src/EasyApplication/Gui/Elements/CursorDelegate.qml index 86dc5ef7..a2c2fea5 100644 --- a/src/EasyApplication/Gui/Elements/CursorDelegate.qml +++ b/src/EasyApplication/Gui/Elements/CursorDelegate.qml @@ -1,6 +1,6 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle Rectangle { id: cursor diff --git a/src/EasyApplication/Gui/Elements/Dialog.qml b/src/EasyApplication/Gui/Elements/Dialog.qml index 9b7e7574..feb6cff1 100644 --- a/src/EasyApplication/Gui/Elements/Dialog.qml +++ b/src/EasyApplication/Gui/Elements/Dialog.qml @@ -4,10 +4,10 @@ import QtQuick.Controls.impl import QtQuick.Controls.Material.impl import QtQuick.Templates as T -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.Dialog { id: control diff --git a/src/EasyApplication/Gui/Elements/DialogButtonBox.qml b/src/EasyApplication/Gui/Elements/DialogButtonBox.qml index 0b985c82..ac391ef3 100644 --- a/src/EasyApplication/Gui/Elements/DialogButtonBox.qml +++ b/src/EasyApplication/Gui/Elements/DialogButtonBox.qml @@ -3,9 +3,9 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.DialogButtonBox { id: control diff --git a/src/EasyApplication/Gui/Elements/GroupBox.qml b/src/EasyApplication/Gui/Elements/GroupBox.qml index dd34b8ed..526f9298 100644 --- a/src/EasyApplication/Gui/Elements/GroupBox.qml +++ b/src/EasyApplication/Gui/Elements/GroupBox.qml @@ -3,10 +3,10 @@ import QtQuick.Controls import QtQuick.Controls.Basic // Fixes EasyApp/Gui/Elements/GroupBox.qml:51:22: QML Row: The current style does not support customization of this control (property: "contentItem" item: QQuickRow(0x60000381e200, parent=0x0, geometry=0,0 0x0)). Please customize a non-native style (such as Basic, Fusion, Material, etc). For more information, see: https://doc.qt.io/qt-6/qtquickcontrols2-customize.html#customization-reference import QtQuick.Templates as T -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements -import EasyApp.Gui.Animations as EaAnimations +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements +import EasyApplication.Gui.Animations as EaAnimations T.GroupBox { diff --git a/src/EasyApplication/Gui/Elements/GroupButton.qml b/src/EasyApplication/Gui/Elements/GroupButton.qml index ec7ba857..ae4492c8 100644 --- a/src/EasyApplication/Gui/Elements/GroupButton.qml +++ b/src/EasyApplication/Gui/Elements/GroupButton.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Material -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle Button { implicitWidth: (parent.width - parent.spacing) * 0.5 - 1 diff --git a/src/EasyApplication/Gui/Elements/GroupColumn.qml b/src/EasyApplication/Gui/Elements/GroupColumn.qml index fba6af32..baf15b6c 100644 --- a/src/EasyApplication/Gui/Elements/GroupColumn.qml +++ b/src/EasyApplication/Gui/Elements/GroupColumn.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Material -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle Column { width: parent.width - EaStyle.Sizes.fontPixelSize diff --git a/src/EasyApplication/Gui/Elements/GroupRow.qml b/src/EasyApplication/Gui/Elements/GroupRow.qml index e8710d71..7dbb1b20 100644 --- a/src/EasyApplication/Gui/Elements/GroupRow.qml +++ b/src/EasyApplication/Gui/Elements/GroupRow.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Controls import QtQuick.Controls.Material -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle Row { spacing: EaStyle.Sizes.groupBoxSpacing * 0.5 diff --git a/src/EasyApplication/Gui/Elements/Label.qml b/src/EasyApplication/Gui/Elements/Label.qml index aeab7ae2..7f6a0f56 100644 --- a/src/EasyApplication/Gui/Elements/Label.qml +++ b/src/EasyApplication/Gui/Elements/Label.qml @@ -1,8 +1,8 @@ import QtQuick import QtQuick.Templates as T -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations T.Label { id: control diff --git a/src/EasyApplication/Gui/Elements/LinkedImage.qml b/src/EasyApplication/Gui/Elements/LinkedImage.qml index e4e40f0b..b62565d6 100644 --- a/src/EasyApplication/Gui/Elements/LinkedImage.qml +++ b/src/EasyApplication/Gui/Elements/LinkedImage.qml @@ -1,6 +1,6 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle Image { id: control diff --git a/src/EasyApplication/Gui/Elements/Menu.qml b/src/EasyApplication/Gui/Elements/Menu.qml index b481479b..ede3433c 100644 --- a/src/EasyApplication/Gui/Elements/Menu.qml +++ b/src/EasyApplication/Gui/Elements/Menu.qml @@ -4,7 +4,7 @@ import QtQuick.Controls.Material import QtQuick.Controls.Material.impl import QtQuick.Templates as T -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle T.Menu { id: control diff --git a/src/EasyApplication/Gui/Elements/MenuItem.qml b/src/EasyApplication/Gui/Elements/MenuItem.qml index b20fd2fd..344c2e0b 100644 --- a/src/EasyApplication/Gui/Elements/MenuItem.qml +++ b/src/EasyApplication/Gui/Elements/MenuItem.qml @@ -3,10 +3,10 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.MenuItem { id: control diff --git a/src/EasyApplication/Gui/Elements/ParamComboBox.qml b/src/EasyApplication/Gui/Elements/ParamComboBox.qml index 48bdd089..4d1df5bf 100644 --- a/src/EasyApplication/Gui/Elements/ParamComboBox.qml +++ b/src/EasyApplication/Gui/Elements/ParamComboBox.qml @@ -3,9 +3,9 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements EaElements.ComboBox { diff --git a/src/EasyApplication/Gui/Elements/ParamTextField.qml b/src/EasyApplication/Gui/Elements/ParamTextField.qml index e84e4936..d794a3d7 100644 --- a/src/EasyApplication/Gui/Elements/ParamTextField.qml +++ b/src/EasyApplication/Gui/Elements/ParamTextField.qml @@ -3,9 +3,9 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Logic as EaLogic -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Logic as EaLogic +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements EaElements.TextField { diff --git a/src/EasyApplication/Gui/Elements/Parameter.qml b/src/EasyApplication/Gui/Elements/Parameter.qml index 271b425c..86fd6346 100644 --- a/src/EasyApplication/Gui/Elements/Parameter.qml +++ b/src/EasyApplication/Gui/Elements/Parameter.qml @@ -3,9 +3,9 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements EaElements.TextField { diff --git a/src/EasyApplication/Gui/Elements/RadioButton.qml b/src/EasyApplication/Gui/Elements/RadioButton.qml index 6d265d7a..32be0357 100644 --- a/src/EasyApplication/Gui/Elements/RadioButton.qml +++ b/src/EasyApplication/Gui/Elements/RadioButton.qml @@ -2,10 +2,10 @@ import QtQuick import QtQuick.Controls import QtQuick.Templates as T -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.RadioButton { id: control diff --git a/src/EasyApplication/Gui/Elements/RadioIndicator.qml b/src/EasyApplication/Gui/Elements/RadioIndicator.qml index 68c4ad57..bd9bc9e9 100644 --- a/src/EasyApplication/Gui/Elements/RadioIndicator.qml +++ b/src/EasyApplication/Gui/Elements/RadioIndicator.qml @@ -1,6 +1,6 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle Rectangle { id: indicator diff --git a/src/EasyApplication/Gui/Elements/RemoteController.qml b/src/EasyApplication/Gui/Elements/RemoteController.qml index 53a83940..b131617a 100644 --- a/src/EasyApplication/Gui/Elements/RemoteController.qml +++ b/src/EasyApplication/Gui/Elements/RemoteController.qml @@ -3,9 +3,9 @@ import QtQuick.Controls import QtMultimedia import QtTest -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Elements as EaElements Item { id: rc diff --git a/src/EasyApplication/Gui/Elements/RunningLabel.qml b/src/EasyApplication/Gui/Elements/RunningLabel.qml index 8ec9182f..e7c798fe 100644 --- a/src/EasyApplication/Gui/Elements/RunningLabel.qml +++ b/src/EasyApplication/Gui/Elements/RunningLabel.qml @@ -1,7 +1,7 @@ import QtQuick -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements Item { id: control diff --git a/src/EasyApplication/Gui/Elements/ScrollBar.qml b/src/EasyApplication/Gui/Elements/ScrollBar.qml index 9aa4eb54..cd7f2a6e 100644 --- a/src/EasyApplication/Gui/Elements/ScrollBar.qml +++ b/src/EasyApplication/Gui/Elements/ScrollBar.qml @@ -2,7 +2,7 @@ import QtQuick import QtQuick.Templates as T import QtQuick.Controls.Material -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle T.ScrollBar { id: control diff --git a/src/EasyApplication/Gui/Elements/ScrollIndicator.qml b/src/EasyApplication/Gui/Elements/ScrollIndicator.qml index 0f10c22c..81e2f98c 100644 --- a/src/EasyApplication/Gui/Elements/ScrollIndicator.qml +++ b/src/EasyApplication/Gui/Elements/ScrollIndicator.qml @@ -1,7 +1,7 @@ import QtQuick import QtQuick.Templates as T -import EasyApp.Gui.Style as EaStyle +import EasyApplication.Gui.Style as EaStyle T.ScrollIndicator { id: control diff --git a/src/EasyApplication/Gui/Elements/SideBarButton.qml b/src/EasyApplication/Gui/Elements/SideBarButton.qml index 204a5e2a..5faf38e6 100644 --- a/src/EasyApplication/Gui/Elements/SideBarButton.qml +++ b/src/EasyApplication/Gui/Elements/SideBarButton.qml @@ -3,10 +3,10 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.Button { id: control diff --git a/src/EasyApplication/Gui/Elements/Slider.qml b/src/EasyApplication/Gui/Elements/Slider.qml index 955f1893..e95f9e90 100644 --- a/src/EasyApplication/Gui/Elements/Slider.qml +++ b/src/EasyApplication/Gui/Elements/Slider.qml @@ -3,10 +3,10 @@ import QtQuick.Templates as T import QtQuick.Controls.Material import QtQuick.Controls.Material.impl -import EasyApp.Gui.Logic as EaLogic -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Logic as EaLogic +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.Slider { id: control diff --git a/src/EasyApplication/Gui/Elements/SliderHandle.qml b/src/EasyApplication/Gui/Elements/SliderHandle.qml index e84057fc..88ed8d0f 100644 --- a/src/EasyApplication/Gui/Elements/SliderHandle.qml +++ b/src/EasyApplication/Gui/Elements/SliderHandle.qml @@ -2,8 +2,8 @@ import QtQuick import QtQuick.Controls.Material import QtQuick.Controls.Material.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations Item { id: root diff --git a/src/EasyApplication/Gui/Elements/SpinBox.qml b/src/EasyApplication/Gui/Elements/SpinBox.qml index 44324aed..b7c2faa9 100644 --- a/src/EasyApplication/Gui/Elements/SpinBox.qml +++ b/src/EasyApplication/Gui/Elements/SpinBox.qml @@ -1,9 +1,9 @@ import QtQuick import QtQuick.Templates as T -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.SpinBox { diff --git a/src/EasyApplication/Gui/Elements/SplashScreen.qml b/src/EasyApplication/Gui/Elements/SplashScreen.qml index cce6871b..b42b7930 100644 --- a/src/EasyApplication/Gui/Elements/SplashScreen.qml +++ b/src/EasyApplication/Gui/Elements/SplashScreen.qml @@ -3,8 +3,8 @@ import QtQuick.Controls import QtQuick.Window -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Elements as EaElements Window { diff --git a/src/EasyApplication/Gui/Elements/StatusBar.qml b/src/EasyApplication/Gui/Elements/StatusBar.qml index c50d6acd..11ac8a06 100644 --- a/src/EasyApplication/Gui/Elements/StatusBar.qml +++ b/src/EasyApplication/Gui/Elements/StatusBar.qml @@ -1,9 +1,9 @@ import QtQuick import QtQuick.Controls -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements Rectangle { id: statusBar diff --git a/src/EasyApplication/Gui/Elements/StatusBarItem.qml b/src/EasyApplication/Gui/Elements/StatusBarItem.qml index 2fadc841..fec4624e 100644 --- a/src/EasyApplication/Gui/Elements/StatusBarItem.qml +++ b/src/EasyApplication/Gui/Elements/StatusBarItem.qml @@ -3,10 +3,10 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.Button { id: control diff --git a/src/EasyApplication/Gui/Elements/TabBar.qml b/src/EasyApplication/Gui/Elements/TabBar.qml index c11428a7..d92ec211 100644 --- a/src/EasyApplication/Gui/Elements/TabBar.qml +++ b/src/EasyApplication/Gui/Elements/TabBar.qml @@ -3,8 +3,8 @@ import QtQuick.Templates as T import QtQuick.Controls.Material import QtQuick.Controls.Material.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations T.TabBar { id: control diff --git a/src/EasyApplication/Gui/Elements/TabButton.qml b/src/EasyApplication/Gui/Elements/TabButton.qml index 69f1c285..851643a9 100644 --- a/src/EasyApplication/Gui/Elements/TabButton.qml +++ b/src/EasyApplication/Gui/Elements/TabButton.qml @@ -5,10 +5,10 @@ import QtQuick.Controls.impl import QtQuick.Controls.Material import QtQuick.Controls.Material.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.TabButton { id: control diff --git a/src/EasyApplication/Gui/Elements/TextArea.qml b/src/EasyApplication/Gui/Elements/TextArea.qml index 4dd4c570..12d36a86 100644 --- a/src/EasyApplication/Gui/Elements/TextArea.qml +++ b/src/EasyApplication/Gui/Elements/TextArea.qml @@ -3,9 +3,9 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.TextArea { diff --git a/src/EasyApplication/Gui/Elements/TextField.qml b/src/EasyApplication/Gui/Elements/TextField.qml index ddebe6e1..16af3c05 100644 --- a/src/EasyApplication/Gui/Elements/TextField.qml +++ b/src/EasyApplication/Gui/Elements/TextField.qml @@ -3,9 +3,9 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.TextField { id: control diff --git a/src/EasyApplication/Gui/Elements/TextInput.qml b/src/EasyApplication/Gui/Elements/TextInput.qml index 54a91ebf..fc960d15 100644 --- a/src/EasyApplication/Gui/Elements/TextInput.qml +++ b/src/EasyApplication/Gui/Elements/TextInput.qml @@ -3,9 +3,9 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.TextField { diff --git a/src/EasyApplication/Gui/Elements/ToolButton.qml b/src/EasyApplication/Gui/Elements/ToolButton.qml index 9c6ab8ca..0832ced7 100644 --- a/src/EasyApplication/Gui/Elements/ToolButton.qml +++ b/src/EasyApplication/Gui/Elements/ToolButton.qml @@ -3,10 +3,10 @@ import QtQuick.Templates as T import QtQuick.Controls import QtQuick.Controls.impl -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.ToolButton { id: control diff --git a/src/EasyApplication/Gui/Elements/ToolTip.qml b/src/EasyApplication/Gui/Elements/ToolTip.qml index c0eabaca..ec4a29b0 100644 --- a/src/EasyApplication/Gui/Elements/ToolTip.qml +++ b/src/EasyApplication/Gui/Elements/ToolTip.qml @@ -3,10 +3,10 @@ import QtQuick.Effects import QtQuick.Controls.Material.impl import QtQuick.Templates as T -import EasyApp.Gui.Style as EaStyle -import EasyApp.Gui.Globals as EaGlobals -import EasyApp.Gui.Animations as EaAnimations -import EasyApp.Gui.Elements as EaElements +import EasyApplication.Gui.Style as EaStyle +import EasyApplication.Gui.Globals as EaGlobals +import EasyApplication.Gui.Animations as EaAnimations +import EasyApplication.Gui.Elements as EaElements T.ToolTip { diff --git a/src/EasyApplication/Gui/Globals/Vars.qml b/src/EasyApplication/Gui/Globals/Vars.qml index b541e8b3..1acdf7e6 100644 --- a/src/EasyApplication/Gui/Globals/Vars.qml +++ b/src/EasyApplication/Gui/Globals/Vars.qml @@ -3,7 +3,7 @@ pragma Singleton import QtQuick import QtCore -import EasyApp.Gui.Logic as EaLogic +import EasyApplication.Gui.Logic as EaLogic QtObject { diff --git a/src/EasyApplication/Gui/Style/Colors.qml b/src/EasyApplication/Gui/Style/Colors.qml index 6fcb5843..294ce144 100644 --- a/src/EasyApplication/Gui/Style/Colors.qml +++ b/src/EasyApplication/Gui/Style/Colors.qml @@ -4,7 +4,7 @@ import QtQuick import QtQuick.Controls.Material import QtCore -import EasyApp.Gui.Globals as EaGlobals +import EasyApplication.Gui.Globals as EaGlobals QtObject { From 7e1db01035e4678132f572a9d9af152b46a5438f Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 29 Apr 2026 16:47:02 +0200 Subject: [PATCH 6/7] Update copier answers --- .copier-answers.yml | 8 +- .github/workflows/docs.yml | 2 +- .github/workflows/pypi-publish.yml | 4 +- .github/workflows/pypi-test.yml | 20 +- .github/workflows/test.yml | 26 +- CONTRIBUTING.md | 11 +- README.md | 25 +- docs/docs/installation-and-setup/index.md | 28 +- docs/docs/introduction/index.md | 12 +- docs/docs/tutorials/tutorial.py | 4 +- docs/mkdocs.yml | 6 +- pixi.lock | 854 +++++++++++----------- pixi.toml | 32 +- pyproject.toml | 11 +- 14 files changed, 516 insertions(+), 527 deletions(-) diff --git a/.copier-answers.yml b/.copier-answers.yml index a2c2a231..a8e73301 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -2,12 +2,12 @@ # Any changes will be overwritten by Copier. _commit: v0.11.0 _src_path: gh:easyscience/templates -lib_docs_url: https://easyscience.github.io/easyapp +lib_docs_url: https://easyscience.github.io/gui-components lib_doi: 10.5281/zenodo.18163581 -lib_package_name: easyapplication +lib_package_name: EasyApplication lib_python_max: '3.14' -lib_python_min: '3.11' -lib_repo_name: easyapp +lib_python_min: '3.12' +lib_repo_name: gui-components project_contact_email: support@easyscience.org project_copyright_years: 2021-2026 project_extended_description: A collection of Qt/QML graphical components to create diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 642f8757..b60bbe40 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -108,7 +108,7 @@ jobs: # imported. This step allows to avoid "Matplotlib is building the font # cache" messages during notebook execution. - name: Pre-build site step - run: pixi run python -c "import easyapplication" + run: pixi run python -c "import EasyApplication" # Prepare the Jupyter notebooks for documentation (strip output, etc.). - name: Prepare notebooks diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index ed231ca8..b03a4859 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -40,10 +40,10 @@ jobs: # Instead of publishing with personal access token, we use # GitHub Actions OIDC to get a short-lived token from PyPI. # New publisher must be previously configured in PyPI at - # https://pypi.org/manage/project/easyapplication/settings/publishing/ + # https://pypi.org/manage/project/EasyApplication/settings/publishing/ # Use the following data: # Owner: easyscience - # Repository name: easyapp + # Repository name: gui-components # Workflow name: pypi-publish.yml - name: Publish to PyPI uses: pypa/gh-action-pypi-publish@release/v1 diff --git a/.github/workflows/pypi-test.yml b/.github/workflows/pypi-test.yml index 8acd655d..6c3cb94e 100644 --- a/.github/workflows/pypi-test.yml +++ b/.github/workflows/pypi-test.yml @@ -46,34 +46,34 @@ jobs: frozen: false - name: Init pixi project - run: pixi init easyapplication + run: pixi init EasyApplication - name: Set the minimum system requirements - working-directory: easyapplication + working-directory: EasyApplication run: pixi project system-requirements add macos 14.0 - name: Add Python 3.14 from Conda - working-directory: easyapplication + working-directory: EasyApplication run: pixi add "python=3.14" - name: Add other Conda dependencies - working-directory: easyapplication + working-directory: EasyApplication run: pixi add gsl - - name: Add easyapplication (with dev dependencies) from PyPI - working-directory: easyapplication - run: pixi add --pypi "easyapplication[dev]" + - name: Add EasyApplication (with dev dependencies) from PyPI + working-directory: EasyApplication + run: pixi add --pypi "EasyApplication[dev]" - name: Run unit tests to verify the installation - working-directory: easyapplication + working-directory: EasyApplication run: pixi run python -m pytest ../tests/unit/ --color=yes -v - name: Run functional tests to verify the installation - working-directory: easyapplication + working-directory: EasyApplication run: pixi run python -m pytest ../tests/functional/ --color=yes -v - name: Run integration tests to verify the installation - working-directory: easyapplication + working-directory: EasyApplication run: pixi run python -m pytest ../tests/integration/ --color=yes -n auto # Job 2: Build and publish dashboard (reusable workflow) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2ea7a481..313d9552 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,8 +44,8 @@ concurrency: # Set the environment variables to be used in all jobs defined in this workflow env: CI_BRANCH: ${{ github.head_ref || github.ref_name }} - PY_VERSIONS: '3.11 3.14' - PIXI_ENVS: 'py-311-env py-314-env' + PY_VERSIONS: '3.12 3.14' + PIXI_ENVS: 'py-312-env py-314-env' # Opt into Node.js 24 for all JavaScript actions. # Remove once all referenced actions natively target Node 24. FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true @@ -204,7 +204,7 @@ jobs: run-install: false frozen: false - - name: Install easyapplication from the built wheel + - name: Install EasyApplication from the built wheel shell: bash run: | set -euo pipefail @@ -214,8 +214,8 @@ jobs: echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" echo "Initializing pixi project" - pixi init easyapplication_py$py_ver - cd easyapplication_py$py_ver + pixi init EasyApplication_py$py_ver + cd EasyApplication_py$py_ver echo "Adding Python $py_ver" pixi add "python=$py_ver" @@ -234,8 +234,8 @@ jobs: whl_url="file://$(python -c 'import os,sys; print(os.path.abspath(sys.argv[1]))' "${whl_path[0]}")" - echo "Adding easyapplication from: $whl_url" - pixi add --pypi "easyapplication[dev] @ ${whl_url}" + echo "Adding EasyApplication from: $whl_url" + pixi add --pypi "EasyApplication[dev] @ ${whl_url}" echo "Exiting pixi project directory" cd .. @@ -250,8 +250,8 @@ jobs: echo echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" - echo "Entering pixi project directory easyapplication_py$py_ver" - cd easyapplication_py$py_ver + echo "Entering pixi project directory EasyApplication_py$py_ver" + cd EasyApplication_py$py_ver echo "Running tests" pixi run python -m pytest ../tests/unit/ --color=yes -v @@ -269,8 +269,8 @@ jobs: echo echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" - echo "Entering pixi project directory easyapplication_py$py_ver" - cd easyapplication_py$py_ver + echo "Entering pixi project directory EasyApplication_py$py_ver" + cd EasyApplication_py$py_ver echo "Running tests" pixi run python -m pytest ../tests/functional/ --color=yes -v @@ -288,8 +288,8 @@ jobs: echo echo "🔹🔸🔹🔸🔹 Python: $py_ver 🔹🔸🔹🔸🔹" - echo "Entering pixi project directory easyapplication_py$py_ver" - cd easyapplication_py$py_ver + echo "Entering pixi project directory EasyApplication_py$py_ver" + cd EasyApplication_py$py_ver echo "Running tests" pixi run python -m pytest ../tests/integration/ --color=yes -n auto -v ${{ needs.env-prepare.outputs.pytest-marks }} diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c16a65b..0476c549 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -45,7 +45,7 @@ If you are not planning to contribute code, you may want to: - 🛡 Report a security issue — see [Security Issues](#12-security-issues) - 💬 Ask a question or start a discussion at - [Project Discussions](https://github.com/easyscience/easyapp/discussions) + [Project Discussions](https://github.com/easyscience/gui-components/discussions) If you plan to contribute code or documentation, continue below. @@ -84,7 +84,8 @@ strategy. If you are not a core maintainer of this repository, follow these steps. -1. Open the repository page: `https://github.com/easyscience/easyapp` +1. Open the repository page: + `https://github.com/easyscience/gui-components` 2. Click the **Fork** button (top-right corner). This creates your own copy of the repository. @@ -92,14 +93,14 @@ If you are not a core maintainer of this repository, follow these steps. 3. Clone your fork locally: ```bash - git clone https://github.com//easyapp.git - cd easyapp + git clone https://github.com//gui-components.git + cd gui-components ``` 4. Add the original repository as `upstream`: ```bash - git remote add upstream https://github.com/easyscience/easyapp.git + git remote add upstream https://github.com/easyscience/gui-components.git ``` 5. Switch to the `develop` branch and update it: diff --git a/README.md b/README.md index fd0efda4..413b7359 100644 --- a/README.md +++ b/README.md @@ -20,25 +20,32 @@ based on the EasyScience framework. License: [BSD 3-Clause](https://github.com/easyscience/easyapp/blob/master/LICENSE) +License: +[BSD 3-Clause](https://github.com/easyscience/gui-components/blob/master/LICENSE) + ## Useful Links ### For Users -- 📖 [Documentation](https://easyscience.github.io/easyapp/latest) +- 📖 + [Documentation](https://easyscience.github.io/gui-components/latest) - 🚀 - [Getting Started](https://easyscience.github.io/easyapp/latest/introduction) -- 🧪 [Tutorials](https://easyscience.github.io/easyapp/latest/tutorials) + [Getting Started](https://easyscience.github.io/gui-components/latest/introduction) +- 🧪 + [Tutorials](https://easyscience.github.io/gui-components/latest/tutorials) - 💬 - [Get in Touch](https://easyscience.github.io/easyapp/latest/introduction/#get-in-touch) + [Get in Touch](https://easyscience.github.io/gui-components/latest/introduction/#get-in-touch) - 🧾 - [Citation](https://easyscience.github.io/easyapp/latest/introduction/#citation) + [Citation](https://easyscience.github.io/gui-components/latest/introduction/#citation) ### For Contributors -- 🧑‍💻 [Source Code](https://github.com/easyscience/easyapp) -- 🐞 [Issue Tracker](https://github.com/easyscience/easyapp/issues) -- 💡 [Discussions](https://github.com/easyscience/easyapp/discussions) +- 🧑‍💻 [Source Code](https://github.com/easyscience/gui-components) +- 🐞 + [Issue Tracker](https://github.com/easyscience/gui-components/issues) +- 💡 + [Discussions](https://github.com/easyscience/gui-components/discussions) - 🤝 - [Contributing Guide](https://github.com/easyscience/easyapp/blob/master/CONTRIBUTING.md) + [Contributing Guide](https://github.com/easyscience/gui-components/blob/master/CONTRIBUTING.md) - 🛡 [Code of Conduct](https://github.com/easyscience/.github/blob/master/CODE_OF_CONDUCT.md) diff --git a/docs/docs/installation-and-setup/index.md b/docs/docs/installation-and-setup/index.md index 86cc9d05..ce61fd48 100644 --- a/docs/docs/installation-and-setup/index.md +++ b/docs/docs/installation-and-setup/index.md @@ -5,7 +5,7 @@ icon: material/cog-box # :material-cog-box: Installation & Setup **EasyApplication** is a cross-platform Python library compatible with -**Python 3.11** through **3.14**. +**Python 3.12** through **3.14**. To install and set up EasyApplication, we recommend using [**Pixi**](https://pixi.prefix.dev), a modern package manager for @@ -59,8 +59,8 @@ This section describes the simplest way to set up EasyApplication using - Initialize a new Pixi project and navigate into it: ```txt - pixi init easyapplication - cd easyapplication + pixi init EasyApplication + cd EasyApplication ``` - Set the Python version for the Pixi environment (e.g., 3.14): ```txt @@ -68,11 +68,11 @@ This section describes the simplest way to set up EasyApplication using ``` - Add EasyApplication to the Pixi environment from PyPI: ```txt - pixi add --pypi easyapplication + pixi add --pypi EasyApplication ``` - Add a Pixi task to run EasyApplication commands easily: ```txt - pixi task add easyapplication "python -m easyapplication" + pixi task add EasyApplication "python -m EasyApplication" ``` #### Updating Pixi and EasyApplication @@ -165,32 +165,32 @@ EasyApplication is available on **PyPI (Python Package Index)** and can be installed using `pip`. To do so, use the following command: ```txt -pip install easyapplication +pip install EasyApplication ``` To install a specific version of EasyApplication, e.g., 1.0.3: ```txt -pip install 'easyapplication==1.0.3' +pip install 'EasyApplication==1.0.3' ``` To upgrade to the latest version: ```txt -pip install --upgrade easyapplication +pip install --upgrade EasyApplication ``` To upgrade to the latest version and force reinstallation of all dependencies (useful if files are corrupted): ```txt -pip install --upgrade --force-reinstall easyapplication +pip install --upgrade --force-reinstall EasyApplication ``` To check the installed version: ```txt -pip show easyapplication +pip show EasyApplication ``` ### Installing from GitHub alternative { #from-github data-toc-label="Installing from GitHub" } @@ -202,13 +202,13 @@ To install EasyApplication from the `develop` branch of GitHub, for example: ```txt -pip install git+https://github.com/easyscience/easyapp@develop +pip install git+https://github.com/easyscience/gui-components@develop ``` To include extra dependencies (e.g., dev): ```txt -pip install 'easyapplication[dev] @ git+https://github.com/easyscience/easyapp@develop' +pip install 'EasyApplication[dev] @ git+https://github.com/easyscience/gui-components@develop' ``` ## How to Run Tutorials @@ -239,7 +239,7 @@ once using the command line, as shown below. - Download all the EasyApplication tutorials to the `tutorials/` directory: ```txt - pixi run easyapplication download-all-tutorials + pixi run EasyApplication download-all-tutorials ``` - Start JupyterLab in the `tutorials/` directory to access the notebooks: @@ -262,7 +262,7 @@ once using the command line, as shown below. - Download all the EasyApplication tutorials to the `tutorials/` directory: ```txt - python -m easyapplication download-all-tutorials + python -m EasyApplication download-all-tutorials ``` - Launch the Jupyter Notebook server (opens browser automatically at `http://localhost:8888/`): diff --git a/docs/docs/introduction/index.md b/docs/docs/introduction/index.md index ef276e64..7395fa02 100644 --- a/docs/docs/introduction/index.md +++ b/docs/docs/introduction/index.md @@ -20,16 +20,16 @@ based on the EasyScience framework. ## License **EasyApplication** library is released under the -[BSD 3-Clause License](https://raw.githubusercontent.com/easyscience/easyapp/master/LICENSE). +[BSD 3-Clause License](https://raw.githubusercontent.com/easyscience/gui-components/master/LICENSE). ## Releases The latest version of the **EasyApplication** library is -[{{ vars.release_version }}](https://github.com/easyscience/easyapp/releases/latest). +[{{ vars.release_version }}](https://github.com/easyscience/gui-components/releases/latest). For a complete list of new features, bug fixes, and improvements, see the -[GitHub Releases page](https://github.com/easyscience/easyapp/releases). +[GitHub Releases page](https://github.com/easyscience/gui-components/releases). ## Citation @@ -55,11 +55,11 @@ The project is maintained by the If you would like to report a bug or request a new feature, please use the -[GitHub Issue Tracker](https://github.com/easyscience/easyapp/issues) (A -free GitHub account is required.) +[GitHub Issue Tracker](https://github.com/easyscience/gui-components/issues) +(A free GitHub account is required.) To contribute code, documentation, or tests, please see our -[:material-account-plus: Contributing Guidelines](https://github.com/easyscience/easyapp/blob/master/CONTRIBUTING.md) +[:material-account-plus: Contributing Guidelines](https://github.com/easyscience/gui-components/blob/master/CONTRIBUTING.md) for detailed development instructions. ## Get in Touch diff --git a/docs/docs/tutorials/tutorial.py b/docs/docs/tutorials/tutorial.py index 5d88f6b4..200123db 100644 --- a/docs/docs/tutorials/tutorial.py +++ b/docs/docs/tutorials/tutorial.py @@ -9,7 +9,7 @@ # ## Import Library # %% -import easyapplication +import EasyApplication # %% [markdown] # ## Step 1: Blah Blah Blah @@ -17,4 +17,4 @@ # %% # This is a placeholder for tutorial content. print('This is a dummy tutorial.') -print('Imported library:', easyapplication) +print('Imported library:', EasyApplication) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 06cd27d2..2f581412 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -1,9 +1,9 @@ # Project information site_name: EasyApplication Library -site_url: https://easyscience.github.io/easyapp +site_url: https://easyscience.github.io/gui-components # Repository -repo_url: https://github.com/easyscience/easyapp +repo_url: https://github.com/easyscience/gui-components edit_uri: edit/develop/docs/ # Copyright @@ -65,7 +65,7 @@ extra: link: https://easyscience.github.io/application name: EasyApplication Main Webpage - icon: fontawesome/brands/github # Name as in Font Awesome - link: https://github.com/easyscience/easyapp + link: https://github.com/easyscience/gui-components name: EasyApplication Library Source Code on GitHub # Set custom variables to be used in Markdown and HTML files vars: diff --git a/pixi.lock b/pixi.lock index 9cb6bbc4..e602a80a 100644 --- a/pixi.lock +++ b/pixi.lock @@ -708,7 +708,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/4b/eb/03bfb1299d4c4510329e470f13f9a4ce793df7fcb5a2fd3510f911066f61/virtualenv-21.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl - pypi: ./ - py-311-env: + py-312-env: channels: - url: https://conda.anaconda.org/conda-forge/ indexes: @@ -718,29 +718,31 @@ environments: packages: linux-64: - conda: https://conda.anaconda.org/conda-forge/linux-64/_openmp_mutex-4.5-20_gnu.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.13.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py311h49ec1c0_2.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py312h4c3975b_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/arrow-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/async-lru-2.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.3.0-py311h6b1f9c4_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.3.0-py312h90b7ffd_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-6.3.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.3.0-hbca2aae_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py311h66f275b_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312hdb49522_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/bzip2-1.0.8-hda65f42_9.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/c-ares-1.34.6-hb03c661_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.4.22-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.4.22-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py311h03d9500_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.20-py311hc665b79_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.13-py312hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.20-py312h8285ef7_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda @@ -800,10 +802,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/libuv-1.51.0-hb03c661_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libxcrypt-4.4.36-hd590300_1.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/libzlib-1.3.2-h25fd6f3_2.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py311h3778330_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mistune-3.2.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/msgspec-0.21.1-py311h49ec1c0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/msgspec-0.21.1-py312h4c3975b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.17.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda @@ -821,20 +823,21 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.25.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py311haee01d2_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h5253ce2_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.13-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-3.2.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py311h3778330_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py311h57d2397_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hda471dd_2.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/readline-8.3-h853b02a_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.33.1-pyhcf101f3_1.conda @@ -842,7 +845,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.30.0-py311h902ca64_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.30.0-py312h868fb18_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-2.1.0-pyha191276_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda @@ -853,7 +856,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/linux-64/tk-8.6.13-noxft_h366c992_103.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.5-py311h49ec1c0_0.conda + - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.5-py312h4c3975b_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda @@ -871,14 +874,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/linux-64/zstd-1.5.7-hb78ec9c_6.conda - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl + - pypi: https://files.pythonhosted.org/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/88/6764e7a109dd84294850741501145da90d13cdeac9d4e614929464a37420/build-1.4.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b1/10/31932775c94a86814f76b41c4a772b52abfb0e6125324f32c6da1196c297/chardet-7.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/e5/59/a32a241d861cf180853a11c8e5a67641cb1b2af13c3a5ccce83ec07e2c9f/chardet-7.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - pypi: https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/75/3f/aa2458b3b88e59b0be1a06685f237c944375186f4652eb9b5d43bb5ebe21/copier-9.14.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/92/be/b1afb692be85b947f3401375851484496134c5554e67e822c35f28bf2fbc/coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/8c/49/cd14b789536ac6a4778c453c6a2338bc0a2fb60c5a5a41b4008328b9acc1/coverage-7.13.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/85/d7/9b6ac05350ab7f7d3a730ff143ff3e2cada54514117c37be37e26dc91242/docstripy-0.7.2-py3-none-any.whl @@ -928,7 +931,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/0a/44/93f489d16fb63fbd41c670441536541f6e8cfa1e5a69f40bc9c5d30d8c90/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl + - pypi: https://files.pythonhosted.org/packages/5e/d5/e3d9717c9eba10855325650afd2a9cba8e607321697f18953af9d562da2f/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl @@ -954,30 +957,32 @@ environments: - pypi: https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl - pypi: ./ osx-arm64: + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.13.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/appnope-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/argon2-cffi-bindings-25.1.0-py311h9408147_2.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/argon2-cffi-bindings-25.1.0-py312h4409184_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/arrow-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/async-lru-2.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.3.0-py311hee243d0_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.3.0-py312h44dc372_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-6.3.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.3.0-hbca2aae_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py311hdc60ec4_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py312h0dfefe5_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/bzip2-1.0.8-hd037594_9.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/c-ares-1.34.6-hc919400_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.4.22-hbd8a1cb_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.4.22-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py311hd10dc20_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py312h1b4d9a2_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.20-py311h8948835_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.13-py312hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.20-py312h6510ced_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda @@ -1029,10 +1034,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libsqlite-3.53.0-h1b79a29_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libuv-1.51.0-h6caf38d_1.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/libzlib-1.3.2-h8088a28_2.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py311hc290fe0_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mistune-3.2.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgspec-0.21.1-py311hc949640_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgspec-0.21.1-py312h2bbb03f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.17.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda @@ -1050,22 +1055,23 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.25.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py311he363849_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py312hb3ab3e3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ptyprocess-0.7.0-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-core-12.1-py311hce6e4fa_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-framework-cocoa-12.1-py311h9049b8e_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-core-12.1-py312h19bbe71_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-framework-cocoa-12.1-py312h1de3e18_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyha55dd90_7.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.15-h8561d8f_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.13-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-3.2.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py311hc290fe0_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py311h745ac33_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h04c11ed_1.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py312h022ad19_2.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/readline-8.3-h46df422_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.33.1-pyhcf101f3_1.conda @@ -1073,7 +1079,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-0.30.0-py311h71babbd_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-0.30.0-py312h6ef9ec0_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-2.1.0-pyh5552912_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda @@ -1084,7 +1090,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tk-8.6.13-h010d191_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.5-py311hc949640_0.conda + - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.5-py312h2bbb03f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda @@ -1102,14 +1108,14 @@ environments: - conda: https://conda.anaconda.org/conda-forge/osx-arm64/zstd-1.5.7-hbf9d68e_6.conda - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl + - pypi: https://files.pythonhosted.org/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/88/6764e7a109dd84294850741501145da90d13cdeac9d4e614929464a37420/build-1.4.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/14/4b/d3c79495dee4831b8bebca2790e72cb90f0c5849c940570a7c7e5b70b952/chardet-7.4.3-cp311-cp311-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/9c/2f/4c5af01fd1a7506a1d5375403d68925eac70289229492db5aa68b58103d8/chardet-7.4.3-cp312-cp312-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/75/3f/aa2458b3b88e59b0be1a06685f237c944375186f4652eb9b5d43bb5ebe21/copier-9.14.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/35/8b/cd129b0ca4afe886a6ce9d183c44d8301acbd4ef248622e7c49a23145605/coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/a6/16/a68a19e5384e93f811dccc51034b1fd0b865841c390e3c931dcc4699e035/coverage-7.13.5-cp312-cp312-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/85/d7/9b6ac05350ab7f7d3a730ff143ff3e2cada54514117c37be37e26dc91242/docstripy-0.7.2-py3-none-any.whl @@ -1159,7 +1165,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/b6/f6/99ae893c89a0b9d3daec9f95487aa676709aa83f67643b3f0abaf4ab628a/pydantic_core-2.46.3-cp311-cp311-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/a1/4f/2fb62c2267cae99b815bbf4a7b9283812c88ca3153ef29f7707200f1d4e5/pydantic_core-2.46.3-cp312-cp312-macosx_11_0_arm64.whl - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl @@ -1182,32 +1188,34 @@ environments: - pypi: https://files.pythonhosted.org/packages/1c/59/964ecb8008722d27d8a835baea81f56a91cea8e097b3be992bc6ccde6367/versioningit-3.3.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/a4/ce/3b6fee91c85626eaf769d617f1be9d2e15c1cca027bbdeb2e0d751469355/verspec-0.1.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/4b/eb/03bfb1299d4c4510329e470f13f9a4ce793df7fcb5a2fd3510f911066f61/virtualenv-21.3.0-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl + - pypi: https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl - pypi: ./ win-64: + - conda: https://conda.anaconda.org/conda-forge/noarch/_python_abi3_support-1.0-hd8ed1ab_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/anyio-4.13.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/argon2-cffi-25.1.0-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/argon2-cffi-bindings-25.1.0-py311h3485c13_2.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/argon2-cffi-bindings-25.1.0-py312he06e257_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/arrow-1.4.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/asttokens-3.0.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/async-lru-2.3.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/attrs-26.1.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/babel-2.18.0-pyhcf101f3_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/backports.zstd-1.3.0-py311h71c1bcc_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/backports.zstd-1.3.0-py312h06d0912_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/beautifulsoup4-4.14.3-pyha770c72_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-6.3.0-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/bleach-with-css-6.3.0-hbca2aae_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py311hc5da9e4_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py312hc6d9e41_1.conda - conda: https://conda.anaconda.org/conda-forge/win-64/bzip2-1.0.8-h0ad9c76_9.conda - conda: https://conda.anaconda.org/conda-forge/noarch/ca-certificates-2026.4.22-h4c7d964_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/cached-property-1.5.2-hd8ed1ab_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/cached_property-1.5.2-pyha770c72_1.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/certifi-2026.4.22-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py311h3485c13_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py312he06e257_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/colorama-0.4.6-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/comm-0.2.3-pyhe01879c_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.20-py311h5dfdfe8_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.13-py312hd8ed1ab_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.20-py312ha1a9051_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/decorator-5.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/defusedxml-0.7.1-pyhd8ed1ab_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda @@ -1249,10 +1257,10 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/libsodium-1.0.21-h6a83c73_3.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libsqlite-3.53.0-hf5d6505_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/libzlib-1.3.2-hfd05255_2.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py311h3f79411_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py312h05f76fc_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/matplotlib-inline-0.2.1-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/mistune-3.2.0-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/msgspec-0.21.1-py311h3485c13_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/msgspec-0.21.1-py312he06e257_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbclient-0.10.4-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbconvert-core-7.17.1-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/nbformat-5.10.4-pyhd8ed1ab_1.conda @@ -1268,28 +1276,29 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/platformdirs-4.9.6-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prometheus_client-0.25.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/prompt-toolkit-3.0.52-pyha770c72_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py311hf893f09_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py312he5662c2_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pure_eval-0.2.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pycparser-2.22-pyh29332c3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pygments-2.20.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/pysocks-1.7.1-pyh09c184e_7.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.15-h0159041_0_cpython.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.13-h0159041_0_cpython.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-dateutil-2.9.0.post0-pyhe01879c_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-fastjsonschema-2.21.2-pyhe01879c_0.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.13-hd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-json-logger-3.2.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/python-tzdata-2026.2-pyhd8ed1ab_0.conda - - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-311-py311hefeebc8_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pywinpty-2.0.15-py311hda3d55a_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py311h3f79411_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/pyzmq-27.1.0-py311hff25285_2.conda + - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-311-py312h829343e_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pywinpty-2.0.15-py312h275cf98_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py312h05f76fc_1.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/pyzmq-27.1.0-py312h343a6d4_2.conda - conda: https://conda.anaconda.org/conda-forge/noarch/referencing-0.37.0-pyhcf101f3_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/requests-2.33.1-pyhcf101f3_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/returns-0.27.0-pyhc364b38_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3339-validator-0.1.4-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3986-validator-0.1.1-pyh9f0ad1d_0.tar.bz2 - conda: https://conda.anaconda.org/conda-forge/noarch/rfc3987-syntax-1.1.0-pyhe01879c_1.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-0.30.0-py311hf51aa87_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-0.30.0-py312hdabe01f_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/send2trash-2.1.0-pyh6dadd2b_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/setuptools-82.0.1-pyh332efcf_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/six-1.17.0-pyhe01879c_1.conda @@ -1300,7 +1309,7 @@ environments: - conda: https://conda.anaconda.org/conda-forge/noarch/tinycss2-1.4.0-pyhd8ed1ab_0.conda - conda: https://conda.anaconda.org/conda-forge/win-64/tk-8.6.13-h6ed50ae_3.conda - conda: https://conda.anaconda.org/conda-forge/noarch/tomli-2.4.1-pyhcf101f3_0.conda - - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.5-py311h3485c13_0.conda + - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.5-py312he06e257_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/traitlets-5.14.3-pyhd8ed1ab_1.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing-extensions-4.15.0-h396c80c_0.conda - conda: https://conda.anaconda.org/conda-forge/noarch/typing_extensions-4.15.0-pyhcf101f3_0.conda @@ -1324,13 +1333,13 @@ environments: - conda: https://conda.anaconda.org/conda-forge/win-64/zstd-1.5.7-h534d264_6.conda - pypi: https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/9e/43/53afb8ba17218f19b77c7834128566c5bbb100a0ad9ba2e8e89d089d7079/autopep8-2.3.2-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl + - pypi: https://files.pythonhosted.org/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl - pypi: https://files.pythonhosted.org/packages/fa/88/6764e7a109dd84294850741501145da90d13cdeac9d4e614929464a37420/build-1.4.4-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/db/3c/33bac158f8ab7f89b2e59426d5fe2e4f63f7ed25df84c036890172b412b5/cfgv-3.5.0-py2.py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/5d/a6/e9b8f8a3e99602792b01fa7d0a731737615ab56d8bfd0b52935a0ef88b85/chardet-7.4.3-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/32/60/fca69c534602a7ced04280c952a246ad1edde2a6ca3a164f65d32ac41fe7/chardet-7.4.3-cp312-cp312-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/ae/44/c1221527f6a71a01ec6fbad7fa78f1d50dfa02217385cf0fa3eec7087d59/click-8.3.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/75/3f/aa2458b3b88e59b0be1a06685f237c944375186f4652eb9b5d43bb5ebe21/copier-9.14.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/af/7f/4cd8a92531253f9d7c1bbecd9fa1b472907fb54446ca768c59b531248dc5/coverage-7.13.5-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/29/3d/821a9a5799fac2556bcf0bd37a70d1d11fa9e49784b6d22e92e8b2f85f18/coverage-7.13.5-cp312-cp312-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/33/6b/e0547afaf41bf2c42e52430072fa5658766e3d65bd4b03a563d1b6336f57/distlib-0.4.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/bf/50/98b146aea0f1cd7531d25f12bea69fa9ce8d1662124f93fb30dc4511b65e/docstring_parser_fork-0.0.14-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/85/d7/9b6ac05350ab7f7d3a730ff143ff3e2cada54514117c37be37e26dc91242/docstripy-0.7.2-py3-none-any.whl @@ -1380,7 +1389,7 @@ environments: - pypi: https://files.pythonhosted.org/packages/f6/f0/10642828a8dfb741e5f3fbaac830550a518a775c7fff6f04a007259b0548/py-1.11.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/d7/27/a58ddaf8c588a3ef080db9d0b7e0b97215cee3a45df74f3a94dbbf5c893a/pycodestyle-2.14.0-py2.py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f3/0a/fd7d723f8f8153418fb40cf9c940e82004fce7e987026b08a68a36dd3fe7/pydantic-2.13.3-py3-none-any.whl - - pypi: https://files.pythonhosted.org/packages/18/9c/f41951b0d858e343f1cf09398b2a7b3014013799744f2c4a8ad6a3eec4f2/pydantic_core-2.46.3-cp311-cp311-win_amd64.whl + - pypi: https://files.pythonhosted.org/packages/57/be/6b5e757b859013ebfbd7adba02f23b428f37c86dcbf78b5bb0b4ffd36e99/pydantic_core-2.46.3-cp312-cp312-win_amd64.whl - pypi: https://files.pythonhosted.org/packages/87/6f/cc2b231dc78d8c3aaa674a676db190b8f8071c50134af8f8cf39b9b8e8df/pydoclint-0.8.3-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl - pypi: https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl @@ -2211,21 +2220,21 @@ packages: - pkg:pypi/argon2-cffi?source=hash-mapping size: 18715 timestamp: 1749017288144 -- conda: https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py311h49ec1c0_2.conda - sha256: b81f852f13a1d148f6ad7e2a29ab375eb1558b73c9bfa38792d98ea7fb414cff - md5: 6e36e9d2b535c3fbe2e093108df26695 +- conda: https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py312h4c3975b_2.conda + sha256: 7988c207b2b766dad5ebabf25a92b8d75cb8faed92f256fd7a4e0875c9ec6d58 + md5: 1567f06d717246abab170736af8bad1b depends: - __glibc >=2.17,<3.0.a0 - cffi >=1.0.1 - libgcc >=14 - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 license: MIT license_family: MIT purls: - pkg:pypi/argon2-cffi-bindings?source=hash-mapping - size: 35831 - timestamp: 1762509453632 + size: 35646 + timestamp: 1762509443854 - conda: https://conda.anaconda.org/conda-forge/linux-64/argon2-cffi-bindings-25.1.0-py314h5bd0f2a_2.conda sha256: 39234a99df3d2e3065383808ed8bfda36760de5ef590c54c3692bb53571ef02b md5: 3cca1b74b2752917b5b65b81f61f0553 @@ -2241,21 +2250,21 @@ packages: - pkg:pypi/argon2-cffi-bindings?source=hash-mapping size: 35598 timestamp: 1762509505285 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/argon2-cffi-bindings-25.1.0-py311h9408147_2.conda - sha256: c15fc1aa6184185f1b31670a16b76d59f484d3efa25b467f28d59088cf0085c0 - md5: 501c2a606787bcd55b9ddee776208333 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/argon2-cffi-bindings-25.1.0-py312h4409184_2.conda + sha256: 24c475f6f7abf03ef3cc2ac572b7a6d713bede00ef984591be92cdc439b09fbc + md5: 0a2a07b42db3f92b8dccf0f60b5ebee8 depends: - __osx >=11.0 - cffi >=1.0.1 - - python >=3.11,<3.12.0a0 - - python >=3.11,<3.12.0a0 *_cpython - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 license: MIT license_family: MIT purls: - pkg:pypi/argon2-cffi-bindings?source=hash-mapping - size: 34403 - timestamp: 1762509963182 + size: 34224 + timestamp: 1762509989973 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/argon2-cffi-bindings-25.1.0-py314h0612a62_2.conda sha256: aab60bbaea5cc49dff37438d1ad469d64025cda2ce58103cf68da61701ed2075 md5: a240a79a49a95b388ef81ccda27a5e51 @@ -2271,13 +2280,13 @@ packages: - pkg:pypi/argon2-cffi-bindings?source=hash-mapping size: 34218 timestamp: 1762509977830 -- conda: https://conda.anaconda.org/conda-forge/win-64/argon2-cffi-bindings-25.1.0-py311h3485c13_2.conda - sha256: 787e9a5f0a5624fee2af4f58c823bd7933b8ca560e794dd9a821e990b31d2d49 - md5: 5fde0926a521a37d454a5e3162cb6e51 +- conda: https://conda.anaconda.org/conda-forge/win-64/argon2-cffi-bindings-25.1.0-py312he06e257_2.conda + sha256: 38c5e43d991b0c43713fa2ceba3063afa4ccad2dd4c8eb720143de54d461a338 + md5: 5dc3781bbc4ddce0bf250a04c1a192c2 depends: - cffi >=1.0.1 - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -2285,8 +2294,8 @@ packages: license_family: MIT purls: - pkg:pypi/argon2-cffi-bindings?source=hash-mapping - size: 38502 - timestamp: 1762509668838 + size: 38535 + timestamp: 1762509763237 - conda: https://conda.anaconda.org/conda-forge/win-64/argon2-cffi-bindings-25.1.0-py314h5a2d7ad_2.conda sha256: a742e7cd0d5534bfff3fd550a0c1e430411fad60a24f88930d261056ab08096f md5: ffa247e46f47e157851dc547f4c513e4 @@ -2377,20 +2386,20 @@ packages: - pkg:pypi/babel?source=compressed-mapping size: 7684321 timestamp: 1772555330347 -- conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.3.0-py311h6b1f9c4_0.conda - sha256: 246e50ec7fc222875c6ecfa3feab77f5661dc43e26397bc01d9e0310e3cd48a0 - md5: adda5ef2a74c9bdb338ff8a51192898a +- conda: https://conda.anaconda.org/conda-forge/linux-64/backports.zstd-1.3.0-py312h90b7ffd_0.conda + sha256: d77a24be15e283d83214121428290dbe55632a6e458378205b39c550afa008cf + md5: 5b8c55fed2e576dde4b0b33693a4fdb1 depends: - python - - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - python_abi 3.11.* *_cp311 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 - zstd >=1.5.7,<1.6.0a0 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=hash-mapping - size: 244920 - timestamp: 1767044984647 + size: 237970 + timestamp: 1767045004512 - conda: https://conda.anaconda.org/conda-forge/noarch/backports.zstd-1.3.0-py314h680f03e_0.conda noarch: generic sha256: c31ab719d256bc6f89926131e88ecd0f0c5d003fe8481852c6424f4ec6c7eb29 @@ -2401,46 +2410,46 @@ packages: purls: [] size: 7514 timestamp: 1767044983590 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.3.0-py311hee243d0_0.conda - sha256: 57bace9e1431c53952af4cc98ee276cd47604ab98686d837b457246aa2a6dd45 - md5: 6838fc10cc74fe2feb818e2add03d328 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/backports.zstd-1.3.0-py312h44dc372_0.conda + sha256: aee745bfca32f7073d3298157bbb2273d6d83383cb266840cf0a7862b3cd8efc + md5: c2d5961bfd98504b930e704426d16572 depends: - python + - python 3.12.* *_cpython - __osx >=11.0 - - python 3.11.* *_cpython - zstd >=1.5.7,<1.6.0a0 - - python_abi 3.11.* *_cp311 + - python_abi 3.12.* *_cp312 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=hash-mapping - size: 246748 - timestamp: 1767045020742 -- conda: https://conda.anaconda.org/conda-forge/win-64/backports.zstd-1.3.0-py311h71c1bcc_0.conda - sha256: 5a30429e009b93c6dffe539cf0e3d220ef8d36ea42d36ca5c26b603cb3319c71 - md5: 49eb28c4f92e8a7440e3da6d8e8b5e58 + size: 241051 + timestamp: 1767045000787 +- conda: https://conda.anaconda.org/conda-forge/win-64/backports.zstd-1.3.0-py312h06d0912_0.conda + sha256: c9c97cd644faa6c4fb38017c5ecfd082f56a3126af5925d246364fa4a22b2a74 + md5: 2db2b356f08f19ce4309a79a9ee6b9d8 depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 + - python_abi 3.12.* *_cp312 - zstd >=1.5.7,<1.6.0a0 - - python_abi 3.11.* *_cp311 license: BSD-3-Clause AND MIT AND EPL-2.0 purls: - pkg:pypi/backports-zstd?source=hash-mapping - size: 243289 - timestamp: 1767045012846 -- pypi: https://files.pythonhosted.org/packages/3e/5c/fb93d3092640a24dfb7bd7727a24016d7c01774ca013e60efd3f683c8002/backrefs-7.0-py314-none-any.whl + size: 236635 + timestamp: 1767045021157 +- pypi: https://files.pythonhosted.org/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl name: backrefs version: '7.0' - sha256: a6448b28180e3ca01134c9cf09dcebafad8531072e09903c5451748a05f24bc9 + sha256: ca42ce6a49ace3d75684dfa9937f3373902a63284ecb385ce36d15e5dcb41c12 requires_dist: - regex ; extra == 'extras' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl +- pypi: https://files.pythonhosted.org/packages/3e/5c/fb93d3092640a24dfb7bd7727a24016d7c01774ca013e60efd3f683c8002/backrefs-7.0-py314-none-any.whl name: backrefs version: '7.0' - sha256: a0fa7360c63509e9e077e174ef4e6d3c21c8db94189b9d957289ae6d794b9475 + sha256: a6448b28180e3ca01134c9cf09dcebafad8531072e09903c5451748a05f24bc9 requires_dist: - regex ; extra == 'extras' requires_python: '>=3.10' @@ -2481,23 +2490,23 @@ packages: purls: [] size: 4409 timestamp: 1770719370682 -- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py311h66f275b_1.conda - sha256: c36eb061d9ead85f97644cfb740d485dba9b8823357f35c17851078e95e975c1 - md5: 86daecb8e4ed1042d5dc6efbe0152590 +- conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py312hdb49522_1.conda + sha256: 49df13a1bb5e388ca0e4e87022260f9501ed4192656d23dc9d9a1b4bf3787918 + md5: 64088dffd7413a2dd557ce837b4cbbdb depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - libstdcxx >=14 - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 constrains: - libbrotlicommon 1.2.0 hb03c661_1 license: MIT license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping - size: 367573 - timestamp: 1764017405384 + size: 368300 + timestamp: 1764017300621 - conda: https://conda.anaconda.org/conda-forge/linux-64/brotli-python-1.2.0-py314h3de4e8d_1.conda sha256: 3ad3500bff54a781c29f16ce1b288b36606e2189d0b0ef2f67036554f47f12b0 md5: 8910d2c46f7e7b519129f486e0fe927a @@ -2515,23 +2524,23 @@ packages: - pkg:pypi/brotli?source=hash-mapping size: 367376 timestamp: 1764017265553 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py311hdc60ec4_1.conda - sha256: 617545ec0e97d35ed2ff7852f2581a20c0dda80b366d0c42a43706687f971ba8 - md5: 150cbf381febcf0a5e470a8d066e1bc0 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py312h0dfefe5_1.conda + sha256: 6178775a86579d5e8eec6a7ab316c24f1355f6c6ccbe84bb341f342f1eda2440 + md5: 311fcf3f6a8c4eb70f912798035edd35 depends: - __osx >=11.0 - libcxx >=19 - - python >=3.11,<3.12.0a0 - - python >=3.11,<3.12.0a0 *_cpython - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 constrains: - libbrotlicommon 1.2.0 hc919400_1 license: MIT license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping - size: 359588 - timestamp: 1764018467340 + size: 359503 + timestamp: 1764018572368 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/brotli-python-1.2.0-py314h3daef5d_1.conda sha256: 5c2e471fd262fcc3c5a9d5ea4dae5917b885e0e9b02763dbd0f0d9635ed4cb99 md5: f9501812fe7c66b6548c7fcaa1c1f252 @@ -2549,12 +2558,12 @@ packages: - pkg:pypi/brotli?source=hash-mapping size: 359854 timestamp: 1764018178608 -- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py311hc5da9e4_1.conda - sha256: 1803c838946d79ef6485ae8c7dafc93e28722c5999b059a34118ef758387a4c9 - md5: b0c459f98ac5ea504a9d9df6242f7ee1 +- conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py312hc6d9e41_1.conda + sha256: 2bb6f384a51929ef2d5d6039fcf6c294874f20aaab2f63ca768cbe462ed4b379 + md5: e8e7a6346a9e50d19b4daf41f367366f depends: - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -2564,8 +2573,8 @@ packages: license_family: MIT purls: - pkg:pypi/brotli?source=hash-mapping - size: 335333 - timestamp: 1764018370925 + size: 335482 + timestamp: 1764018063640 - conda: https://conda.anaconda.org/conda-forge/win-64/brotli-python-1.2.0-py314he701e3d_1.conda sha256: 6854ee7675135c57c73a04849c29cbebc2fb6a3a3bfee1f308e64bf23074719b md5: 1302b74b93c44791403cbeee6a0f62a3 @@ -2754,22 +2763,22 @@ packages: - pkg:pypi/certifi?source=compressed-mapping size: 135656 timestamp: 1776866680878 -- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py311h03d9500_1.conda - sha256: 3ad13377356c86d3a945ae30e9b8c8734300925ef81a3cb0a9db0d755afbe7bb - md5: 3912e4373de46adafd8f1e97e4bd166b +- conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py312h460c074_1.conda + sha256: 7dafe8173d5f94e46cf9cd597cc8ff476a8357fbbd4433a8b5697b2864845d9c + md5: 648ee28dcd4e07a1940a17da62eccd40 depends: - __glibc >=2.17,<3.0.a0 - libffi >=3.5.2,<3.6.0a0 - libgcc >=14 - pycparser - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 license: MIT license_family: MIT purls: - pkg:pypi/cffi?source=hash-mapping - size: 303338 - timestamp: 1761202960110 + size: 295716 + timestamp: 1761202958833 - conda: https://conda.anaconda.org/conda-forge/linux-64/cffi-2.0.0-py314h4a8dc5f_1.conda sha256: c6339858a0aaf5d939e00d345c98b99e4558f285942b27232ac098ad17ac7f8e md5: cf45f4278afd6f4e6d03eda0f435d527 @@ -2786,22 +2795,22 @@ packages: - pkg:pypi/cffi?source=hash-mapping size: 300271 timestamp: 1761203085220 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py311hd10dc20_1.conda - sha256: 1ffde698463d6e7ed571bdb85cb17bfc1d3a107c026d20047995512dcc2749ec - md5: 4d7f6780e36f18e7601811dddf3bbec5 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py312h1b4d9a2_1.conda + sha256: 597e986ac1a1bd1c9b29d6850e1cdea4a075ce8292af55568952ec670e7dd358 + md5: 503ac138ad3cfc09459738c0f5750705 depends: - __osx >=11.0 - libffi >=3.5.2,<3.6.0a0 - pycparser - - python >=3.11,<3.12.0a0 - - python >=3.11,<3.12.0a0 *_cpython - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 license: MIT license_family: MIT purls: - pkg:pypi/cffi?source=hash-mapping - size: 294848 - timestamp: 1761203196617 + size: 288080 + timestamp: 1761203317419 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/cffi-2.0.0-py314h44086f9_1.conda sha256: 5b5ee5de01eb4e4fd2576add5ec9edfc654fbaf9293e7b7ad2f893a67780aa98 md5: 10dd19e4c797b8f8bdb1ec1fbb6821d7 @@ -2818,13 +2827,13 @@ packages: - pkg:pypi/cffi?source=hash-mapping size: 292983 timestamp: 1761203354051 -- conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py311h3485c13_1.conda - sha256: c9caca6098e3d92b1a269159b759d757518f2c477fbbb5949cb9fee28807c1f1 - md5: f02335db0282d5077df5bc84684f7ff9 +- conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py312he06e257_1.conda + sha256: 3e3bdcb85a2e79fe47d9c8ce64903c76f663b39cb63b8e761f6f884e76127f82 + md5: 46f7dccfee37a52a97c0ed6f33fcf0a3 depends: - pycparser - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -2832,8 +2841,8 @@ packages: license_family: MIT purls: - pkg:pypi/cffi?source=hash-mapping - size: 297941 - timestamp: 1761203850323 + size: 291324 + timestamp: 1761203195397 - conda: https://conda.anaconda.org/conda-forge/win-64/cffi-2.0.0-py314h5a2d7ad_1.conda sha256: 924f2f01fa7a62401145ef35ab6fc95f323b7418b2644a87fea0ea68048880ed md5: c360170be1c9183654a240aadbedad94 @@ -2855,25 +2864,20 @@ packages: version: 3.5.0 sha256: a8dc6b26ad22ff227d2634a65cb388215ce6cc96bbcc5cfde7641ae87e8dacc0 requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/14/4b/d3c79495dee4831b8bebca2790e72cb90f0c5849c940570a7c7e5b70b952/chardet-7.4.3-cp311-cp311-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/32/60/fca69c534602a7ced04280c952a246ad1edde2a6ca3a164f65d32ac41fe7/chardet-7.4.3-cp312-cp312-win_amd64.whl name: chardet version: 7.4.3 - sha256: 7005c88da26fd95d8abb8acbe6281d833e9a9181b03cf49b4546c4555389bd97 + sha256: 4b2799bd58e7245cfa8d4ab2e8ad1d76a5c3a5b1f32318eb6acca4c69a3e7101 requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/51/ac/b9d68ebddfe1b02c77af5bf81120e12b036b4432dc6af7a303d90e2bc38b/chardet-7.4.3-cp314-cp314-macosx_11_0_arm64.whl name: chardet version: 7.4.3 sha256: acc46d1b8b7d5783216afe15db56d1c179b9a40e5a1558bc13164c4fd20674c4 requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/5d/a6/e9b8f8a3e99602792b01fa7d0a731737615ab56d8bfd0b52935a0ef88b85/chardet-7.4.3-cp311-cp311-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/9c/2f/4c5af01fd1a7506a1d5375403d68925eac70289229492db5aa68b58103d8/chardet-7.4.3-cp312-cp312-macosx_11_0_arm64.whl name: chardet version: 7.4.3 - sha256: ccc1f83ab4bcfb901cf39e0c4ba6bc6e726fc6264735f10e24ceb5cb47387578 - requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/b1/10/31932775c94a86814f76b41c4a772b52abfb0e6125324f32c6da1196c297/chardet-7.4.3-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl - name: chardet - version: 7.4.3 - sha256: 4c3da294de1a681097848ab58bd3f2771a674f8039d2d87a5538b28856b815e9 + sha256: 29af5999f654e8729d251f1724a62b538b1262d9292cccaefddf8a02aae1ef6a requires_python: '>=3.10' - pypi: https://files.pythonhosted.org/packages/b1/2c/cad8b5e3623a987f3c930b68e2bdd06cfc388cd91cd42ed05f1227701b73/chardet-7.4.3-cp314-cp314-win_amd64.whl name: chardet @@ -2885,6 +2889,11 @@ packages: version: 7.4.3 sha256: 6e3bd9f936e04bae89c254262af08d9e5b98f805175ba1e29d454e6cba3107b7 requires_python: '>=3.10' +- pypi: https://files.pythonhosted.org/packages/e5/59/a32a241d861cf180853a11c8e5a67641cb1b2af13c3a5ccce83ec07e2c9f/chardet-7.4.3-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl + name: chardet + version: 7.4.3 + sha256: 9a4904dd5f071b7a7d7f50b4a67a86db3c902d243bf31708f1d5cde2f68239cb + requires_python: '>=3.10' - conda: https://conda.anaconda.org/conda-forge/noarch/charset-normalizer-3.4.7-pyhd8ed1ab_0.conda sha256: 3f9483d62ce24ecd063f8a5a714448445dc8d9e201147c46699fc0033e824457 md5: a9167b9571f3baa9d448faa2139d1089 @@ -2958,31 +2967,31 @@ packages: requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/35/8b/cd129b0ca4afe886a6ce9d183c44d8301acbd4ef248622e7c49a23145605/coverage-7.13.5-cp311-cp311-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/29/3d/821a9a5799fac2556bcf0bd37a70d1d11fa9e49784b6d22e92e8b2f85f18/coverage-7.13.5-cp312-cp312-win_amd64.whl name: coverage version: 7.13.5 - sha256: 145ede53ccbafb297c1c9287f788d1bc3efd6c900da23bf6931b09eafc931587 + sha256: d2c87e0c473a10bffe991502eac389220533024c8082ec1ce849f4218dded810 requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/8d/b7/4758d4f73fb536347cc5e4ad63662f9d60ba9118cb6785e9616b2ce5d7fa/coverage-7.13.5-cp314-cp314-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/8c/49/cd14b789536ac6a4778c453c6a2338bc0a2fb60c5a5a41b4008328b9acc1/coverage-7.13.5-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl name: coverage version: 7.13.5 - sha256: 2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633 + sha256: 03ccc709a17a1de074fb1d11f217342fb0d2b1582ed544f554fc9fc3f07e95f5 requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/92/be/b1afb692be85b947f3401375851484496134c5554e67e822c35f28bf2fbc/coverage-7.13.5-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/8d/b7/4758d4f73fb536347cc5e4ad63662f9d60ba9118cb6785e9616b2ce5d7fa/coverage-7.13.5-cp314-cp314-win_amd64.whl name: coverage version: 7.13.5 - sha256: ec10e2a42b41c923c2209b846126c6582db5e43a33157e9870ba9fb70dc7854b + sha256: 2aa055ae1857258f9e0045be26a6d62bdb47a72448b62d7b55f4820f361a2633 requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' -- pypi: https://files.pythonhosted.org/packages/af/7f/4cd8a92531253f9d7c1bbecd9fa1b472907fb54446ca768c59b531248dc5/coverage-7.13.5-cp311-cp311-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/a6/16/a68a19e5384e93f811dccc51034b1fd0b865841c390e3c931dcc4699e035/coverage-7.13.5-cp312-cp312-macosx_11_0_arm64.whl name: coverage version: 7.13.5 - sha256: 258354455f4e86e3e9d0d17571d522e13b4e1e19bf0f8596bcf9476d61e7d8a9 + sha256: 0e223ce4b4ed47f065bfb123687686512e37629be25cc63728557ae7db261422 requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' @@ -2993,6 +3002,17 @@ packages: requires_dist: - tomli ; python_full_version <= '3.11' and extra == 'toml' requires_python: '>=3.10' +- conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.12.13-py312hd8ed1ab_0.conda + noarch: generic + sha256: d3e9bbd7340199527f28bbacf947702368f31de60c433a16446767d3c6aaf6fe + md5: f54c1ffb8ecedb85a8b7fcde3a187212 + depends: + - python >=3.12,<3.13.0a0 + - python_abi * *_cp312 + license: Python-2.0 + purls: [] + size: 46463 + timestamp: 1772728929620 - conda: https://conda.anaconda.org/conda-forge/noarch/cpython-3.14.4-py314hd8ed1ab_100.conda noarch: generic sha256: 40dc224f2b718e5f034efd2332bc315a719063235f63673468d26a24770094ee @@ -3004,21 +3024,21 @@ packages: purls: [] size: 49809 timestamp: 1775614256655 -- conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.20-py311hc665b79_0.conda - sha256: e69be2be543c4d4898895d8aebe758bc683c5a1198583ad676f5719782a07131 - md5: 400e4667a12884216df869cad5fb004b +- conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.20-py312h8285ef7_0.conda + sha256: f20121b67149ff80bf951ccae7442756586d8789204cd08ade59397b22bfd098 + md5: ee1b48795ceb07311dd3e665dd4f5f33 depends: - python - libgcc >=14 - libstdcxx >=14 - __glibc >=2.17,<3.0.a0 - - python_abi 3.11.* *_cp311 + - python_abi 3.12.* *_cp312 license: MIT license_family: MIT purls: - pkg:pypi/debugpy?source=hash-mapping - size: 2733654 - timestamp: 1769744984842 + size: 2858582 + timestamp: 1769744978783 - conda: https://conda.anaconda.org/conda-forge/linux-64/debugpy-1.8.20-py314h42812f9_0.conda sha256: d9e89e351d7189c41615cfceca76b3bcacaa9c81d9945ac1caa6fb9e5184f610 md5: 57e6fad901c05754d5256fe3ab9f277b @@ -3034,21 +3054,21 @@ packages: - pkg:pypi/debugpy?source=hash-mapping size: 2886804 timestamp: 1769744977998 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.20-py311h8948835_0.conda - sha256: 093b015e9abf27fb4d3b4f7e52417d35cd69a99fab8b95ec5c6c3983275c46ba - md5: 150c921424bc9f08c0378f8a6ae58d05 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.20-py312h6510ced_0.conda + sha256: f0ca130b5ffd6949673d3c61d7b8562ab76ad8debafb83f8b3443d30c172f5eb + md5: da3b5efcb0caabcede61a6ce4e0a7669 depends: - python - __osx >=11.0 + - python 3.12.* *_cpython - libcxx >=19 - - python 3.11.* *_cpython - - python_abi 3.11.* *_cp311 + - python_abi 3.12.* *_cp312 license: MIT license_family: MIT purls: - pkg:pypi/debugpy?source=hash-mapping - size: 2668163 - timestamp: 1769745020016 + size: 2752978 + timestamp: 1769744996462 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/debugpy-1.8.20-py314he609de1_0.conda sha256: 7736a82ebe75c0f3ea6991298363d1f2edb34291f8616c1d3719862881c3a167 md5: 407c74dc27356ba6bf3a0191070e3ac0 @@ -3064,21 +3084,21 @@ packages: - pkg:pypi/debugpy?source=hash-mapping size: 2778080 timestamp: 1769745040206 -- conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.20-py311h5dfdfe8_0.conda - sha256: 661e5c582b1f853a46a78d4bb6e55f2bfdac66e68d015e111f1580a11c28abbf - md5: 683be2cd10e80a367790b3083ce529b7 +- conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.20-py312ha1a9051_0.conda + sha256: 5a886b1af3c66bf58213c7f3d802ea60fe8218313d9072bc1c9e8f7840548ba0 + md5: 032746a0b0663920f0afb18cec61062b depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - python_abi 3.11.* *_cp311 + - python_abi 3.12.* *_cp312 license: MIT license_family: MIT purls: - pkg:pypi/debugpy?source=hash-mapping - size: 3940002 - timestamp: 1769745017274 + size: 3996113 + timestamp: 1769745013982 - conda: https://conda.anaconda.org/conda-forge/win-64/debugpy-1.8.20-py314hb98de8c_0.conda sha256: ece1d8299ad081edaf1e5279f2a900bdedddb2c795ac029a06401543cd7610ad md5: 48ae8370a4562f7049d587d017792a3a @@ -3148,8 +3168,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easyapplication - version: 0.8.0+devdirty35 - sha256: 3109fcc04be06e53cf1c182ec11f092a566460ecf1f85e2e4d56d1663c38cc2a + version: 0.8.0+devdirty37 + sha256: 07cc2d7d4f893a9ef373e240902c667f8e8199338ae0340222e652dce6d225ab requires_dist: - pooch - build ; extra == 'dev' @@ -3183,7 +3203,7 @@ packages: - spdx-headers ; extra == 'dev' - validate-pyproject[all] ; extra == 'dev' - versioningit ; extra == 'dev' - requires_python: '>=3.11' + requires_python: '>=3.12' - conda: https://conda.anaconda.org/conda-forge/noarch/exceptiongroup-1.3.1-pyhd8ed1ab_0.conda sha256: ee6cf346d017d954255bbcbdb424cddea4d14e4ed7e9813e429db1d795d01144 md5: 8e662bd460bda79b1ea39194e3c4c9ab @@ -4778,22 +4798,22 @@ packages: - pytest-regressions ; extra == 'testing' - requests ; extra == 'testing' requires_python: '>=3.10' -- conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py311h3778330_1.conda - sha256: 710e207b2e91308a34bcfe547c60ad86c1fa294827266ba18548c1fe1a9d8333 - md5: f9efdf9b0f3d0cc309d56af6edf2a6b0 +- conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py312h8a5da7c_1.conda + sha256: 5f3aad1f3a685ed0b591faad335957dbdb1b73abfd6fc731a0d42718e0653b33 + md5: 93a4752d42b12943a355b682ee43285b depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 constrains: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/markupsafe?source=hash-mapping - size: 26756 - timestamp: 1772445078834 + size: 26057 + timestamp: 1772445297924 - conda: https://conda.anaconda.org/conda-forge/linux-64/markupsafe-3.0.3-py314h67df5f8_1.conda sha256: c279be85b59a62d5c52f5dd9a4cd43ebd08933809a8416c22c3131595607d4cf md5: 9a17c4307d23318476d7fbf0fedc0cde @@ -4810,22 +4830,22 @@ packages: - pkg:pypi/markupsafe?source=hash-mapping size: 27424 timestamp: 1772445227915 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py311hc290fe0_1.conda - sha256: d635f2b1d9e19e8e68c5d33150f7e4f62df08ef2ef0e85977f743e81939afc01 - md5: ff068874356bbc7f9bd2d793f809f44b +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py312h04c11ed_1.conda + sha256: 330394fb9140995b29ae215a19fad46fcc6691bdd1b7654513d55a19aaa091c1 + md5: 11d95ab83ef0a82cc2de12c1e0b47fe4 depends: - __osx >=11.0 - - python >=3.11,<3.12.0a0 - - python >=3.11,<3.12.0a0 *_cpython - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 constrains: - jinja2 >=3.0.0 license: BSD-3-Clause license_family: BSD purls: - - pkg:pypi/markupsafe?source=compressed-mapping - size: 26511 - timestamp: 1772445369187 + - pkg:pypi/markupsafe?source=hash-mapping + size: 25564 + timestamp: 1772445846939 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/markupsafe-3.0.3-py314h6e9b3f0_1.conda sha256: 411153d14ee0d98be6e3751cf5cc0502db17bce2deebebb8779e33d29d0e525f md5: d33c0a15882b70255abdd54711b06a45 @@ -4842,12 +4862,12 @@ packages: - pkg:pypi/markupsafe?source=compressed-mapping size: 27256 timestamp: 1772445397216 -- conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py311h3f79411_1.conda - sha256: 3d37fb1900e31131f84549560e7a4bfea5f39aa3ecd73345fef1f33975cf0baa - md5: f55de41c947bdd2ff9bbeffedf8089f7 +- conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py312h05f76fc_1.conda + sha256: b744287a780211ac4595126ef96a44309c791f155d4724021ef99092bae4aace + md5: a73298d225c7852f97403ca105d10a13 depends: - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -4856,9 +4876,9 @@ packages: license: BSD-3-Clause license_family: BSD purls: - - pkg:pypi/markupsafe?source=hash-mapping - size: 29362 - timestamp: 1772445178723 + - pkg:pypi/markupsafe?source=compressed-mapping + size: 28510 + timestamp: 1772445175216 - conda: https://conda.anaconda.org/conda-forge/win-64/markupsafe-3.0.3-py314h2359020_1.conda sha256: 02805a0f3cd168dbf13afc5e4aed75cc00fe538ce143527a6471485b36f5887c md5: 8de7b40f8b30a8fcaa423c2537fe4199 @@ -5082,20 +5102,20 @@ packages: - griffelib>=2.0 - typing-extensions>=4.0 ; python_full_version < '3.11' requires_python: '>=3.10' -- conda: https://conda.anaconda.org/conda-forge/linux-64/msgspec-0.21.1-py311h49ec1c0_0.conda - sha256: 59123ba4af69c1e7ca6b1b8ffcc40f2aff9a635814495bda3f4f555b3b929165 - md5: 0347d2804701019ac005528df9eb502c +- conda: https://conda.anaconda.org/conda-forge/linux-64/msgspec-0.21.1-py312h4c3975b_0.conda + sha256: 25eb262c378a922eeed85c941ab7de2687ea842daed80521b861b7472b5a7f9a + md5: 5e07dc45b4458c19fdc085bd6c1aa51f depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/msgspec?source=hash-mapping - size: 217556 - timestamp: 1776337480302 + size: 218330 + timestamp: 1776337395109 - conda: https://conda.anaconda.org/conda-forge/linux-64/msgspec-0.21.1-py314h5bd0f2a_0.conda sha256: 52565ceea81e801c59dcaeaf5a9c77fba2fade445e67e0864fda50d4b944e15b md5: 4a8ea416a56e58f012e445f7af2bbcc8 @@ -5110,20 +5130,20 @@ packages: - pkg:pypi/msgspec?source=hash-mapping size: 220990 timestamp: 1776337508167 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgspec-0.21.1-py311hc949640_0.conda - sha256: 55ce08c77b6e87fcc5cca61a516e81fdcc1cd0969e0204fce0a32ca84118a148 - md5: 24707aa446fa7babd806397b8c5349a2 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgspec-0.21.1-py312h2bbb03f_0.conda + sha256: 50e284832520f08ef1e37e0ca20459f5df2c048f59dfba1f2e3ee0ccfe7be317 + md5: ae340bdc5bdf5abd3183c5962517cbde depends: - __osx >=11.0 - - python >=3.11,<3.12.0a0 - - python >=3.11,<3.12.0a0 *_cpython - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/msgspec?source=hash-mapping - size: 207945 - timestamp: 1776338655947 + size: 212357 + timestamp: 1776338798628 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/msgspec-0.21.1-py314h6c2aa35_0.conda sha256: 24a9105921e94fa526ffde1e956fa550c48ddb9ce4b0cf19ae22e79ed267261e md5: 26fce586b13842a0f9f9a3aabae3e943 @@ -5138,12 +5158,12 @@ packages: - pkg:pypi/msgspec?source=hash-mapping size: 216965 timestamp: 1776338889692 -- conda: https://conda.anaconda.org/conda-forge/win-64/msgspec-0.21.1-py311h3485c13_0.conda - sha256: d096ad02a47a61aeeade2ad3639cc300adf950790cca097717040d46ca5bff52 - md5: 6e6fd9a9012358254bebee33eb3d7f71 +- conda: https://conda.anaconda.org/conda-forge/win-64/msgspec-0.21.1-py312he06e257_0.conda + sha256: 003de3343b481937b5eb500ecdbfc882e87cea608be3741dc1fb13d22f8ed95e + md5: 1f32f4f6aa595377a7e651e67ba53d30 depends: - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -5151,8 +5171,8 @@ packages: license_family: BSD purls: - pkg:pypi/msgspec?source=hash-mapping - size: 199272 - timestamp: 1776337711230 + size: 199413 + timestamp: 1776337631789 - conda: https://conda.anaconda.org/conda-forge/win-64/msgspec-0.21.1-py314h5a2d7ad_0.conda sha256: 6a076225fa315d29c5d556e3912a6319aea60b4f458c23f23f5ce66495cb9414 md5: a4b20f401c93cf8651093fcc8380e3c9 @@ -5611,20 +5631,20 @@ packages: - pkg:pypi/prompt-toolkit?source=hash-mapping size: 273927 timestamp: 1756321848365 -- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py311haee01d2_0.conda - sha256: 8d9325af538a8f56013e42bbb91a4dc6935aece34476e20bafacf6007b571e86 - md5: 2ed8f6fe8b51d8e19f7621941f7bb95f +- conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py312h5253ce2_0.conda + sha256: d834fd656133c9e4eaf63ffe9a117c7d0917d86d89f7d64073f4e3a0020bd8a7 + md5: dd94c506b119130aef5a9382aed648e7 depends: - python - - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - python_abi 3.11.* *_cp311 + - __glibc >=2.17,<3.0.a0 + - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - - pkg:pypi/psutil?source=hash-mapping - size: 231786 - timestamp: 1769678156460 + - pkg:pypi/psutil?source=compressed-mapping + size: 225545 + timestamp: 1769678155334 - conda: https://conda.anaconda.org/conda-forge/linux-64/psutil-7.2.2-py314h0f05182_0.conda sha256: f15574ed6c8c8ed8c15a0c5a00102b1efe8b867c0bd286b498cd98d95bd69ae5 md5: 4f225a966cfee267a79c5cb6382bd121 @@ -5639,20 +5659,20 @@ packages: - pkg:pypi/psutil?source=hash-mapping size: 231303 timestamp: 1769678156552 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py311he363849_0.conda - sha256: 2b774e8f4ccaac8783d1908f281e4bb20b7036d6708dc913ee7811b070f5b4b5 - md5: 7cff50265141513c960f00ba586780ea +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py312hb3ab3e3_0.conda + sha256: 6d0e21c76436374635c074208cfeee62a94d3c37d0527ad67fd8a7615e546a05 + md5: fd856899666759403b3c16dcba2f56ff depends: - python - - python 3.11.* *_cpython - __osx >=11.0 - - python_abi 3.11.* *_cp311 + - python 3.12.* *_cpython + - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/psutil?source=hash-mapping - size: 245203 - timestamp: 1769678306347 + size: 239031 + timestamp: 1769678393511 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/psutil-7.2.2-py314ha14b1ff_0.conda sha256: e0f31c053eb11803d63860c213b2b1b57db36734f5f84a3833606f7c91fedff9 md5: fc4c7ab223873eee32080d51600ce7e7 @@ -5667,21 +5687,21 @@ packages: - pkg:pypi/psutil?source=hash-mapping size: 245502 timestamp: 1769678303655 -- conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py311hf893f09_0.conda - sha256: 32da17824abadd1f5b46faedfa4964c7b1817b11887c2e8bb4e48628da51b93a - md5: fd968cdacc7967efd0ff5ef1805b812c +- conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py312he5662c2_0.conda + sha256: edffc84c001a05b996b5f8607c8164432754e86ec9224e831cd00ebabdec04e7 + md5: a2724c93b745fc7861948eb8b9f6679a depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - python_abi 3.11.* *_cp311 + - python_abi 3.12.* *_cp312 license: BSD-3-Clause license_family: BSD purls: - pkg:pypi/psutil?source=hash-mapping - size: 249478 - timestamp: 1769678166841 + size: 242769 + timestamp: 1769678170631 - conda: https://conda.anaconda.org/conda-forge/win-64/psutil-7.2.2-py314hc5dbbe4_0.conda sha256: 17c8274ce5a32c9793f73a5a0094bd6188f3a13026a93147655143d4df034214 md5: fd539ac231820f64066839251aa9fa48 @@ -5752,38 +5772,38 @@ packages: - email-validator>=2.0.0 ; extra == 'email' - tzdata ; python_full_version >= '3.9' and sys_platform == 'win32' and extra == 'timezone' requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/0a/44/93f489d16fb63fbd41c670441536541f6e8cfa1e5a69f40bc9c5d30d8c90/pydantic_core-2.46.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/20/eb/59980e5f1ae54a3b86372bd9f0fa373ea2d402e8cdcd3459334430f91e91/pydantic_core-2.46.3-cp314-cp314-win_amd64.whl name: pydantic-core version: 2.46.3 - sha256: cc7e8c32db809aa0f6ea1d6869ebc8518a65d5150fdfad8bcae6a49ae32a22e2 + sha256: 8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e requires_dist: - typing-extensions>=4.14.1 requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/18/9c/f41951b0d858e343f1cf09398b2a7b3014013799744f2c4a8ad6a3eec4f2/pydantic_core-2.46.3-cp311-cp311-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/24/35/e4066358a22e3e99519db370494c7528f5a2aa1367370e80e27e20283543/pydantic_core-2.46.3-cp314-cp314-macosx_11_0_arm64.whl name: pydantic-core version: 2.46.3 - sha256: b5b9c6cf08a8a5e502698f5e153056d12c34b8fb30317e0c5fd06f45162a6346 + sha256: ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 requires_dist: - typing-extensions>=4.14.1 requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/20/eb/59980e5f1ae54a3b86372bd9f0fa373ea2d402e8cdcd3459334430f91e91/pydantic_core-2.46.3-cp314-cp314-win_amd64.whl +- pypi: https://files.pythonhosted.org/packages/57/be/6b5e757b859013ebfbd7adba02f23b428f37c86dcbf78b5bb0b4ffd36e99/pydantic_core-2.46.3-cp312-cp312-win_amd64.whl name: pydantic-core version: 2.46.3 - sha256: 8940562319bc621da30714617e6a7eaa6b98c84e8c685bcdc02d7ed5e7c7c44e + sha256: c3212fda0ee959c1dd04c60b601ec31097aaa893573a3a1abd0a47bcac2968c1 requires_dist: - typing-extensions>=4.14.1 requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/24/35/e4066358a22e3e99519db370494c7528f5a2aa1367370e80e27e20283543/pydantic_core-2.46.3-cp314-cp314-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/5e/d5/e3d9717c9eba10855325650afd2a9cba8e607321697f18953af9d562da2f/pydantic_core-2.46.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl name: pydantic-core version: 2.46.3 - sha256: ced3310e51aa425f7f77da8bbbb5212616655bedbe82c70944320bc1dbe5e018 + sha256: fb528e295ed31570ac3dcc9bfdd6e0150bc11ce6168ac87a8082055cf1a67395 requires_dist: - typing-extensions>=4.14.1 requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/b6/f6/99ae893c89a0b9d3daec9f95487aa676709aa83f67643b3f0abaf4ab628a/pydantic_core-2.46.3-cp311-cp311-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/a1/4f/2fb62c2267cae99b815bbf4a7b9283812c88ca3153ef29f7707200f1d4e5/pydantic_core-2.46.3-cp312-cp312-macosx_11_0_arm64.whl name: pydantic-core version: 2.46.3 - sha256: cca67d52a5c7a16aed2b3999e719c4bcf644074eac304a5d3d62dd70ae7d4b2c + sha256: af8653713055ea18a3abc1537fe2ebc42f5b0bbb768d1eb79fd74eb47c0ac089 requires_dist: - typing-extensions>=4.14.1 requires_python: '>=3.9' @@ -5824,22 +5844,22 @@ packages: - pyyaml - pygments>=2.19.1 ; extra == 'extra' requires_python: '>=3.9' -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-core-12.1-py311hce6e4fa_0.conda - sha256: f6dced0ea4f220abc1278f6217e22ca1c631f6154dd086b0a7a2866589d6b78c - md5: 812e22c5c7859e719ec225ece0c6f2c1 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-core-12.1-py312h19bbe71_0.conda + sha256: b015f430fe9ea2c53e14be13639f1b781f68deaa5ae74cd8c1d07720890cd02a + md5: c65d7abdc9e60fd3af0ed852591adf1b depends: - __osx >=11.0 - libffi >=3.5.2,<3.6.0a0 - - python >=3.11,<3.12.0a0 - - python >=3.11,<3.12.0a0 *_cpython - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 - setuptools license: MIT license_family: MIT purls: - pkg:pypi/pyobjc-core?source=hash-mapping - size: 481434 - timestamp: 1763151508974 + size: 476750 + timestamp: 1763151865523 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-core-12.1-py314h3a4d195_0.conda sha256: df5af268c5a74b7160d772c263ece6f43257faff571783443e34b5f1d5a61cf2 md5: 75a84fc8337557347252cc4fd3ba2a93 @@ -5856,22 +5876,22 @@ packages: - pkg:pypi/pyobjc-core?source=hash-mapping size: 483374 timestamp: 1763151489724 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-framework-cocoa-12.1-py311h9049b8e_0.conda - sha256: 67e209a5e9ebaf08aa36e2a4b9139ff91a7ecee7c17408d0a3a1ea1dc3a67681 - md5: b215a767b5ef6f16347cacebc409e66b +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-framework-cocoa-12.1-py312h1de3e18_0.conda + sha256: 3710f5ae09c2ea77ba4d82cc51e876d9fc009b878b197a40d3c6347c09ae7d7c + md5: f0bae1b67ece138378923e340b940051 depends: - __osx >=11.0 - libffi >=3.5.2,<3.6.0a0 - pyobjc-core 12.1.* - - python >=3.11,<3.12.0a0 - - python >=3.11,<3.12.0a0 *_cpython - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 license: MIT license_family: MIT purls: - pkg:pypi/pyobjc-framework-cocoa?source=hash-mapping - size: 383445 - timestamp: 1763160541362 + size: 377723 + timestamp: 1763160705325 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyobjc-framework-cocoa-12.1-py314h36abed7_0.conda sha256: aa76ee4328d0514d7c1c455dcd2d3b547db1c59797e54ce0a3f27de5b970e508 md5: 4219bb3408016e22316cf8b443b5ef93 @@ -5969,9 +5989,9 @@ packages: - psutil>=3.0 ; extra == 'psutil' - setproctitle ; extra == 'setproctitle' requires_python: '>=3.9' -- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.11.15-hd63d673_0_cpython.conda - sha256: bf6a32c69889d38482436a786bea32276756cedf0e9805cc856ffd088e8d00f0 - md5: a5ebcefec0c12a333bcd6d7bf3bddc1f +- conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.12.13-hd63d673_0_cpython.conda + sha256: a44655c1c3e1d43ed8704890a91e12afd68130414ea2c0872e154e5633a13d7e + md5: 7eccb41177e15cc672e1babe9056018e depends: - __glibc >=2.17,<3.0.a0 - bzip2 >=1.0.8,<2.0a0 @@ -5991,11 +6011,11 @@ packages: - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - - python_abi 3.11.* *_cp311 + - python_abi 3.12.* *_cp312 license: Python-2.0 purls: [] - size: 30949404 - timestamp: 1772730362552 + size: 31608571 + timestamp: 1772730708989 - conda: https://conda.anaconda.org/conda-forge/linux-64/python-3.14.4-habeac84_100_cp314.conda build_number: 100 sha256: dec247c5badc811baa34d6085df9d0465535883cf745e22e8d79092ad54a3a7b @@ -6024,9 +6044,9 @@ packages: size: 36705460 timestamp: 1775614357822 python_site_packages_path: lib/python3.14/site-packages -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.11.15-h8561d8f_0_cpython.conda - sha256: 9a846065863925b2562126a5c6fecd7a972e84aaa4de9e686ad3715ca506acfa - md5: 49c7d96c58b969585cf09fb01d74e08e +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.12.13-h8561d8f_0_cpython.conda + sha256: e658e647a4a15981573d6018928dec2c448b10c77c557c29872043ff23c0eb6a + md5: 8e7608172fa4d1b90de9a745c2fd2b81 depends: - __osx >=11.0 - bzip2 >=1.0.8,<2.0a0 @@ -6041,11 +6061,11 @@ packages: - tk >=8.6.13,<8.7.0a0 - tzdata constrains: - - python_abi 3.11.* *_cp311 + - python_abi 3.12.* *_cp312 license: Python-2.0 purls: [] - size: 14753109 - timestamp: 1772730203101 + size: 12127424 + timestamp: 1772730755512 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/python-3.14.4-h4c637c5_100_cp314.conda build_number: 100 sha256: 27e7d6cbe021f37244b643f06a98e46767255f7c2907108dd3736f042757ddad @@ -6071,9 +6091,9 @@ packages: size: 13533346 timestamp: 1775616188373 python_site_packages_path: lib/python3.14/site-packages -- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.11.15-h0159041_0_cpython.conda - sha256: a1f1031088ce69bc99c82b95980c1f54e16cbd5c21f042e9c1ea25745a8fc813 - md5: d09dbf470b41bca48cbe6a78ba1e009b +- conda: https://conda.anaconda.org/conda-forge/win-64/python-3.12.13-h0159041_0_cpython.conda + sha256: a02b446d8b7b167b61733a3de3be5de1342250403e72a63b18dac89e99e6180e + md5: 2956dff38eb9f8332ad4caeba941cfe7 depends: - bzip2 >=1.0.8,<2.0a0 - libexpat >=2.7.4,<3.0a0 @@ -6088,11 +6108,11 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 constrains: - - python_abi 3.11.* *_cp311 + - python_abi 3.12.* *_cp312 license: Python-2.0 purls: [] - size: 18416208 - timestamp: 1772728847666 + size: 15840187 + timestamp: 1772728877265 - conda: https://conda.anaconda.org/conda-forge/win-64/python-3.14.4-h4b44e0e_100_cp314.conda build_number: 100 sha256: e258d626b0ba778abb319f128de4c1211306fe86fe0803166817b1ce2514c920 @@ -6160,6 +6180,16 @@ packages: - pkg:pypi/fastjsonschema?source=hash-mapping size: 244628 timestamp: 1755304154927 +- conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.12.13-hd8ed1ab_0.conda + sha256: 97327b9509ae3aae28d27217a5d7bd31aff0ab61a02041e9c6f98c11d8a53b29 + md5: 32780d6794b8056b78602103a04e90ef + depends: + - cpython 3.12.13.* + - python_abi * *_cp312 + license: Python-2.0 + purls: [] + size: 46449 + timestamp: 1772728979370 - conda: https://conda.anaconda.org/conda-forge/noarch/python-gil-3.14.4-h4df99d1_100.conda sha256: 36ff7984e4565c85149e64f8206303d412a0652e55cf806dcb856903fa056314 md5: e4e60721757979d01d3964122f674959 @@ -6193,17 +6223,17 @@ packages: - pkg:pypi/tzdata?source=compressed-mapping size: 146639 timestamp: 1777068997932 -- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.11-8_cp311.conda +- conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.12-8_cp312.conda build_number: 8 - sha256: fddf123692aa4b1fc48f0471e346400d9852d96eeed77dbfdd746fa50a8ff894 - md5: 8fcb6b0e2161850556231336dae58358 + sha256: 80677180dd3c22deb7426ca89d6203f1c7f1f256f2d5a94dc210f6e758229809 + md5: c3efd25ac4d74b1584d2f7a57195ddf1 constrains: - - python 3.11.* *_cpython + - python 3.12.* *_cpython license: BSD-3-Clause license_family: BSD purls: [] - size: 7003 - timestamp: 1752805919375 + size: 6958 + timestamp: 1752805918820 - conda: https://conda.anaconda.org/conda-forge/noarch/python_abi-3.14-8_cp314.conda build_number: 8 sha256: ad6d2e9ac39751cc0529dd1566a26751a0bf2542adb0c232533d32e176e21db5 @@ -6215,9 +6245,9 @@ packages: purls: [] size: 6989 timestamp: 1752805904792 -- conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-311-py311hefeebc8_1.conda - sha256: e3ef7e0cc53111ab81b8a9dd3eabc1374d7420d4c9fce3c8631e73310203ad55 - md5: c1cfe9f5d8e278cc4d2d4c7b0126634d +- conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-311-py312h829343e_1.conda + sha256: a7505522048dad63940d06623f07eb357b9b65510a8d23ff32b99add05aac3a1 + md5: 64cbe4ecbebe185a2261d3f298a60cde depends: - python - vc >=14.3,<15 @@ -6226,13 +6256,13 @@ packages: - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - python_abi 3.11.* *_cp311 + - python_abi 3.12.* *_cp312 license: PSF-2.0 license_family: PSF purls: - pkg:pypi/pywin32?source=hash-mapping - size: 6729388 - timestamp: 1756487145061 + size: 6684490 + timestamp: 1756487136116 - conda: https://conda.anaconda.org/conda-forge/win-64/pywin32-311-py314h8f8f202_1.conda sha256: 6918a8067f296f3c65d43e84558170c9e6c3f4dd735cfe041af41a7fdba7b171 md5: 2d7b7ba21e8a8ced0eca553d4d53f773 @@ -6251,12 +6281,12 @@ packages: - pkg:pypi/pywin32?source=hash-mapping size: 6713155 timestamp: 1756487145487 -- conda: https://conda.anaconda.org/conda-forge/win-64/pywinpty-2.0.15-py311hda3d55a_1.conda - sha256: b1f6b3a907e36f7af486faf3892f47fab42993c13c934cc19855bbae227f2b18 - md5: e5dd9afed138ff193d4593f1b15a388b +- conda: https://conda.anaconda.org/conda-forge/win-64/pywinpty-2.0.15-py312h275cf98_1.conda + sha256: 61cc6c2c712ab4d2b8e7a73d884ef8d3262cb80cc93a4aa074e8b08aa7ddd648 + md5: 66255d136bd0daa41713a334db41d9f0 depends: - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 - ucrt >=10.0.20348.0 - vc >=14.2,<15 - vc14_runtime >=14.29.30139 @@ -6265,8 +6295,8 @@ packages: license_family: MIT purls: - pkg:pypi/pywinpty?source=hash-mapping - size: 215911 - timestamp: 1759557817579 + size: 215371 + timestamp: 1759557609855 - conda: https://conda.anaconda.org/conda-forge/win-64/pywinpty-2.0.15-py314h51f0985_1.conda sha256: 048e20641da680aedaab285640a2aca56b7b5baf7a18f8f164f2796e13628c1f md5: dd84e8748bd3c85a5c751b0576488080 @@ -6283,21 +6313,21 @@ packages: - pkg:pypi/pywinpty?source=hash-mapping size: 216325 timestamp: 1759557436167 -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py311h3778330_1.conda - sha256: c9a6cd2c290d7c3d2b30ea34a0ccda30f770e8ddb2937871f2c404faf60d0050 - md5: a24add9a3bababee946f3bc1c829acfe +- conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py312h8a5da7c_1.conda + sha256: cb142bfd92f6e55749365ddc244294fa7b64db6d08c45b018ff1c658907bfcbf + md5: 15878599a87992e44c059731771591cb depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping - size: 206190 - timestamp: 1770223702917 + size: 198293 + timestamp: 1770223620706 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyyaml-6.0.3-py314h67df5f8_1.conda sha256: b318fb070c7a1f89980ef124b80a0b5ccf3928143708a85e0053cde0169c699d md5: 2035f68f96be30dc60a5dfd7452c7941 @@ -6313,21 +6343,21 @@ packages: - pkg:pypi/pyyaml?source=compressed-mapping size: 202391 timestamp: 1770223462836 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py311hc290fe0_1.conda - sha256: 984e73d7957460689e10533059de8adb38a308853d298900a37acc58edd84cec - md5: e4b908da7cd496b3fa6798c0f60a2a19 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py312h04c11ed_1.conda + sha256: 737959262d03c9c305618f2d48c7f1691fb996f14ae420bfd05932635c99f873 + md5: 95a5f0831b5e0b1075bbd80fcffc52ac depends: - __osx >=11.0 - - python >=3.11,<3.12.0a0 - - python >=3.11,<3.12.0a0 *_cpython - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 - yaml >=0.2.5,<0.3.0a0 license: MIT license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping - size: 192948 - timestamp: 1770223655988 + size: 187278 + timestamp: 1770223990452 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyyaml-6.0.3-py314h6e9b3f0_1.conda sha256: 95f385f9606e30137cf0b5295f63855fd22223a4cf024d306cf9098ea1c4a252 md5: dcf51e564317816cb8d546891019b3ab @@ -6343,12 +6373,12 @@ packages: - pkg:pypi/pyyaml?source=hash-mapping size: 189475 timestamp: 1770223788648 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py311h3f79411_1.conda - sha256: 301c3ba100d25cd5ae37895988ee3ab986210d4d972aa58efed948fbe857773d - md5: a0153c033dc55203e11d1cac8f6a9cf2 +- conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py312h05f76fc_1.conda + sha256: 1cab6cbd6042b2a1d8ee4d6b4ec7f36637a41f57d2f5c5cf0c12b7c4ce6a62f6 + md5: 9f6ebef672522cb9d9a6257215ca5743 depends: - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 @@ -6357,8 +6387,8 @@ packages: license_family: MIT purls: - pkg:pypi/pyyaml?source=hash-mapping - size: 187108 - timestamp: 1770223467913 + size: 179738 + timestamp: 1770223468771 - conda: https://conda.anaconda.org/conda-forge/win-64/pyyaml-6.0.3-py314h2359020_1.conda sha256: a2aff34027aa810ff36a190b75002d2ff6f9fbef71ec66e567616ac3a679d997 md5: 0cd9b88826d0f8db142071eb830bce56 @@ -6382,22 +6412,6 @@ packages: requires_dist: - pyyaml requires_python: '>=3.9' -- conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py311h57d2397_2.conda - sha256: 4137226663b353919396f8cf239971cfa54107cd077cf3b65e979ba3e1f8a037 - md5: 759edfe34f07c5c4565b6c5ec0c7fb17 - depends: - - python - - libgcc >=14 - - libstdcxx >=14 - - __glibc >=2.17,<3.0.a0 - - zeromq >=4.3.5,<4.4.0a0 - - python_abi 3.11.* *_cp311 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/pyzmq?source=hash-mapping - size: 383627 - timestamp: 1771716979818 - conda: https://conda.anaconda.org/conda-forge/linux-64/pyzmq-27.1.0-py312hda471dd_2.conda noarch: python sha256: be66c1f85c3b48137200d62c12d918f4f8ad329423daef04fed292818efd3c28 @@ -6416,22 +6430,6 @@ packages: - pkg:pypi/pyzmq?source=hash-mapping size: 211567 timestamp: 1771716961404 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py311h745ac33_2.conda - sha256: c5d65e1192241d764a68eb0e95ef3ec4800a7e8584260aa2bdcfad0a2d769609 - md5: 9648524ed194922a084f0c2c2c2ada6a - depends: - - python - - __osx >=11.0 - - python 3.11.* *_cpython - - libcxx >=19 - - zeromq >=4.3.5,<4.4.0a0 - - python_abi 3.11.* *_cp311 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/pyzmq?source=hash-mapping - size: 359869 - timestamp: 1771717160649 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/pyzmq-27.1.0-py312h022ad19_2.conda noarch: python sha256: 2f31f799a46ed75518fae0be75ecc8a1b84360dbfd55096bc2fe8bd9c797e772 @@ -6449,22 +6447,6 @@ packages: - pkg:pypi/pyzmq?source=hash-mapping size: 191641 timestamp: 1771717073430 -- conda: https://conda.anaconda.org/conda-forge/win-64/pyzmq-27.1.0-py311hff25285_2.conda - sha256: d37e831618e8df0506d32e16820adab550b74b36d039da753f7543bb967b700d - md5: 0ad824e928af5d6200a97649d810ab2b - depends: - - python - - vc >=14.3,<15 - - vc14_runtime >=14.44.35208 - - ucrt >=10.0.20348.0 - - python_abi 3.11.* *_cp311 - - zeromq >=4.3.5,<4.3.6.0a0 - license: BSD-3-Clause - license_family: BSD - purls: - - pkg:pypi/pyzmq?source=hash-mapping - size: 363314 - timestamp: 1771716977369 - conda: https://conda.anaconda.org/conda-forge/win-64/pyzmq-27.1.0-py312h343a6d4_2.conda noarch: python sha256: d84bcc19a945ca03d1fd794be3e9896ab6afc9f691d58d9c2da514abe584d4df @@ -6604,22 +6586,22 @@ packages: - pkg:pypi/rfc3987-syntax?source=hash-mapping size: 22913 timestamp: 1752876729969 -- conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.30.0-py311h902ca64_0.conda - sha256: bf5e6197fb08b8c6e421ca0126e966b7c3ae62b84d7b98523356b4fd5ae6f8ae - md5: 3893f7b40738f9fe87510cb4468cdda5 +- conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.30.0-py312h868fb18_0.conda + sha256: 62f46e85caaba30b459da7dfcf3e5488ca24fd11675c33ce4367163ab191a42c + md5: 3ffc5a3572db8751c2f15bacf6a0e937 depends: - python - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - python_abi 3.11.* *_cp311 + - python_abi 3.12.* *_cp312 constrains: - __glibc >=2.17 license: MIT license_family: MIT purls: - pkg:pypi/rpds-py?source=hash-mapping - size: 383153 - timestamp: 1764543197251 + size: 383750 + timestamp: 1764543174231 - conda: https://conda.anaconda.org/conda-forge/linux-64/rpds-py-0.30.0-py314h2e6c369_0.conda sha256: e53b0cbf3b324eaa03ca1fe1a688fdf4ab42cea9c25270b0a7307d8aaaa4f446 md5: c1c368b5437b0d1a68f372ccf01cb133 @@ -6636,22 +6618,22 @@ packages: - pkg:pypi/rpds-py?source=hash-mapping size: 376121 timestamp: 1764543122774 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-0.30.0-py311h71babbd_0.conda - sha256: 15873755f078583cea046f7ca7fc0d5348d1f29a16a30b73bdb53dd62f2ba379 - md5: 4408829b022e8e0d19365c0c00be00c4 +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-0.30.0-py312h6ef9ec0_0.conda + sha256: ea06f6f66b1bea97244c36fd2788ccd92fd1fb06eae98e469dd95ee80831b057 + md5: a7cfbbdeb93bb9a3f249bc4c3569cd4c depends: - python - - python 3.11.* *_cpython - __osx >=11.0 - - python_abi 3.11.* *_cp311 + - python 3.12.* *_cpython + - python_abi 3.12.* *_cp312 constrains: - __osx >=11.0 license: MIT license_family: MIT purls: - pkg:pypi/rpds-py?source=hash-mapping - size: 357600 - timestamp: 1764543142990 + size: 358853 + timestamp: 1764543161524 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/rpds-py-0.30.0-py314haad56a0_0.conda sha256: e161dd97403b8b8a083d047369a5cf854557dba1204d29e2f0250f5ac4403925 md5: 76a4f88d1b7748c477abf3c341edc64c @@ -6668,21 +6650,21 @@ packages: - pkg:pypi/rpds-py?source=hash-mapping size: 350976 timestamp: 1764543169524 -- conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-0.30.0-py311hf51aa87_0.conda - sha256: 6edeab1412def450e72f0e96a5d8bb31a2a0b4e56624699c916d3bafd4d9b475 - md5: 43ab63451a9df29f2c499da524665de9 +- conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-0.30.0-py312hdabe01f_0.conda + sha256: faad05e6df2fc15e3ae06fdd71a36e17ff25364777aa4c40f2ec588740d64091 + md5: 2c51baeda0a355b0a5e7b6acb28cf02d depends: - python - vc >=14.3,<15 - vc14_runtime >=14.44.35208 - ucrt >=10.0.20348.0 - - python_abi 3.11.* *_cp311 + - python_abi 3.12.* *_cp312 license: MIT license_family: MIT purls: - pkg:pypi/rpds-py?source=hash-mapping - size: 241288 - timestamp: 1764543026991 + size: 243577 + timestamp: 1764543069837 - conda: https://conda.anaconda.org/conda-forge/win-64/rpds-py-0.30.0-py314h9f07db2_0.conda sha256: e4435368c5c25076dc0f5918ba531c5a92caee8e0e2f9912ef6810049cf00db2 md5: e86531e278ad304438e530953cd55d14 @@ -6978,20 +6960,20 @@ packages: - pkg:pypi/tomli?source=hash-mapping size: 21561 timestamp: 1774492402955 -- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.5-py311h49ec1c0_0.conda - sha256: cd8bc08ca661291cfbb05581b79f7e6a6971a072a54a335abcea5460c1230880 - md5: 73b44a114241e564deb5846e7394bf19 +- conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.5-py312h4c3975b_0.conda + sha256: 4629b1c9139858fb08bb357df917ffc12e4d284c57ff389806bb3ae476ef4e0a + md5: 2b37798adbc54fd9e591d24679d2133a depends: - __glibc >=2.17,<3.0.a0 - libgcc >=14 - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache purls: - pkg:pypi/tornado?source=compressed-mapping - size: 876817 - timestamp: 1774358035290 + size: 859665 + timestamp: 1774358032165 - conda: https://conda.anaconda.org/conda-forge/linux-64/tornado-6.5.5-py314h5bd0f2a_0.conda sha256: ed8d06093ff530a2dae9ed1e51eb6f908fbfd171e8b62f4eae782d67b420be5a md5: dc1ff1e915ab35a06b6fa61efae73ab5 @@ -7006,20 +6988,20 @@ packages: - pkg:pypi/tornado?source=hash-mapping size: 912476 timestamp: 1774358032579 -- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.5-py311hc949640_0.conda - sha256: 572923958ec987f3f68e228dfec243c89ab94b50398731215d36e2e040db8703 - md5: 6f12d9ff025ab1875dff67ad779ca29d +- conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.5-py312h2bbb03f_0.conda + sha256: 29edd36311b4a810a9e6208437bdbedb28c9ac15221caf812cb5c5cf48375dca + md5: 02cce5319b0f1317d9642dcb2e475379 depends: - __osx >=11.0 - - python >=3.11,<3.12.0a0 - - python >=3.11,<3.12.0a0 *_cpython - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python >=3.12,<3.13.0a0 *_cpython + - python_abi 3.12.* *_cp312 license: Apache-2.0 license_family: Apache purls: - - pkg:pypi/tornado?source=hash-mapping - size: 875022 - timestamp: 1774358570256 + - pkg:pypi/tornado?source=compressed-mapping + size: 859155 + timestamp: 1774358568476 - conda: https://conda.anaconda.org/conda-forge/osx-arm64/tornado-6.5.5-py314h6c2aa35_0.conda sha256: 4ccc4a20d676c0ba85adee9c99015bec7f5b685df0cf8006e34573f1d6c2ce75 md5: 3f81f8b2fe2c26a82c0abf57ab2b9610 @@ -7034,21 +7016,21 @@ packages: - pkg:pypi/tornado?source=hash-mapping size: 910845 timestamp: 1774358965067 -- conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.5-py311h3485c13_0.conda - sha256: 71f58fe09f7729ad82c8eb942e775ddeffcef454483911d19d1041ac5d81eec3 - md5: b004afcc680af88cb877978e71d42667 +- conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.5-py312he06e257_0.conda + sha256: 1220c986664e9e8662e660dc64dd97ed823926b1ba05175771408cf1d6a46dd2 + md5: c6c66a64da3d2953c83ed2789a7f4bdb depends: - - python >=3.11,<3.12.0a0 - - python_abi 3.11.* *_cp311 + - python >=3.12,<3.13.0a0 + - python_abi 3.12.* *_cp312 - ucrt >=10.0.20348.0 - vc >=14.3,<15 - vc14_runtime >=14.44.35208 license: Apache-2.0 license_family: Apache purls: - - pkg:pypi/tornado?source=hash-mapping - size: 878544 - timestamp: 1774358187588 + - pkg:pypi/tornado?source=compressed-mapping + size: 859726 + timestamp: 1774358173994 - conda: https://conda.anaconda.org/conda-forge/win-64/tornado-6.5.5-py314h5a2d7ad_0.conda sha256: 49d64837dd02475903479ca47b82669bd6c9f7e6afde61860c6f3f2bd57d8a03 md5: 87b1215adf7f0ba1fb9250af9fc668e1 @@ -7286,17 +7268,17 @@ packages: - python-discovery>=1.2.2 - typing-extensions>=4.13.2 ; python_full_version < '3.11' requires_python: '>=3.8' -- pypi: https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl +- pypi: https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl name: watchdog version: 6.0.0 - sha256: 20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2 + sha256: 6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0 requires_dist: - pyyaml>=3.10 ; extra == 'watchmedo' requires_python: '>=3.9' -- pypi: https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl +- pypi: https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl name: watchdog version: 6.0.0 - sha256: afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c + sha256: 20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2 requires_dist: - pyyaml>=3.10 ; extra == 'watchmedo' requires_python: '>=3.9' diff --git a/pixi.toml b/pixi.toml index 909a1a1f..4d194291 100644 --- a/pixi.toml +++ b/pixi.toml @@ -59,12 +59,12 @@ pixi-kernel = '*' # Pixi Jupyter kernel [pypi-dependencies] # == [feature.default.pypi-dependencies] #pip = '*' # Native package installer -easyapplication = { path = '.', editable = true, extras = ['dev'] } +EasyApplication = { path = '.', editable = true, extras = ['dev'] } # Specific features: Set specific Python versions [feature.py-min.dependencies] -python = '3.11.*' +python = '3.12.*' [feature.py-max.dependencies] python = '3.14.*' @@ -76,7 +76,7 @@ python = '3.14.*' # The `default` feature is always included in all environments. # Additional features can be specified per environment. -py-311-env = { features = ['py-min'] } +py-312-env = { features = ['py-min'] } py-314-env = { features = ['py-max'] } # The `default` environment is always created and includes the `default` feature. @@ -152,10 +152,10 @@ raw-metrics-json = 'radon raw -s -j src/' # 📊 Coverage ############# -unit-tests-coverage = 'pixi run unit-tests --cov=src/easyapplication --cov-report=term-missing' -functional-tests-coverage = 'pixi run functional-tests --cov=src/easyapplication --cov-report=term-missing' -integration-tests-coverage = 'pixi run integration-tests --cov=src/easyapplication --cov-report=term-missing' -docstring-coverage = 'interrogate -c pyproject.toml src/easyapplication' +unit-tests-coverage = 'pixi run unit-tests --cov=src/EasyApplication --cov-report=term-missing' +functional-tests-coverage = 'pixi run functional-tests --cov=src/EasyApplication --cov-report=term-missing' +integration-tests-coverage = 'pixi run integration-tests --cov=src/EasyApplication --cov-report=term-missing' +docstring-coverage = 'interrogate -c pyproject.toml src/EasyApplication' cov = { depends-on = [ 'docstring-coverage', @@ -219,10 +219,10 @@ pre-commit-setup = { depends-on = [ # 🐙️ GitHub Tasks ################# -repo-wiki = 'gh api -X PATCH repos/easyscience/easyapp -f has_wiki=false' -repo-discussions = 'gh api -X PATCH repos/easyscience/easyapp -f has_discussions=true' -repo-description = "gh api -X PATCH repos/easyscience/easyapp -f description='Qt/QML components for building graphical applications'" -repo-homepage = "gh api -X PATCH repos/easyscience/easyapp -f homepage='https://easyscience.github.io/easyapp'" +repo-wiki = 'gh api -X PATCH repos/easyscience/gui-components -f has_wiki=false' +repo-discussions = 'gh api -X PATCH repos/easyscience/gui-components -f has_discussions=true' +repo-description = "gh api -X PATCH repos/easyscience/gui-components -f description='Qt/QML components for building graphical applications'" +repo-homepage = "gh api -X PATCH repos/easyscience/gui-components -f homepage='https://easyscience.github.io/gui-components'" repo-config = { depends-on = [ 'repo-wiki', 'repo-discussions', @@ -230,16 +230,16 @@ repo-config = { depends-on = [ 'repo-homepage', ] } -master-protection = 'gh api -X POST repos/easyscience/easyapp/rulesets --input .github/configs/rulesets-master.json' -develop-protection = 'gh api -X POST repos/easyscience/easyapp/rulesets --input .github/configs/rulesets-develop.json' -gh-pages-protection = 'gh api -X POST repos/easyscience/easyapp/rulesets --input .github/configs/rulesets-gh-pages.json' +master-protection = 'gh api -X POST repos/easyscience/gui-components/rulesets --input .github/configs/rulesets-master.json' +develop-protection = 'gh api -X POST repos/easyscience/gui-components/rulesets --input .github/configs/rulesets-develop.json' +gh-pages-protection = 'gh api -X POST repos/easyscience/gui-components/rulesets --input .github/configs/rulesets-gh-pages.json' branch-protection = { depends-on = [ 'master-protection', 'develop-protection', 'gh-pages-protection', ] } -pages-deployment = 'gh api -X POST repos/easyscience/easyapp/pages --input .github/configs/pages-deployment.json' +pages-deployment = 'gh api -X POST repos/easyscience/gui-components/pages --input .github/configs/pages-deployment.json' github-labels = 'python tools/update_github_labels.py' @@ -272,4 +272,4 @@ post-install = { depends-on = [ ########################## # 🔗 Main Package Shortcut ########################## -easyapplication = 'python -m easyapplication' +EasyApplication = 'python -m EasyApplication' diff --git a/pyproject.toml b/pyproject.toml index d12376fe..1c31277d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -17,12 +17,11 @@ classifiers = [ 'Operating System :: OS Independent', 'Programming Language :: Python :: 3 :: Only', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', 'Programming Language :: Python :: 3.13', 'Programming Language :: Python :: 3.14', ] -requires-python = '>=3.11' +requires-python = '>=3.12' dependencies = [ #'easyscience', # The base library of the EasyScience framework 'pooch', # Data downloader @@ -64,10 +63,10 @@ dev = [ ] [project.urls] -Documentation = 'https://easyscience.github.io/easyapp' -'Release Notes' = 'https://github.com/easyscience/easyapp/releases' -'Source Code' = 'https://github.com/easyscience/easyapp' -'Issue Tracker' = 'https://github.com/easyscience/easyapp/issues' +Documentation = 'https://easyscience.github.io/gui-components' +'Release Notes' = 'https://github.com/easyscience/gui-components/releases' +'Source Code' = 'https://github.com/easyscience/gui-components' +'Issue Tracker' = 'https://github.com/easyscience/gui-components/issues' ############################ # Build system configuration From 9274807b08cbc98a51649f83c3c02f1d1e94ea2f Mon Sep 17 00:00:00 2001 From: Andrew Sazonov Date: Wed, 29 Apr 2026 16:53:31 +0200 Subject: [PATCH 7/7] Add dummy notebook --- docs/docs/tutorials/tutorial.ipynb | 63 ++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 docs/docs/tutorials/tutorial.ipynb diff --git a/docs/docs/tutorials/tutorial.ipynb b/docs/docs/tutorials/tutorial.ipynb new file mode 100644 index 00000000..ea07cddd --- /dev/null +++ b/docs/docs/tutorials/tutorial.ipynb @@ -0,0 +1,63 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "0", + "metadata": {}, + "source": [ + "# Dummy Tutorial\n", + "\n", + "This is a dummy tutorial file to ensure that the tutorials section is\n", + "not empty. You can replace this file with actual tutorial content as\n", + "needed." + ] + }, + { + "cell_type": "markdown", + "id": "1", + "metadata": {}, + "source": [ + "## Import Library" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2", + "metadata": {}, + "outputs": [], + "source": [ + "import EasyApplication" + ] + }, + { + "cell_type": "markdown", + "id": "3", + "metadata": {}, + "source": [ + "## Step 1: Blah Blah Blah" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4", + "metadata": {}, + "outputs": [], + "source": [ + "# This is a placeholder for tutorial content.\n", + "print('This is a dummy tutorial.')\n", + "print('Imported library:', EasyApplication)" + ] + } + ], + "metadata": { + "jupytext": { + "cell_metadata_filter": "-all", + "main_language": "python", + "notebook_metadata_filter": "-all" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}