mammoth-cli 2.2.29__tar.gz → 2.2.31__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.29 → mammoth_cli-2.2.31}/PKG-INFO +1 -1
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/__init__.py +1 -1
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/about-mammoth.md +1 -1
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/batch.md +2 -2
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/exports.md +11 -1
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/automation.py +46 -1
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/batch.py +159 -6
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/project.py +37 -6
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/view.py +232 -50
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/view_ops.py +53 -1
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/errors/envelope.py +4 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/command_contract.py +9 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/input_fields.py +4 -11
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/pyproject.toml +1 -1
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/automation.yaml +3 -1
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/batch.yaml +4 -5
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/project.yaml +1 -1
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/view.yaml +18 -13
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/.gitignore +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/LICENSE +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/README.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/__main__.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/app.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities-misc.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/command-index.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/activity.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/addon.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/agent.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/ai.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/annotation.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/auth.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/automation.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/billing.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/browse.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/calc.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/capability.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/client-app.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/completion.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/config.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/connector.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/context.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-qa.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-style.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-template.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/data-app.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dataset.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/doctor.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/external-key.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/file.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/folder.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/job.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/link.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/log.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/notification.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/parameter.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/project.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/report.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schedule.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schema.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/skill.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/snippet.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/support.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/template.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/trash.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/upgrade.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/user.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/version.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view-transform.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/webhook.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workflow.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workspace.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/handoff.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/input.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/operations.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/auth-scope.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/cleanup.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/dashboards.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/database-import.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/end-to-end.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/index.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/need-action.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/resources.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/scheduling.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/transforms.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/typed-etl-discovery.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/report-checklist.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/retention.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/bundled_skill/mammoth-cli/references/task-start.md +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/__init__.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/activity.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/addon.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/agent.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/ai.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/annotation.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/auth.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/billing.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/browse.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/calc.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/capability.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/client_app.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/completion.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/config.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/connector.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/context.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/dashboard.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/data_app.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/dataset.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/doctor.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/external_key.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/file.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/folder.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/job.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/link.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/notification.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/parameter.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/registry.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/report.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/schedule.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/schema.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/skill.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/snippet.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/support.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/template.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/trash.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/upgrade.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/user.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/view_profile.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/webhook.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/workflow.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/commands/workspace.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/context/__init__.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/context/credentials.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/context/endpoint.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/context/profiles.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/context/resolver.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/contracts/__init__.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/contracts/auth.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/embed.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/errors/__init__.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/manifest/__init__.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/manifest/loader.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/messages/__init__.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/output/__init__.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/output/envelope.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/output/normalize.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/output/policy.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/output/progress.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/output/render.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/py.typed +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/__init__.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/confirm.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/dataset_health.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/dryrun.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/dryrun_targets.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/embedded.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/executor.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/input_loader.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/intent_only.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/invocation.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/new_data.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/options.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/parents.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/runlog.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/session.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/state.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/strict.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/updates.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/validate.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/runtime/verify.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/__init__.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/argspec.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/board_values.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/coerce.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/conditional_format.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/conditions.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/dashboard_filters.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/dashboard_pages.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/dashboard_review.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/data_profile.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/data_quality.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/dispatch.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/factory.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/listing.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/local_time.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/mapping.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/openapi_types.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/positionals.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/protocol.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/read_queries.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/sdk_service.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/sql_check.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/stored_stats.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/testing.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/text_dates.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/type_system.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/services/write_impact.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/skills/__init__.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/skills/installer.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/skills/steering.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/mammoth_cli/testing.py +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/activity.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/addon.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/agent.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/ai.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/annotation.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/auth.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/billing.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/browse.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/calc.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/capability.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/client-app.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/completion.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/config.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/connector.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/context.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/dashboard.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/data-app.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/dataset.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/doctor.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/external-key.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/file.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/folder.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/job.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/link.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/log.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/notification.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/parameter.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/report.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/schedule.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/schema.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/skill.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/snippet.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/support.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/template.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/trash.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/upgrade.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/user.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/version.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/webhook.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/workflow.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/commands/workspace.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/edits-target.source.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/openapi-operations.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/schema-v1.json +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/sdk-catalog.source.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/manifests/sdk-methods.yaml +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/openapi/metadata.json +0 -0
- {mammoth_cli-2.2.29 → mammoth_cli-2.2.31}/spec/openapi/openapi.json +0 -0
|
@@ -106,7 +106,7 @@ Checkpoint Alert are the `view data-check` and `view checkpoint` families.
|
|
|
106
106
|
| Add columns from another view by matching keys | Join | `join` (`INNER`, `LEFT`, `RIGHT`, `OUTER`) |
|
|
107
107
|
| Bring one value per key from another view | Lookup | `lookup` |
|
|
108
108
|
| Totals, counts, averages per group | Group & Pivot | `pivot` (replaces the view's columns) |
|
|
109
|
-
| A rows-by-columns summary table | Group & Pivot (crosstab) | `crosstab` (writes a new dataset) |
|
|
109
|
+
| A rows-by-columns summary table | Group & Pivot (crosstab) | `crosstab` (writes a new dataset, and keeps rewriting it on every pipeline run of the view) |
|
|
110
110
|
| Turn wide columns into label/value rows | Unpivot | `unnest` |
|
|
111
111
|
| Read fields out of a JSON column | Extract JSON | `json-extract` |
|
|
112
112
|
| Classify, tag or summarise rows with AI | Generative AI | `ai` |
|
|
@@ -6,7 +6,7 @@ Every command returns the standard JSON envelope. On nonzero exit, read the erro
|
|
|
6
6
|
|
|
7
7
|
Run: `mammoth batch bulk-delete`. Exact input fields: `mammoth schema get batch.bulk-delete`.
|
|
8
8
|
|
|
9
|
-
Example: `mammoth batch bulk-delete 123`. Illustrative only: append `--yes` after observing an owned target.
|
|
9
|
+
Example: `mammoth batch bulk-delete 123 --input '{"ids": [11, 12]}'`. Illustrative only: append `--yes` after observing an owned target.
|
|
10
10
|
|
|
11
11
|
Result: `BatchBulkDeleteResult`; mutation `destructive`, confirmation `prompt_or_yes`, wait policy `not_async`.
|
|
12
12
|
|
|
@@ -16,7 +16,7 @@ Status on release: ran once on CLI 2.0.14 — write sweep 2026-09-18: exit 0 on
|
|
|
16
16
|
|
|
17
17
|
Run: `mammoth batch create`. Exact input fields: `mammoth schema get batch.create`.
|
|
18
18
|
|
|
19
|
-
Example: `mammoth batch create 123 123 --input '{"mapping":
|
|
19
|
+
Example: `mammoth batch create 123 123 --input '{"mapping": {"column_1": "column_1"}}'`. Placeholders are illustrative; resolve IDs and input from observed reads.
|
|
20
20
|
|
|
21
21
|
Result: `BatchCreateResult`; mutation `benign_mutation`, confirmation `none`, wait policy `returns_job`.
|
|
22
22
|
|
|
@@ -84,7 +84,7 @@ the run log. The connector command is `external_effect` with
|
|
|
84
84
|
mammoth schema get view.export.postgres # read secret_fields, required fields
|
|
85
85
|
# operator writes /private/path/request.json (mode 0600):
|
|
86
86
|
# {"host":"db.example","port":5432,"database":"analytics","table":"exports",
|
|
87
|
-
# "username":"agent","password":"…","dataset_id":DATASET_ID}
|
|
87
|
+
# "username":"agent","password":"…","replace_table":true,"dataset_id":DATASET_ID}
|
|
88
88
|
mammoth view export postgres VIEW_ID DATASET_ID --project PROJECT_ID \
|
|
89
89
|
--input /private/path/request.json --yes
|
|
90
90
|
mammoth view export list VIEW_ID DATASET_ID --project PROJECT_ID
|
|
@@ -93,6 +93,16 @@ mammoth view export list VIEW_ID DATASET_ID --project PROJECT_ID
|
|
|
93
93
|
If the operator has not supplied such a file, stop and ask for it; do not
|
|
94
94
|
compose the body yourself from values seen in chat.
|
|
95
95
|
|
|
96
|
+
A database export (`postgres`, `mysql`, `mssql`, `redshift`, `bigquery` with
|
|
97
|
+
`export_type` REPLACE, and `publish-db`) drops and recreates its destination
|
|
98
|
+
table now and on every later run of the view's pipeline, so any existing table
|
|
99
|
+
of that name is lost. It is refused (`replace_table_not_acknowledged`) unless
|
|
100
|
+
the input says `"replace_table": true`; only set that for a table the user said
|
|
101
|
+
may be overwritten. `postgres` and `mysql` also cut a table name to 60
|
|
102
|
+
characters and write a different table, so a longer name is refused
|
|
103
|
+
(`table_name_too_long`). The result carries `refreshes_on_pipeline_run` and a
|
|
104
|
+
`note` saying so.
|
|
105
|
+
|
|
96
106
|
## Generic export routes
|
|
97
107
|
|
|
98
108
|
`view export csv` and the typed destination commands are the proven path. The
|
|
@@ -24,6 +24,7 @@ from mammoth_cli.errors.envelope import (
|
|
|
24
24
|
)
|
|
25
25
|
from mammoth_cli.manifest.loader import command_by_id
|
|
26
26
|
from mammoth_cli.runtime.confirm import (
|
|
27
|
+
POLICY_CONFIRM_TARGET,
|
|
27
28
|
POLICY_PROMPT_OR_YES,
|
|
28
29
|
POLICY_YES_ALWAYS,
|
|
29
30
|
enforce_confirmation,
|
|
@@ -215,6 +216,50 @@ def automation_delete(invocation: Invocation) -> HandlerResult:
|
|
|
215
216
|
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
216
217
|
|
|
217
218
|
|
|
219
|
+
_RETENTION_TASK = "apply_retention_policy"
|
|
220
|
+
_UNATTENDED_DELETE_TARGET = "unattended-delete"
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def _guard_retention_tasks(invocation: Invocation, tasks: Any) -> Any:
|
|
224
|
+
"""Default retention tasks to ``require_approval`` and gate an unattended delete.
|
|
225
|
+
|
|
226
|
+
The route defaults ``require_approval`` to false, so a retention task with
|
|
227
|
+
the default ``delete`` action would hard-delete batches on every run with no
|
|
228
|
+
one approving. Here an omitted ``require_approval`` becomes true; an explicit
|
|
229
|
+
false with a delete action needs ``--yes --confirm unattended-delete``.
|
|
230
|
+
"""
|
|
231
|
+
if not isinstance(tasks, list):
|
|
232
|
+
return tasks
|
|
233
|
+
guarded: list[Any] = []
|
|
234
|
+
unattended = False
|
|
235
|
+
for task in tasks:
|
|
236
|
+
details = task.get("details") if isinstance(task, dict) else None
|
|
237
|
+
if not (
|
|
238
|
+
isinstance(task, dict)
|
|
239
|
+
and task.get("task_type") == _RETENTION_TASK
|
|
240
|
+
and isinstance(details, dict)
|
|
241
|
+
):
|
|
242
|
+
guarded.append(task)
|
|
243
|
+
elif "require_approval" not in details:
|
|
244
|
+
guarded.append({**task, "details": {**details, "require_approval": True}})
|
|
245
|
+
else:
|
|
246
|
+
unattended = unattended or (
|
|
247
|
+
details["require_approval"] is False and details.get("action", "delete") == "delete"
|
|
248
|
+
)
|
|
249
|
+
guarded.append(task)
|
|
250
|
+
if unattended:
|
|
251
|
+
enforce_confirmation(
|
|
252
|
+
invocation,
|
|
253
|
+
policy=POLICY_CONFIRM_TARGET,
|
|
254
|
+
action=(
|
|
255
|
+
"create a retention policy that DELETES matching batches on every run without "
|
|
256
|
+
"approval (require_approval is false)"
|
|
257
|
+
),
|
|
258
|
+
target=_UNATTENDED_DELETE_TARGET,
|
|
259
|
+
)
|
|
260
|
+
return guarded
|
|
261
|
+
|
|
262
|
+
|
|
218
263
|
def automation_create(invocation: Invocation) -> HandlerResult:
|
|
219
264
|
"""Create an automation. Name comes from a positional or the ``name`` field.
|
|
220
265
|
|
|
@@ -232,7 +277,7 @@ def automation_create(invocation: Invocation) -> HandlerResult:
|
|
|
232
277
|
hint="Pass the name as a positional argument or a 'name' input field.",
|
|
233
278
|
)
|
|
234
279
|
description = document.get("description", "")
|
|
235
|
-
tasks = _require_field(document, "tasks")
|
|
280
|
+
tasks = _guard_retention_tasks(invocation, _require_field(document, "tasks"))
|
|
236
281
|
kwargs: dict[str, Any] = {"name": name, "description": description, "tasks": tasks}
|
|
237
282
|
_forward_optional(document, kwargs, ("conditions", "condition_mode"))
|
|
238
283
|
enforce_confirmation(invocation, policy=POLICY_YES_ALWAYS, action="create an automation")
|
|
@@ -123,6 +123,144 @@ def batch_get(invocation: Invocation) -> HandlerResult:
|
|
|
123
123
|
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
124
124
|
|
|
125
125
|
|
|
126
|
+
_DATASET_GET_SYMBOL = "mammoth.api.datasets.DatasetsAPI.get"
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _dataset_columns(service: Any, dataset_id: int, side: str) -> list[dict[str, Any]]:
|
|
130
|
+
"""The dataset's own schema rows (``c_name``, ``c_id``, ``c_type``), or raise.
|
|
131
|
+
|
|
132
|
+
An unreadable schema is never treated as "no columns": the mapping check
|
|
133
|
+
could not run, so the batch is not created.
|
|
134
|
+
"""
|
|
135
|
+
response = service.call(_DATASET_GET_SYMBOL, dataset_id=dataset_id)
|
|
136
|
+
dataset = response.get("dataset") if isinstance(response, dict) else None
|
|
137
|
+
schema = dataset.get("data_schema") if isinstance(dataset, dict) else None
|
|
138
|
+
if not isinstance(schema, list) or not all(
|
|
139
|
+
isinstance(col, dict) and isinstance(col.get("c_name"), str) for col in schema
|
|
140
|
+
):
|
|
141
|
+
raise CliError(
|
|
142
|
+
code="append_schema_unreadable",
|
|
143
|
+
message=f"Could not read the {side} dataset {dataset_id}'s schema before the batch.",
|
|
144
|
+
hint=f"Read dataset {dataset_id} before creating the batch.",
|
|
145
|
+
details={"side": side, "dataset_id": dataset_id},
|
|
146
|
+
)
|
|
147
|
+
return schema
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _mapping_items(mapping: Any) -> list[Any]:
|
|
151
|
+
"""Expand a ``{src: dst}`` mapping into ``ColumnNameMapping`` items, untyped."""
|
|
152
|
+
if isinstance(mapping, dict):
|
|
153
|
+
return [{"source_c_name": src, "destination_c_name": dst} for src, dst in mapping.items()]
|
|
154
|
+
return list(mapping) if isinstance(mapping, list) else []
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _resolve_item(
|
|
158
|
+
item: dict[str, Any], source: list[dict[str, Any]], dest: list[dict[str, Any]]
|
|
159
|
+
) -> tuple[dict[str, Any] | None, dict[str, Any] | None]:
|
|
160
|
+
"""The (source, destination) schema rows an item names by name or id, else None."""
|
|
161
|
+
by_id = "source_c_id" in item
|
|
162
|
+
field, src_key, dest_key = (
|
|
163
|
+
("c_id", "source_c_id", "destination_c_id")
|
|
164
|
+
if by_id
|
|
165
|
+
else ("c_name", "source_c_name", "destination_c_name")
|
|
166
|
+
)
|
|
167
|
+
src_col = next((col for col in source if col.get(field) == item.get(src_key)), None)
|
|
168
|
+
dest_col = next((col for col in dest if col.get(field) == item.get(dest_key)), None)
|
|
169
|
+
return src_col, dest_col
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def _reject_incomplete_batch_mapping(
|
|
173
|
+
ids: tuple[int, int],
|
|
174
|
+
source_names: set[str],
|
|
175
|
+
mapped_sources: set[str],
|
|
176
|
+
unknown: list[str],
|
|
177
|
+
) -> None:
|
|
178
|
+
"""Refuse a mapping that leaves source columns out or names a missing destination."""
|
|
179
|
+
unmapped = sorted(source_names - mapped_sources)
|
|
180
|
+
if not unmapped and not unknown:
|
|
181
|
+
return
|
|
182
|
+
dataset_id, source_id = ids
|
|
183
|
+
problems = []
|
|
184
|
+
if unknown:
|
|
185
|
+
problems.append(f"destination column(s) not in dataset {dataset_id}: {', '.join(unknown)}")
|
|
186
|
+
if unmapped:
|
|
187
|
+
problems.append(f"source column(s) left unmapped and not appended: {', '.join(unmapped)}")
|
|
188
|
+
raise CliError(
|
|
189
|
+
code="append_mapping_incomplete",
|
|
190
|
+
message=(
|
|
191
|
+
f"mapping for appending dataset {source_id} into dataset {dataset_id} would corrupt "
|
|
192
|
+
f"the append: {'; '.join(problems)}. Unmapped source columns are not appended."
|
|
193
|
+
),
|
|
194
|
+
exit_status=EXIT_USAGE,
|
|
195
|
+
hint=(
|
|
196
|
+
f"Map every source column of dataset {source_id} to an existing column of dataset "
|
|
197
|
+
f"{dataset_id}."
|
|
198
|
+
),
|
|
199
|
+
details={"unknown_destinations": unknown, "unmapped_source_columns": unmapped},
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _reject_type_change(item: dict[str, Any], dest_col: dict[str, Any], dataset_id: int) -> None:
|
|
204
|
+
"""Refuse an explicit expected type that differs from the destination's real one."""
|
|
205
|
+
expected, actual = item.get("expected_destination_c_type"), dest_col.get("c_type")
|
|
206
|
+
if expected is None or not actual or str(expected).upper() == str(actual).upper():
|
|
207
|
+
return
|
|
208
|
+
raise CliError(
|
|
209
|
+
code="append_type_mismatch",
|
|
210
|
+
message=(
|
|
211
|
+
f"expected_destination_c_type {expected} for destination column "
|
|
212
|
+
f"{dest_col['c_name']!r} of dataset {dataset_id} would change its type from "
|
|
213
|
+
f"{actual}; the append would convert the whole destination column."
|
|
214
|
+
),
|
|
215
|
+
exit_status=EXIT_USAGE,
|
|
216
|
+
hint=(
|
|
217
|
+
"Omit expected_destination_c_type (the CLI sends the destination's current "
|
|
218
|
+
f"type, {actual})."
|
|
219
|
+
),
|
|
220
|
+
details={"column": dest_col["c_name"], "expected": expected, "actual": actual},
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def _checked_batch_mapping(
|
|
225
|
+
service: Any, dataset_id: int, source_id: int, mapping: Any
|
|
226
|
+
) -> tuple[list[dict[str, Any]], list[str]]:
|
|
227
|
+
"""Validate a batch mapping against both schemas; return typed items and warnings.
|
|
228
|
+
|
|
229
|
+
Refuses unmapped source columns and unknown destinations (the backend appends
|
|
230
|
+
only the mapped columns) and stamps every item with the destination's actual
|
|
231
|
+
current type, so no destination column is silently re-typed. A mapped pair
|
|
232
|
+
whose source and destination types differ is returned as a warning.
|
|
233
|
+
"""
|
|
234
|
+
source = _dataset_columns(service, source_id, "source")
|
|
235
|
+
dest = _dataset_columns(service, dataset_id, "target")
|
|
236
|
+
items = [dict(item) for item in _mapping_items(mapping) if isinstance(item, dict)]
|
|
237
|
+
typed: list[dict[str, Any]] = []
|
|
238
|
+
mapped_sources: set[str] = set()
|
|
239
|
+
unknown: list[str] = []
|
|
240
|
+
warnings: list[str] = []
|
|
241
|
+
for item in items:
|
|
242
|
+
src_col, dest_col = _resolve_item(item, source, dest)
|
|
243
|
+
if src_col is not None:
|
|
244
|
+
mapped_sources.add(src_col["c_name"])
|
|
245
|
+
if dest_col is None:
|
|
246
|
+
if item.get("action") != "add_column":
|
|
247
|
+
unknown.append(str(item.get("destination_c_name", item.get("destination_c_id"))))
|
|
248
|
+
typed.append(item)
|
|
249
|
+
continue
|
|
250
|
+
_reject_type_change(item, dest_col, dataset_id)
|
|
251
|
+
item.setdefault("expected_destination_c_type", dest_col.get("c_type"))
|
|
252
|
+
typed.append(item)
|
|
253
|
+
if src_col and src_col.get("c_type") and src_col["c_type"] != dest_col.get("c_type"):
|
|
254
|
+
warnings.append(
|
|
255
|
+
f"mapped column {src_col['c_name']!r} is {str(src_col['c_type']).lower()} but "
|
|
256
|
+
f"destination column {dest_col['c_name']!r} is {str(dest_col['c_type']).lower()}"
|
|
257
|
+
)
|
|
258
|
+
_reject_incomplete_batch_mapping(
|
|
259
|
+
(dataset_id, source_id), {col["c_name"] for col in source}, mapped_sources, unknown
|
|
260
|
+
)
|
|
261
|
+
return typed, warnings
|
|
262
|
+
|
|
263
|
+
|
|
126
264
|
def batch_create(invocation: Invocation) -> HandlerResult:
|
|
127
265
|
"""Create a batch for a dataset. ``source_id`` and ``mapping`` are required."""
|
|
128
266
|
project_id = require_project(invocation)
|
|
@@ -158,7 +296,12 @@ def batch_create(invocation: Invocation) -> HandlerResult:
|
|
|
158
296
|
action=f"create a batch on dataset {dataset_id} and DELETE the source dataset",
|
|
159
297
|
)
|
|
160
298
|
with open_service(invocation) as (service, auth):
|
|
299
|
+
kwargs["mapping"], warnings = _checked_batch_mapping(
|
|
300
|
+
service, dataset_id, source_id, mapping
|
|
301
|
+
)
|
|
161
302
|
data = service.call(_symbol(invocation), **kwargs)
|
|
303
|
+
if warnings and isinstance(data, dict):
|
|
304
|
+
data["mapping_check"] = {"warnings": warnings}
|
|
162
305
|
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
163
306
|
|
|
164
307
|
|
|
@@ -249,20 +392,30 @@ def batch_delete(invocation: Invocation) -> HandlerResult:
|
|
|
249
392
|
|
|
250
393
|
|
|
251
394
|
def batch_bulk_delete(invocation: Invocation) -> HandlerResult:
|
|
252
|
-
"""Bulk-delete batches
|
|
395
|
+
"""Bulk-delete the named batches of a dataset. Prompt or ``--yes`` required.
|
|
253
396
|
|
|
254
|
-
|
|
397
|
+
``ids`` is required: the backend rejects a delete with no batch ids, it
|
|
398
|
+
never deletes "all batches".
|
|
255
399
|
"""
|
|
256
400
|
project_id = require_project(invocation)
|
|
257
401
|
dataset_id = _require_int_positional_at(invocation, 0, "dataset id")
|
|
258
402
|
document = invocation.load_input() or {}
|
|
403
|
+
ids = _require_field(document, "ids")
|
|
404
|
+
if not ids:
|
|
405
|
+
raise CliError(
|
|
406
|
+
code=CODE_INVALID_ARGUMENT,
|
|
407
|
+
message="The 'ids' input field must name at least one batch id.",
|
|
408
|
+
exit_status=EXIT_USAGE,
|
|
409
|
+
hint=f"List batch ids with `mammoth batch list {dataset_id}`.",
|
|
410
|
+
)
|
|
411
|
+
count = len(ids) if isinstance(ids, list) else len(str(ids).split(","))
|
|
259
412
|
enforce_confirmation(
|
|
260
413
|
invocation,
|
|
261
414
|
policy=POLICY_PROMPT_OR_YES,
|
|
262
|
-
action=f"delete
|
|
415
|
+
action=f"delete {count} batch(es) of dataset {dataset_id}",
|
|
263
416
|
)
|
|
264
|
-
kwargs: dict[str, Any] = {"dataset_id": dataset_id, "project_id": project_id}
|
|
265
|
-
_forward_optional(document, kwargs, ("ids",))
|
|
266
417
|
with open_service(invocation) as (service, auth):
|
|
267
|
-
data = service.call(
|
|
418
|
+
data = service.call(
|
|
419
|
+
_symbol(invocation), dataset_id=dataset_id, project_id=project_id, ids=ids
|
|
420
|
+
)
|
|
268
421
|
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
@@ -505,23 +505,54 @@ def project_data_check_list(invocation: Invocation) -> HandlerResult:
|
|
|
505
505
|
return _list_with_filters(invocation)
|
|
506
506
|
|
|
507
507
|
|
|
508
|
+
_PROJECT_MEMBERS_SYMBOL = "mammoth.api.workspace.WorkspaceAPI.list_users"
|
|
509
|
+
|
|
510
|
+
|
|
511
|
+
def _reject_existing_members(service: Any, project_id: int, user_ids: Any) -> None:
|
|
512
|
+
"""Refuse an add naming a user who is already a member of the project.
|
|
513
|
+
|
|
514
|
+
The backend treats adding an existing member as a role replacement (it
|
|
515
|
+
revokes the current role, then assigns the given one), so an add can
|
|
516
|
+
silently demote a project admin. Role changes belong to ``project user
|
|
517
|
+
update``.
|
|
518
|
+
"""
|
|
519
|
+
members = service.call(_PROJECT_MEMBERS_SYMBOL, project_id=project_id)
|
|
520
|
+
member_ids = {str(m.get("id")) for m in members if isinstance(m, dict)}
|
|
521
|
+
existing = [uid for uid in user_ids if str(uid) in member_ids]
|
|
522
|
+
if existing:
|
|
523
|
+
raise CliError(
|
|
524
|
+
code=CODE_INVALID_ARGUMENT,
|
|
525
|
+
message=(
|
|
526
|
+
f"User(s) {', '.join(str(uid) for uid in existing)} already belong to project "
|
|
527
|
+
f"{project_id}; adding them again would replace their current role."
|
|
528
|
+
),
|
|
529
|
+
exit_status=EXIT_USAGE,
|
|
530
|
+
hint="Use `mammoth project user update` to change a member's role.",
|
|
531
|
+
details={"existing_user_ids": existing, "project_id": project_id},
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
|
|
508
535
|
def project_user_add(invocation: Invocation) -> HandlerResult:
|
|
509
|
-
"""Add users to a project. High-impact: ``--yes --confirm PROJECT_ID``.
|
|
536
|
+
"""Add users to a project. High-impact: ``--yes --confirm PROJECT_ID``.
|
|
537
|
+
|
|
538
|
+
``role`` is required and no listed user may already be a member (the
|
|
539
|
+
backend would replace that member's role).
|
|
540
|
+
"""
|
|
510
541
|
project_id = _project_id(invocation)
|
|
511
542
|
document = invocation.load_input()
|
|
512
543
|
user_ids = _require_input_field(document, "user_ids")
|
|
544
|
+
role = _require_input_field(document, "role")
|
|
513
545
|
enforce_confirmation(
|
|
514
546
|
invocation,
|
|
515
547
|
policy=POLICY_CONFIRM_TARGET,
|
|
516
548
|
action=f"add users to project {project_id}",
|
|
517
549
|
target=str(project_id),
|
|
518
550
|
)
|
|
519
|
-
kwargs: dict[str, Any] = {"project_id": project_id, "user_ids": user_ids}
|
|
520
|
-
assert document is not None
|
|
521
|
-
if "role" in document:
|
|
522
|
-
kwargs["role"] = document["role"]
|
|
523
551
|
with open_service(invocation) as (service, auth):
|
|
524
|
-
|
|
552
|
+
_reject_existing_members(service, project_id, user_ids)
|
|
553
|
+
data = service.call(
|
|
554
|
+
_symbol(invocation), project_id=project_id, user_ids=user_ids, role=role
|
|
555
|
+
)
|
|
525
556
|
return data, _meta(invocation, auth.workspace_id, project_id)
|
|
526
557
|
|
|
527
558
|
|