mammoth-cli 2.2.9__tar.gz → 2.2.10__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/PKG-INFO +2 -2
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/__init__.py +1 -1
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities.md +9 -10
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view.md +2 -2
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/dashboard.py +13 -7
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/dataset.py +77 -6
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/file.py +12 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/folder.py +22 -27
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/project.py +30 -4
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/view.py +32 -9
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/view_profile.py +12 -2
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/dryrun_targets.py +46 -5
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/listing.py +1 -1
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/sdk_service.py +5 -1
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/pyproject.toml +2 -2
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/sdk-methods.yaml +12 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/.gitignore +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/LICENSE +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/README.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/__main__.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/app.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/about-mammoth.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities-misc.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/command-index.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/activity.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/addon.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/agent.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/ai.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/annotation.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/auth.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/automation.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/batch.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/billing.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/browse.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/calc.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/capability.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/client-app.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/completion.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/config.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/connector.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/context.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-qa.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-template.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/data-app.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dataset.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/doctor.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/external-key.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/file.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/folder.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/job.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/link.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/log.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/notification.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/parameter.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/project.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/report.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schedule.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schema.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/skill.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/snippet.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/support.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/template.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/trash.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/upgrade.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/user.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/version.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view-transform.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/webhook.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workflow.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workspace.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/handoff.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/input.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/operations.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/auth-scope.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/cleanup.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/dashboards.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/database-import.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/end-to-end.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/exports.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/index.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/need-action.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/resources.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/scheduling.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/transforms.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/typed-etl-discovery.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/report-checklist.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/retention.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/bundled_skill/mammoth-cli/references/task-start.md +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/__init__.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/activity.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/addon.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/agent.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/ai.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/annotation.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/auth.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/automation.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/batch.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/billing.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/browse.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/calc.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/capability.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/client_app.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/completion.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/config.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/connector.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/context.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/data_app.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/doctor.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/external_key.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/job.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/link.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/notification.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/parameter.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/registry.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/report.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/schedule.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/schema.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/skill.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/snippet.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/support.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/template.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/trash.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/upgrade.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/user.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/view_ops.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/webhook.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/workflow.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/commands/workspace.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/context/__init__.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/context/credentials.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/context/endpoint.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/context/profiles.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/context/resolver.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/contracts/__init__.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/contracts/auth.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/embed.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/errors/__init__.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/errors/envelope.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/manifest/__init__.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/manifest/loader.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/messages/__init__.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/output/__init__.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/output/envelope.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/output/normalize.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/output/policy.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/output/progress.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/output/render.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/py.typed +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/__init__.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/confirm.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/dataset_health.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/dryrun.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/embedded.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/executor.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/input_loader.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/intent_only.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/invocation.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/new_data.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/options.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/parents.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/runlog.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/session.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/state.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/strict.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/updates.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/validate.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/runtime/verify.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/__init__.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/argspec.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/board_values.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/coerce.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/command_contract.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/conditions.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/dashboard_filters.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/dashboard_pages.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/dashboard_review.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/data_profile.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/data_quality.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/dispatch.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/factory.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/input_fields.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/mapping.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/openapi_types.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/positionals.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/protocol.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/read_queries.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/stored_stats.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/testing.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/text_dates.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/type_system.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/services/write_impact.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/skills/__init__.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/skills/installer.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/skills/steering.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/mammoth_cli/testing.py +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/activity.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/addon.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/agent.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/ai.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/annotation.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/auth.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/automation.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/batch.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/billing.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/browse.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/calc.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/capability.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/client-app.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/completion.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/config.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/connector.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/context.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/dashboard.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/data-app.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/dataset.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/doctor.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/external-key.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/file.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/folder.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/job.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/link.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/log.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/notification.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/parameter.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/project.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/report.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/schedule.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/schema.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/skill.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/snippet.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/support.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/template.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/trash.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/upgrade.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/user.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/version.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/view.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/webhook.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/workflow.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/commands/workspace.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/edits-target.source.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/openapi-operations.yaml +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/schema-v1.json +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/manifests/sdk-catalog.source.yaml +62 -62
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/openapi/metadata.json +0 -0
- {mammoth_cli-2.2.9 → mammoth_cli-2.2.10}/spec/openapi/openapi.json +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mammoth-cli
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.10
|
|
4
4
|
Summary: Command-line interface for the Mammoth Analytics platform
|
|
5
5
|
Project-URL: homepage, https://mammoth.io
|
|
6
6
|
Project-URL: repository, https://github.com/EdgeMetric/mammothsdk
|
|
@@ -17,7 +17,7 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
|
17
17
|
Requires-Python: <3.15,>=3.12
|
|
18
18
|
Requires-Dist: jsonschema>=4.20
|
|
19
19
|
Requires-Dist: keyring<26,>=25.7
|
|
20
|
-
Requires-Dist: mammoth-io<0.9,>=0.8.
|
|
20
|
+
Requires-Dist: mammoth-io<0.9,>=0.8.11
|
|
21
21
|
Requires-Dist: platformdirs<5,>=4.10
|
|
22
22
|
Requires-Dist: pydantic<3,>=2.12.5
|
|
23
23
|
Requires-Dist: pyyaml<7,>=6
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# What is proven on release
|
|
2
2
|
|
|
3
3
|
Generated from `docs/release-capability-matrix.json`; do not edit by hand.
|
|
4
|
-
The CLI publishes 633 commands.
|
|
4
|
+
The CLI publishes 633 commands. 504 of them bind one of the 545 API operations in the matrix; the remainder are local commands (`schema`, `auth`, `doctor`, `log`, ...) or typed variants that share an operation (`view transform *` commands submit through `view.task.add`, except `rename-columns` and `sort`, which set view display properties through `view.update`). 262 bound commands ran once successfully on release, 1 are not supported there, and the rest are untried. Untried is not broken: discover the contract with `mammoth schema get COMMAND_ID`, run it, and treat the structured error envelope as the answer. Use an untried command normally: confirm a write with the user first, check its result with a read afterwards, and never refuse a command only because it is untried.
|
|
5
5
|
|
|
6
6
|
Status meanings:
|
|
7
7
|
|
|
@@ -17,20 +17,21 @@ The typed `view transform *` commands all submit through `view.task.add` (`renam
|
|
|
17
17
|
|
|
18
18
|
| Family | Commands | Ran once | Not supported | Untried |
|
|
19
19
|
|---|---|---|---|---|
|
|
20
|
-
| `dashboard` |
|
|
20
|
+
| `dashboard` | 122 | 81 | 1 | 40 |
|
|
21
|
+
| `support` | 66 | 9 | 0 | 57 |
|
|
21
22
|
| `view` | 62 | 58 | 0 | 4 |
|
|
22
|
-
| `
|
|
23
|
+
| `billing` | 24 | 4 | 0 | 20 |
|
|
23
24
|
| `connector` | 22 | 3 | 0 | 19 |
|
|
24
|
-
| `
|
|
25
|
-
| `
|
|
26
|
-
| `dataset` | 17 | 12 | 0 | 5 |
|
|
25
|
+
| `workspace` | 20 | 7 | 0 | 13 |
|
|
26
|
+
| `dataset` | 18 | 12 | 0 | 6 |
|
|
27
27
|
| `project` | 17 | 15 | 0 | 2 |
|
|
28
28
|
| `workflow` | 16 | 9 | 0 | 7 |
|
|
29
29
|
| `parameter` | 14 | 7 | 0 | 7 |
|
|
30
30
|
| `data-app` | 12 | 1 | 0 | 11 |
|
|
31
|
+
| `automation` | 8 | 3 | 0 | 5 |
|
|
32
|
+
| `browse` | 8 | 1 | 0 | 7 |
|
|
31
33
|
| `folder` | 8 | 8 | 0 | 0 |
|
|
32
34
|
| `snippet` | 8 | 6 | 0 | 2 |
|
|
33
|
-
| `automation` | 7 | 3 | 0 | 4 |
|
|
34
35
|
| `user` | 7 | 3 | 0 | 4 |
|
|
35
36
|
| `webhook` | 7 | 5 | 0 | 2 |
|
|
36
37
|
| `addon` | 6 | 0 | 0 | 6 |
|
|
@@ -43,7 +44,6 @@ The typed `view transform *` commands all submit through `view.task.add` (`renam
|
|
|
43
44
|
| `notification` | 5 | 1 | 0 | 4 |
|
|
44
45
|
| `schedule` | 5 | 0 | 0 | 5 |
|
|
45
46
|
| `template` | 5 | 5 | 0 | 0 |
|
|
46
|
-
| `browse` | 4 | 1 | 0 | 3 |
|
|
47
47
|
| `external-key` | 4 | 1 | 0 | 3 |
|
|
48
48
|
| `trash` | 3 | 3 | 0 | 0 |
|
|
49
49
|
| `activity` | 2 | 1 | 0 | 1 |
|
|
@@ -58,7 +58,6 @@ Ran once: `dashboard.action`, `dashboard.analytics`, `dashboard.archive`, `dashb
|
|
|
58
58
|
|
|
59
59
|
| Command | State | Note |
|
|
60
60
|
|---|---|---|
|
|
61
|
-
| `dashboard.create` | not supported | Not supported on release: backend returns HTTP 409 4DASH012 DASHBOARD_LEGACY_CREATION_RETIRED (legacy dashboard creation retired). Use dashboard.create-blank or dashboard.v3.generate. Dashboard sweep 2026-09-18, CLI 2.0.12. |
|
|
62
61
|
| `dashboard.pdf.export` | not supported | Not supported from the CLI on release: backend HTTP 400 4GENR001 requires params.data to be the browser-hydrated DashboardData map and states it cannot rebuild it from stored descriptors (dashboard sweep and Haiku e2e, 2026-09-18). The route works only with a client render pass. |
|
|
63
62
|
| `dashboard.data.draft` | observed blocker | blocked_missing_fixture: Dashboard 56 (create-blank/v3 engine) has no widgets in its canvas and getDraftData rejects with backend_code 4DASH004 DASHBOARD_WRONG_ENGINE: "This dashbo |
|
|
64
63
|
| `dashboard.data.published` | observed blocker | blocked_missing_fixture: Same as dashboard.data.draft: backend_code 4DASH004 DASHBOARD_WRONG_ENGINE on /dashboards/56/getPublishData |
|
|
@@ -110,7 +109,7 @@ Ran once: `project.bulk-delete`, `project.bulk-update`, `project.checkpoint.list
|
|
|
110
109
|
|
|
111
110
|
## `view`
|
|
112
111
|
|
|
113
|
-
Ran once: `view.active-user.list`, `view.active-user.mark`, `view.ai.generate-data`, `view.ai.profile`, `view.bulk-delete`, `view.checkpoint.create`, `view.checkpoint.delete`, `view.checkpoint.get`, `view.checkpoint.list`, `view.checkpoint.update`, `view.conditional-format.create`, `view.conditional-format.delete-all`, `view.conditional-format.list`, `view.conditional-format.update`, `view.create`, `view.data-check.create`, `view.data-check.delete`, `view.data-check.get`, `view.data-check.list`, `view.data.get`, `view.data.query`, `view.delete`, `view.derivative.create`, `view.derivative.data`, `view.derivative.delete`, `view.derivative.list`, `view.derivative.update`, `view.draft.command`, `view.export.create`, `view.export.delete`, `view.export.get`, `view.export.list`, `view.export.publish-db`, `view.export.update`, `view.exportable-config.apply`, `view.exportable-config.get`, `view.get`, `view.list`, `view.parameter-context`, `view.pipeline.edit`, `view.pipeline.get`, `view.pipeline.items`, `view.pipeline.rerun`, `view.preview`, `view.restore`, `view.task.add`, `view.task.delete`, `view.task.get`, `view.task.list`, `view.task.preview`, `view.task.update`, `view.trash`, `view.version.apply`, `view.version.delete`, `view.version.get`, `view.version.list`, `view.version.update`
|
|
112
|
+
Ran once: `view.active-user.list`, `view.active-user.mark`, `view.ai.generate-data`, `view.ai.profile`, `view.bulk-delete`, `view.checkpoint.create`, `view.checkpoint.delete`, `view.checkpoint.get`, `view.checkpoint.list`, `view.checkpoint.update`, `view.conditional-format.create`, `view.conditional-format.delete-all`, `view.conditional-format.list`, `view.conditional-format.update`, `view.create`, `view.data-check.create`, `view.data-check.delete`, `view.data-check.get`, `view.data-check.list`, `view.data.aggregate`, `view.data.get`, `view.data.query`, `view.delete`, `view.derivative.create`, `view.derivative.data`, `view.derivative.delete`, `view.derivative.list`, `view.derivative.update`, `view.draft.command`, `view.export.create`, `view.export.delete`, `view.export.get`, `view.export.list`, `view.export.publish-db`, `view.export.update`, `view.exportable-config.apply`, `view.exportable-config.get`, `view.get`, `view.list`, `view.parameter-context`, `view.pipeline.edit`, `view.pipeline.get`, `view.pipeline.items`, `view.pipeline.rerun`, `view.preview`, `view.restore`, `view.task.add`, `view.task.delete`, `view.task.get`, `view.task.list`, `view.task.preview`, `view.task.update`, `view.trash`, `view.version.apply`, `view.version.delete`, `view.version.get`, `view.version.list`, `view.version.update`
|
|
114
113
|
|
|
115
114
|
| Command | State | Note |
|
|
116
115
|
|---|---|---|
|
|
@@ -220,7 +220,7 @@ Example: `mammoth view data aggregate 123 --input '{"group_by": ["Channel"], "ag
|
|
|
220
220
|
|
|
221
221
|
Result: `ViewDataAggregateResult`; mutation `read`, confirmation `none`, wait policy `always_wait`.
|
|
222
222
|
|
|
223
|
-
Status on release:
|
|
223
|
+
Status on release: ran once on CLI 2.0.18 — golden-data check 2026-09-19 (CLI 2.0.18): condition on the pivoted view compiled to the backend clause shape and answered (exit 0); EQ with a column select and an AND of CONTAINS/NE also verified read-only on a pre-existing view. Before 2.0.18 the spec was f…
|
|
224
224
|
|
|
225
225
|
### `view.data.compare`
|
|
226
226
|
|
|
@@ -270,7 +270,7 @@ Example: `mammoth view data query 123 123`. Placeholders are illustrative; resol
|
|
|
270
270
|
|
|
271
271
|
Result: `ViewDataQueryResult`; mutation `read`, confirmation `none`, wait policy `always_wait`.
|
|
272
272
|
|
|
273
|
-
Status on release: ran once on CLI 2.0.
|
|
273
|
+
Status on release: ran once on CLI 2.0.28 — ILG simulation 2026-09-19: exit 0 on release with CLI 2.0.28. Paged reads (limit up to 500) used for every value check; envelope data.data rows keyed by display name. Single invocation only.
|
|
274
274
|
|
|
275
275
|
### `view.delete`
|
|
276
276
|
|
|
@@ -396,6 +396,10 @@ def dashboard_get(invocation: Invocation) -> HandlerResult:
|
|
|
396
396
|
return data, _meta(invocation, auth.workspace_id)
|
|
397
397
|
|
|
398
398
|
|
|
399
|
+
#: Sources whose owning dataset one ``dashboard get`` looks up (one request each).
|
|
400
|
+
_MAX_SOURCE_LOOKUPS = 20
|
|
401
|
+
|
|
402
|
+
|
|
399
403
|
def _source_dataset(service: Any, invocation: Invocation, workspace_id: Any, view_id: int) -> Any:
|
|
400
404
|
"""The dataset that owns ``view_id``: remembered, else looked up; never raises."""
|
|
401
405
|
profile_name = invocation.profile or profiles.get_selected()
|
|
@@ -422,16 +426,18 @@ def _with_source_datasets(
|
|
|
422
426
|
sources = data.get("sources") if isinstance(data, dict) else None
|
|
423
427
|
if not isinstance(sources, list) or not sources:
|
|
424
428
|
return data
|
|
429
|
+
view_ids = [view_id for view_id in sources if isinstance(view_id, int)]
|
|
425
430
|
views = [
|
|
426
431
|
_source_dataset(service, invocation, workspace_id, view_id)
|
|
427
|
-
for view_id in
|
|
428
|
-
if isinstance(view_id, int)
|
|
432
|
+
for view_id in view_ids[:_MAX_SOURCE_LOOKUPS]
|
|
429
433
|
]
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
434
|
+
note = "sources are view ids; use a source's dataset_id for dataset commands."
|
|
435
|
+
if len(view_ids) > _MAX_SOURCE_LOOKUPS:
|
|
436
|
+
note += (
|
|
437
|
+
f" Only the first {_MAX_SOURCE_LOOKUPS} of {len(view_ids)} sources were labelled;"
|
|
438
|
+
" read the others with 'view get VIEW_ID'."
|
|
439
|
+
)
|
|
440
|
+
return {**data, "source_views": views, "sources_note": note}
|
|
435
441
|
|
|
436
442
|
|
|
437
443
|
def _with_board_project(data: Any) -> Any:
|
|
@@ -196,6 +196,63 @@ def _find_in_projects(
|
|
|
196
196
|
return matches
|
|
197
197
|
|
|
198
198
|
|
|
199
|
+
#: Pages of resource search read for one name (100 hits each); past it the result says so.
|
|
200
|
+
_SEARCH_PAGES = 5
|
|
201
|
+
_SEARCH_PAGE_SIZE = 100
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def search_hits(
|
|
205
|
+
service: Any, resource_type: str, needle: str, project_id: int | None = None
|
|
206
|
+
) -> tuple[list[dict[str, Any]], bool]:
|
|
207
|
+
"""Resources of ``resource_type`` whose name contains ``needle``, from the search route.
|
|
208
|
+
|
|
209
|
+
One request (one more per 100 hits) replaces a listing per project: the
|
|
210
|
+
workspace search, or the project's own resource list when ``project_id`` is
|
|
211
|
+
given. Returns the minimal rows and whether the read stopped at
|
|
212
|
+
``_SEARCH_PAGES`` pages with more hits unread.
|
|
213
|
+
"""
|
|
214
|
+
symbol = (
|
|
215
|
+
"mammoth.api.browse.BrowseAPI.resources_search"
|
|
216
|
+
if project_id is None
|
|
217
|
+
else "mammoth.api.browse.BrowseAPI.resources_list"
|
|
218
|
+
)
|
|
219
|
+
scope: dict[str, Any] = {} if project_id is None else {"project_id": project_id}
|
|
220
|
+
rows: list[dict[str, Any]] = []
|
|
221
|
+
cursor: str | None = None
|
|
222
|
+
for _ in range(_SEARCH_PAGES):
|
|
223
|
+
page = service.call(
|
|
224
|
+
symbol,
|
|
225
|
+
search=needle,
|
|
226
|
+
resource_type=resource_type,
|
|
227
|
+
limit=_SEARCH_PAGE_SIZE,
|
|
228
|
+
cursor=cursor,
|
|
229
|
+
fields="minimal",
|
|
230
|
+
**scope,
|
|
231
|
+
)
|
|
232
|
+
rows += [r for r in page.get("resources", []) if isinstance(r, dict)]
|
|
233
|
+
cursor = page.get("next_cursor")
|
|
234
|
+
if not (page.get("has_more") and cursor):
|
|
235
|
+
return rows, False
|
|
236
|
+
return rows, True
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def search_cut_note(kind: str) -> str:
|
|
240
|
+
"""Said when a name search stopped early, with how to narrow it."""
|
|
241
|
+
return (
|
|
242
|
+
f"The name matched more than {_SEARCH_PAGES * _SEARCH_PAGE_SIZE} {kind}, so only the "
|
|
243
|
+
"first of them were read. Use a longer name, or --project, to narrow it."
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _projects_holding(
|
|
248
|
+
service: Any, needle: str, visible: list[dict[str, Any]]
|
|
249
|
+
) -> tuple[list[dict[str, Any]], bool]:
|
|
250
|
+
"""The visible projects that hold a dataset whose name contains ``needle``."""
|
|
251
|
+
rows, cut = search_hits(service, "datasource", needle)
|
|
252
|
+
holding = {r.get("project_id") for r in rows}
|
|
253
|
+
return [p for p in visible if p.get("id") in holding], cut
|
|
254
|
+
|
|
255
|
+
|
|
199
256
|
def _other_projects(visible: list[dict[str, Any]], project_id: int) -> list[dict[str, Any]]:
|
|
200
257
|
"""Every visible project except ``project_id``."""
|
|
201
258
|
return [p for p in visible if p.get("id") != project_id]
|
|
@@ -232,16 +289,20 @@ def dataset_find(invocation: Invocation) -> HandlerResult:
|
|
|
232
289
|
skipped: list[dict[str, Any]] = []
|
|
233
290
|
with open_service(invocation) as (service, auth):
|
|
234
291
|
visible = _visible_projects(service)
|
|
292
|
+
cut = False
|
|
235
293
|
if invocation.project is not None:
|
|
236
294
|
projects: list[dict[str, Any]] = [named_project(service, invocation.project, visible)]
|
|
237
295
|
matches = _find_in_projects(service, needle, projects, skipped)
|
|
238
296
|
if not matches:
|
|
239
|
-
|
|
240
|
-
matches = _find_in_projects(
|
|
241
|
-
|
|
297
|
+
holding, cut = _projects_holding(service, needle, visible)
|
|
298
|
+
matches = _find_in_projects(
|
|
299
|
+
service, needle, _other_projects(holding, invocation.project), skipped
|
|
300
|
+
)
|
|
301
|
+
projects += _other_projects(visible, invocation.project)
|
|
242
302
|
else:
|
|
243
303
|
projects = visible
|
|
244
|
-
|
|
304
|
+
holding, cut = _projects_holding(service, needle, visible)
|
|
305
|
+
matches = _find_in_projects(service, needle, holding, skipped)
|
|
245
306
|
meta = {
|
|
246
307
|
"profile": invocation.profile,
|
|
247
308
|
"workspace_id": auth.workspace_id,
|
|
@@ -253,7 +314,11 @@ def dataset_find(invocation: Invocation) -> HandlerResult:
|
|
|
253
314
|
}
|
|
254
315
|
if skipped:
|
|
255
316
|
result["projects_skipped"] = skipped
|
|
256
|
-
|
|
317
|
+
notes = [ambiguity_note(len(matches), name_substring)]
|
|
318
|
+
if cut:
|
|
319
|
+
result["truncated"] = True
|
|
320
|
+
notes.append(search_cut_note("datasets"))
|
|
321
|
+
if note := " ".join(n for n in notes if n):
|
|
257
322
|
result["note"] = note
|
|
258
323
|
return result, meta
|
|
259
324
|
|
|
@@ -304,8 +369,9 @@ def _dataset_name_search(
|
|
|
304
369
|
records = data.get("datasets", []) if isinstance(data, dict) else []
|
|
305
370
|
page = search_page(records, name, int(document.get("offset", 0)), document.get("limit"))
|
|
306
371
|
if page["matched"] == 0:
|
|
372
|
+
holding, cut = _projects_holding(service, name.lower(), _visible_projects(service))
|
|
307
373
|
elsewhere = _find_in_projects(
|
|
308
|
-
service, name.lower(), _other_projects(
|
|
374
|
+
service, name.lower(), _other_projects(holding, project_id)
|
|
309
375
|
)
|
|
310
376
|
if elsewhere:
|
|
311
377
|
page["in_other_projects"] = elsewhere
|
|
@@ -313,6 +379,11 @@ def _dataset_name_search(
|
|
|
313
379
|
f"No dataset in project {project_id} matches; the ones listed in "
|
|
314
380
|
"in_other_projects do. Run the next call with --project <its project_id>."
|
|
315
381
|
)
|
|
382
|
+
if cut:
|
|
383
|
+
page["truncated"] = True
|
|
384
|
+
page["note"] = " ".join(
|
|
385
|
+
filter(None, (page.get("note"), search_cut_note("datasets")))
|
|
386
|
+
)
|
|
316
387
|
page["name"] = name
|
|
317
388
|
return page, _meta(invocation, auth.workspace_id, project_id)
|
|
318
389
|
|
|
@@ -237,9 +237,17 @@ def file_upload(invocation: Invocation) -> HandlerResult:
|
|
|
237
237
|
# the status the platform holds for each dataset, not an assumed one.
|
|
238
238
|
result = _upload_result(data, lambda dataset_id: _dataset_status(service, dataset_id))
|
|
239
239
|
project_id = resolved_project(invocation)
|
|
240
|
+
previewed = 0
|
|
240
241
|
for entry in result["datasets"]:
|
|
241
242
|
if entry.get("status") != "ready":
|
|
242
243
|
continue
|
|
244
|
+
if previewed >= _UPLOAD_PREVIEW_LIMIT:
|
|
245
|
+
entry["preview_skipped"] = (
|
|
246
|
+
f"Only the first {_UPLOAD_PREVIEW_LIMIT} datasets are previewed; "
|
|
247
|
+
f"run 'view list {entry['id']}' to see this one."
|
|
248
|
+
)
|
|
249
|
+
continue
|
|
250
|
+
previewed += 1
|
|
243
251
|
preview = upload_preview(service, int(entry["id"]), project_id)
|
|
244
252
|
if "preview_error" in preview:
|
|
245
253
|
entry["preview_error"] = preview["preview_error"]
|
|
@@ -253,6 +261,10 @@ def file_upload(invocation: Invocation) -> HandlerResult:
|
|
|
253
261
|
return result, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
254
262
|
|
|
255
263
|
|
|
264
|
+
#: Uploaded datasets one ``file upload`` previews (several requests each).
|
|
265
|
+
_UPLOAD_PREVIEW_LIMIT = 5
|
|
266
|
+
|
|
267
|
+
|
|
256
268
|
def _require_local_files(files: Any) -> None:
|
|
257
269
|
"""Fail as a usage error, naming the path, when a local file is missing.
|
|
258
270
|
|
|
@@ -11,7 +11,7 @@ from __future__ import annotations
|
|
|
11
11
|
|
|
12
12
|
from typing import Any
|
|
13
13
|
|
|
14
|
-
from mammoth_cli.commands.dataset import named_project
|
|
14
|
+
from mammoth_cli.commands.dataset import named_project, search_cut_note, search_hits
|
|
15
15
|
from mammoth_cli.errors.envelope import (
|
|
16
16
|
CODE_INVALID_ARGUMENT,
|
|
17
17
|
CODE_MISSING_ARGUMENT,
|
|
@@ -107,45 +107,40 @@ def _require_string_positional(invocation: Invocation, name: str) -> str:
|
|
|
107
107
|
def folder_find(invocation: Invocation) -> HandlerResult:
|
|
108
108
|
"""Search folder names for a substring across every visible project.
|
|
109
109
|
|
|
110
|
-
Read-only local composite:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
project.
|
|
110
|
+
Read-only local composite: one resource-search request (the workspace's, or
|
|
111
|
+
the project's with ``--project``) returns every folder whose name contains
|
|
112
|
+
the substring, case-insensitively, and the projects the credential can see
|
|
113
|
+
name them. Does not require an active project.
|
|
114
114
|
"""
|
|
115
115
|
name_substring = _require_string_positional(invocation, "name substring")
|
|
116
|
-
needle = name_substring.lower()
|
|
117
|
-
matches: list[dict[str, Any]] = []
|
|
118
116
|
with open_service(invocation) as (service, auth):
|
|
119
117
|
visible = list(service.list_all_projects())
|
|
120
118
|
if invocation.project is not None:
|
|
121
119
|
projects: list[dict[str, Any]] = [named_project(service, invocation.project, visible)]
|
|
122
120
|
else:
|
|
123
121
|
projects = visible
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
"id": folder.get("id"),
|
|
137
|
-
"name": name,
|
|
138
|
-
}
|
|
139
|
-
)
|
|
122
|
+
rows, cut = search_hits(service, "label", name_substring, invocation.project)
|
|
123
|
+
names = {p.get("id"): p.get("name") for p in projects}
|
|
124
|
+
matches = [
|
|
125
|
+
{
|
|
126
|
+
"project_id": row["project_id"],
|
|
127
|
+
"project_name": names[row["project_id"]],
|
|
128
|
+
"id": row.get("object_id"),
|
|
129
|
+
"name": row.get("name"),
|
|
130
|
+
}
|
|
131
|
+
for row in rows
|
|
132
|
+
if row.get("project_id") in names
|
|
133
|
+
]
|
|
140
134
|
meta = {
|
|
141
135
|
"profile": invocation.profile,
|
|
142
136
|
"workspace_id": auth.workspace_id,
|
|
143
137
|
"project_id": invocation.project,
|
|
144
138
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
"
|
|
148
|
-
|
|
139
|
+
result: dict[str, Any] = {"matches": matches, "projects_searched": len(projects)}
|
|
140
|
+
if cut:
|
|
141
|
+
result["truncated"] = True
|
|
142
|
+
result["note"] = search_cut_note("folders")
|
|
143
|
+
return result, meta
|
|
149
144
|
|
|
150
145
|
|
|
151
146
|
def folder_list(invocation: Invocation) -> HandlerResult:
|
|
@@ -599,11 +599,19 @@ def _datasets_to_check(service: Any, project_id: int, scoped: int | None) -> lis
|
|
|
599
599
|
"""Every dataset of the project, or only the named one -- never a walk when scoped."""
|
|
600
600
|
if scoped is not None:
|
|
601
601
|
return [{"id": scoped, "name": f"dataset {scoped}"}]
|
|
602
|
-
listing = service.call(_DATASETS_LIST_SYMBOL, project_id=project_id)
|
|
602
|
+
listing = service.call(_DATASETS_LIST_SYMBOL, project_id=project_id, sort="(created_at:desc)")
|
|
603
603
|
return listing.get("datasets", []) if isinstance(listing, dict) else []
|
|
604
604
|
|
|
605
605
|
|
|
606
|
-
_CHECK_WORKERS =
|
|
606
|
+
_CHECK_WORKERS = 2
|
|
607
|
+
#: Most recent datasets and dashboards one ``project check`` reads (several requests each).
|
|
608
|
+
_CHECK_MAX_DATASETS = 20
|
|
609
|
+
_CHECK_MAX_DASHBOARDS = 10
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
def _not_checked_line(kind: str, omitted: int, how: str) -> str:
|
|
613
|
+
"""The report line for resources left unchecked by the cap, and how to reach them."""
|
|
614
|
+
return f"{omitted} more {kind} were not checked (only the most recent were); {how}"
|
|
607
615
|
|
|
608
616
|
|
|
609
617
|
def _in_worker[T](invocation: Invocation, work: Callable[[Any, Any], T]) -> T:
|
|
@@ -751,7 +759,16 @@ def project_check(invocation: Invocation) -> HandlerResult:
|
|
|
751
759
|
statuses: list[dict[str, Any]] = []
|
|
752
760
|
scoped_dataset = _scoped_dataset_id(invocation)
|
|
753
761
|
with open_service(invocation) as (service, auth):
|
|
754
|
-
|
|
762
|
+
every_dataset = _datasets_to_check(service, project_id, scoped_dataset)
|
|
763
|
+
datasets = every_dataset[:_CHECK_MAX_DATASETS]
|
|
764
|
+
if len(every_dataset) > len(datasets):
|
|
765
|
+
to_report.append(
|
|
766
|
+
_not_checked_line(
|
|
767
|
+
"datasets",
|
|
768
|
+
len(every_dataset) - len(datasets),
|
|
769
|
+
"run 'project check DATASET_ID' for a specific one.",
|
|
770
|
+
)
|
|
771
|
+
)
|
|
755
772
|
previews = _map_checks(
|
|
756
773
|
invocation,
|
|
757
774
|
service,
|
|
@@ -787,11 +804,20 @@ def project_check(invocation: Invocation) -> HandlerResult:
|
|
|
787
804
|
boards = (
|
|
788
805
|
[] if scoped_dataset else service.call(_DASHBOARDS_LIST_SYMBOL, project_id=project_id)
|
|
789
806
|
)
|
|
807
|
+
every_board = [b for b in boards if isinstance(b, dict)] if isinstance(boards, list) else []
|
|
808
|
+
if len(every_board) > _CHECK_MAX_DASHBOARDS:
|
|
809
|
+
to_report.append(
|
|
810
|
+
_not_checked_line(
|
|
811
|
+
"dashboards",
|
|
812
|
+
len(every_board) - _CHECK_MAX_DASHBOARDS,
|
|
813
|
+
"check one with 'dashboard get DASHBOARD_ID'.",
|
|
814
|
+
)
|
|
815
|
+
)
|
|
790
816
|
results = _map_checks(
|
|
791
817
|
invocation,
|
|
792
818
|
service,
|
|
793
819
|
auth,
|
|
794
|
-
[
|
|
820
|
+
every_board[:_CHECK_MAX_DASHBOARDS],
|
|
795
821
|
_check_dashboard(invocation),
|
|
796
822
|
)
|
|
797
823
|
for result in filter(None, results):
|
|
@@ -416,9 +416,14 @@ def brief_view_record(record: Any) -> Any:
|
|
|
416
416
|
_DATASETS_LIST_ALL_SYMBOL = "mammoth.api.datasets.DatasetsAPI.list_all"
|
|
417
417
|
#: Soft floor for the no-DATASET_ID ``view list`` walk: whole per-dataset
|
|
418
418
|
#: pages are pulled and appended until at least this many views have been
|
|
419
|
-
#: collected (or
|
|
420
|
-
#:
|
|
421
|
-
|
|
419
|
+
#: collected (or the visit cap below is reached), so one call never returns a
|
|
420
|
+
#: single dataset's worth from a large project. Sized to what the agent output
|
|
421
|
+
#: cap shows (about 15 summaries): reading more only to cut them is waste.
|
|
422
|
+
_VIEW_LIST_ALL_DATASETS_MIN_VIEWS = 20
|
|
423
|
+
#: Most datasets one no-DATASET_ID ``view list`` call reads views of, whether or
|
|
424
|
+
#: not they hold any, so a project of many empty datasets costs a fixed number of
|
|
425
|
+
#: requests. The result carries ``next_dataset_offset`` to resume from.
|
|
426
|
+
_VIEW_LIST_ALL_DATASETS_MAX_VISITS = 25
|
|
422
427
|
|
|
423
428
|
|
|
424
429
|
def view_list(invocation: Invocation) -> HandlerResult:
|
|
@@ -521,6 +526,11 @@ def _compact_view_list(
|
|
|
521
526
|
stop = [d.get("id") for d in datasets].index(dropped)
|
|
522
527
|
result["next_dataset_offset"] = stop
|
|
523
528
|
result["datasets_visited"] = stop - start
|
|
529
|
+
if "next_dataset_offset" in result and not summary.get("views_omitted"):
|
|
530
|
+
result["more"] = (
|
|
531
|
+
f"Datasets from position {result['next_dataset_offset']} on were not read; "
|
|
532
|
+
"pass dataset_offset to continue, or list one dataset with 'view list DATASET_ID'."
|
|
533
|
+
)
|
|
524
534
|
if summary.get("views_omitted"):
|
|
525
535
|
result["more"] = (
|
|
526
536
|
"Some views were cut to fit the output cap; list one dataset with "
|
|
@@ -540,9 +550,10 @@ def _view_list_across_project(
|
|
|
540
550
|
|
|
541
551
|
Starts at ``dataset_offset`` (default 0) into the project's dataset
|
|
542
552
|
list, pulling whole per-dataset view pages until either every dataset
|
|
543
|
-
has been visited
|
|
544
|
-
have been collected
|
|
545
|
-
|
|
553
|
+
has been visited, at least ``_VIEW_LIST_ALL_DATASETS_MIN_VIEWS`` views
|
|
554
|
+
have been collected, or ``_VIEW_LIST_ALL_DATASETS_MAX_VISITS`` datasets were
|
|
555
|
+
read. ``next_dataset_offset`` names where to resume when the project holds
|
|
556
|
+
more datasets than were visited. Returns the result and
|
|
546
557
|
the project's dataset records (with the summary fields when ``compact``).
|
|
547
558
|
"""
|
|
548
559
|
dataset_offset = int(document.get("dataset_offset", 0))
|
|
@@ -568,7 +579,10 @@ def _view_list_across_project(
|
|
|
568
579
|
if isinstance(item, dict):
|
|
569
580
|
item = {**item, "dataset_id": dataset_id}
|
|
570
581
|
dataviews.append(item)
|
|
571
|
-
if
|
|
582
|
+
if (
|
|
583
|
+
len(dataviews) >= _VIEW_LIST_ALL_DATASETS_MIN_VIEWS
|
|
584
|
+
or visited - dataset_offset >= _VIEW_LIST_ALL_DATASETS_MAX_VISITS
|
|
585
|
+
):
|
|
572
586
|
break
|
|
573
587
|
result: dict[str, Any] = {"dataviews": dataviews, "datasets_visited": visited - dataset_offset}
|
|
574
588
|
if visited < len(datasets):
|
|
@@ -2786,11 +2800,20 @@ def view_pipeline_rerun(invocation: Invocation) -> HandlerResult:
|
|
|
2786
2800
|
return data, _meta(invocation, auth.workspace_id, None)
|
|
2787
2801
|
|
|
2788
2802
|
|
|
2803
|
+
#: Seconds ``view pipeline wait`` waits when the caller names no ``timeout`` (the SDK's own
|
|
2804
|
+
#: default is an hour, one request every few seconds for as long as it runs).
|
|
2805
|
+
_PIPELINE_WAIT_TIMEOUT = 300.0
|
|
2806
|
+
|
|
2807
|
+
|
|
2789
2808
|
def view_pipeline_wait(invocation: Invocation) -> HandlerResult:
|
|
2790
|
-
"""Wait for a dataview's pipeline to finish running.
|
|
2809
|
+
"""Wait for a dataview's pipeline to finish running.
|
|
2810
|
+
|
|
2811
|
+
Waits up to ``_PIPELINE_WAIT_TIMEOUT`` seconds unless ``timeout`` says more,
|
|
2812
|
+
polling less often the longer it waits.
|
|
2813
|
+
"""
|
|
2791
2814
|
dataview_id = _require_int_positional_at(invocation, 0, "dataview id")
|
|
2792
2815
|
document = invocation.load_input() or {}
|
|
2793
|
-
kwargs: dict[str, Any] = {"dataview_id": dataview_id}
|
|
2816
|
+
kwargs: dict[str, Any] = {"dataview_id": dataview_id, "timeout": _PIPELINE_WAIT_TIMEOUT}
|
|
2794
2817
|
_forward_optional(document, kwargs, ("dataset_id", "timeout", "poll_interval"))
|
|
2795
2818
|
with open_service(invocation) as (service, auth):
|
|
2796
2819
|
data = service.call(_symbol(invocation), **kwargs)
|
|
@@ -39,7 +39,10 @@ HandlerResult = tuple[Any, dict[str, Any]]
|
|
|
39
39
|
_AGGREGATE = "mammoth.api.dataviews.DataviewsAPI.aggregate"
|
|
40
40
|
_STORED_STATS = "mammoth.api.ai.AIAPI.generate_profile"
|
|
41
41
|
_TOTALS_BATCH = 40
|
|
42
|
-
|
|
42
|
+
#: Concurrent aggregate jobs; each is one request plus its polls on the host's own server.
|
|
43
|
+
_WORKERS = 2
|
|
44
|
+
#: Columns profiled when the caller names none (the rest are reported as not profiled).
|
|
45
|
+
_DEFAULT_PROFILE_COLUMNS = 20
|
|
43
46
|
_DEFAULT_TOP = 5
|
|
44
47
|
_DEFAULT_DETAIL_LIMIT = 50
|
|
45
48
|
_ASSOCIATION_LIMIT = 20
|
|
@@ -94,7 +97,8 @@ def view_data_profile(invocation: Invocation) -> HandlerResult:
|
|
|
94
97
|
|
|
95
98
|
def build_profile(scope: _Scope, document: dict[str, Any], workers: int) -> dict[str, Any]:
|
|
96
99
|
"""Read the stored stats, query the rest, and assemble the result."""
|
|
97
|
-
|
|
100
|
+
every_name = _selected_columns(scope, document)
|
|
101
|
+
names = every_name if "columns" in document else every_name[:_DEFAULT_PROFILE_COLUMNS]
|
|
98
102
|
target = document.get("target")
|
|
99
103
|
if target is not None and target not in scope.types:
|
|
100
104
|
raise _usage(f"target column {target!r} is not in the view", sorted(scope.types))
|
|
@@ -119,6 +123,12 @@ def build_profile(scope: _Scope, document: dict[str, Any], workers: int) -> dict
|
|
|
119
123
|
"spelling_variants_checked": variants_checked,
|
|
120
124
|
"spelling_variants_skipped": variants_skipped,
|
|
121
125
|
}
|
|
126
|
+
if len(names) < len(every_name):
|
|
127
|
+
result["columns_not_profiled"] = every_name[len(names) :]
|
|
128
|
+
result["columns_not_profiled_note"] = (
|
|
129
|
+
f"Only the first {len(names)} of {len(every_name)} columns were profiled; "
|
|
130
|
+
"pass input 'columns' (names) to profile others."
|
|
131
|
+
)
|
|
122
132
|
if target:
|
|
123
133
|
result["target"] = _target_report(
|
|
124
134
|
scope, str(target), names, facts, tables, live_rows, workers
|
|
@@ -417,6 +417,34 @@ def _read_name(
|
|
|
417
417
|
return {"type": kind, "id": target_id, "name": name}
|
|
418
418
|
|
|
419
419
|
|
|
420
|
+
#: Resource kinds one bulk request can name, and the resource type the route calls each.
|
|
421
|
+
BULK_TYPES: dict[str, str] = {"dataset": "datasource", "view": "dataview", "folder": "label"}
|
|
422
|
+
_BULK_SYMBOL = "mammoth.api.browse.BrowseAPI.resources_bulk"
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def _read_names(
|
|
426
|
+
service: MammothService, kind: str, ids: list[int], arguments: Mapping[str, Any]
|
|
427
|
+
) -> list[dict[str, Any]]:
|
|
428
|
+
"""Name every id of ``kind``: one bulk request per 100 ids where the route knows the kind.
|
|
429
|
+
|
|
430
|
+
An id the bulk read does not return (missing, or not readable by the
|
|
431
|
+
caller) is read on its own, so the error it raises is the resource's own.
|
|
432
|
+
"""
|
|
433
|
+
if kind not in BULK_TYPES or len(ids) < 2:
|
|
434
|
+
return [_read_name(service, kind, item, arguments) for item in ids]
|
|
435
|
+
scope = {"project_id": arguments["project_id"]} if arguments.get("project_id") else {}
|
|
436
|
+
rows = service.call(_BULK_SYMBOL, items=[(BULK_TYPES[kind], item) for item in ids], **scope)
|
|
437
|
+
names = {row.get("object_id"): row.get("name") for row in rows if isinstance(row, Mapping)}
|
|
438
|
+
return [
|
|
439
|
+
(
|
|
440
|
+
{"type": kind, "id": item, "name": names[item]}
|
|
441
|
+
if isinstance(names.get(item), str) and names[item]
|
|
442
|
+
else _read_name(service, kind, item, arguments)
|
|
443
|
+
)
|
|
444
|
+
for item in ids
|
|
445
|
+
]
|
|
446
|
+
|
|
447
|
+
|
|
420
448
|
def _path(record: Any, dotted: str) -> object:
|
|
421
449
|
"""Follow ``a.b`` through nested mappings (or attributes)."""
|
|
422
450
|
for part in dotted.split("."):
|
|
@@ -468,15 +496,26 @@ def _unresolved_name(kind: str, target_id: object, symbol: str, key: str) -> Cli
|
|
|
468
496
|
)
|
|
469
497
|
|
|
470
498
|
|
|
471
|
-
def _sub_record(
|
|
472
|
-
|
|
499
|
+
def _sub_record(
|
|
500
|
+
service: MammothService,
|
|
501
|
+
sub: Sub,
|
|
502
|
+
target: object,
|
|
503
|
+
args: Mapping[str, Any],
|
|
504
|
+
lists: dict[str, Any],
|
|
505
|
+
) -> Any:
|
|
506
|
+
"""Read the sub-resource (or find it in its list); None when nothing is declared.
|
|
507
|
+
|
|
508
|
+
A list read serves every target of the call, so it is made once (``lists``).
|
|
509
|
+
"""
|
|
473
510
|
if sub.source is None:
|
|
474
511
|
return None
|
|
475
512
|
symbol = _symbol_of(sub.source)
|
|
476
513
|
kwargs = {key: args[key] for key in sub.forward if args.get(key) is not None}
|
|
477
514
|
if sub.container is None:
|
|
478
515
|
return service.call(symbol, **{sub.ids_arg: target}, **kwargs)
|
|
479
|
-
|
|
516
|
+
if symbol not in lists:
|
|
517
|
+
lists[symbol] = service.call(symbol, **kwargs)
|
|
518
|
+
listed = lists[symbol]
|
|
480
519
|
items = listed if isinstance(listed, list) else (listed or {}).get(sub.container, [])
|
|
481
520
|
found = next((i for i in items if str(_path(i, sub.id_field)) == str(target)), None)
|
|
482
521
|
if found is None:
|
|
@@ -499,8 +538,10 @@ def _sub_targets(
|
|
|
499
538
|
raise unresolvable_error(command_id, f"the {sub.parent} id is unknown")
|
|
500
539
|
parent = f"\u201c{_parent_name(service, sub.parent, parent_ids[0], args)}\u201d"
|
|
501
540
|
targets = []
|
|
541
|
+
lists: dict[str, Any] = {}
|
|
502
542
|
for target in ids:
|
|
503
|
-
|
|
543
|
+
record = _sub_record(service, sub, target, args, lists) if target != "all" else None
|
|
544
|
+
label = _label(record, sub) if target != "all" else None
|
|
504
545
|
shown = f"\u201c{label}\u201d" if label else str(target)
|
|
505
546
|
layout = sub.name_fmt or ("{parent} \u203a {kind} {label}" if parent else "{kind} {label}")
|
|
506
547
|
name = layout.format(**{**args, "parent": parent, "kind": sub.kind, "label": shown})
|
|
@@ -595,7 +636,7 @@ def resolve_targets(
|
|
|
595
636
|
parent = {**arguments}
|
|
596
637
|
if "dataset_id" not in parent and would_call.get("dataset_id") is not None:
|
|
597
638
|
parent["dataset_id"] = would_call["dataset_id"]
|
|
598
|
-
return
|
|
639
|
+
return _read_names(service, kind, ids, parent) + invites
|
|
599
640
|
|
|
600
641
|
|
|
601
642
|
#: Deletes whose dry run also lists what depends on the resource: command ->
|