mammoth-cli 2.0.79__tar.gz → 2.0.81__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.0.79 → mammoth_cli-2.0.81}/PKG-INFO +1 -1
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/__init__.py +1 -1
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/connector.md +2 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/scheduling.md +37 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/connector.py +41 -1
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/verify.py +146 -4
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/data_quality.py +90 -3
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/sdk_service.py +51 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/pyproject.toml +1 -1
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/.gitignore +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/LICENSE +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/README.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/__main__.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/app.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/SKILL.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/about-mammoth.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/auth.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities-misc.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/capabilities.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/command-index.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/activity.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/addon.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/agent.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/ai.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/annotation.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/auth.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/automation.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/batch.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/billing.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/browse.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/calc.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/capability.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/client-app.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/completion.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/config.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/context.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard-qa.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dashboard.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/data-app.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/dataset.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/doctor.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/external-key.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/file.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/folder.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/job.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/log.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/notification.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/parameter.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/project.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/report.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schedule.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/schema.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/skill.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/snippet.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/support.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/template.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/trash.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/upgrade.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/user.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/version.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view-transform.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/view.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/webhook.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workflow.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/commands/workspace.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/handoff.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/input.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/jobs-drafts.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/machine-output.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/operations.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/auth-scope.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/cleanup.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/dashboards.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/end-to-end.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/exports.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/index.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/need-action.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/resources.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/transforms.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/recipes/typed-etl-discovery.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/recovery.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/report-checklist.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/retention.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/safety.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/task-start.md +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/__init__.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/activity.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/addon.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/agent.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/ai.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/annotation.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/auth.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/automation.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/batch.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/billing.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/browse.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/calc.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/capability.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/client_app.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/completion.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/config.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/context.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/dashboard.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/data_app.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/dataset.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/doctor.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/external_key.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/file.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/folder.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/job.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/notification.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/parameter.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/project.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/registry.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/report.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/schedule.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/schema.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/skill.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/snippet.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/support.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/template.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/trash.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/upgrade.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/user.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/view.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/view_ops.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/webhook.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/workflow.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/commands/workspace.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/context/__init__.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/context/credentials.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/context/endpoint.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/context/profiles.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/context/resolver.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/contracts/__init__.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/contracts/auth.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/embed.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/errors/__init__.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/errors/envelope.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/manifest/__init__.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/manifest/loader.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/messages/__init__.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/output/__init__.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/output/envelope.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/output/normalize.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/output/policy.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/output/progress.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/output/render.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/py.typed +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/__init__.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/confirm.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/dryrun.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/embedded.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/executor.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/input_loader.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/invocation.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/options.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/parents.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/runlog.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/session.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/state.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/strict.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/updates.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/runtime/validate.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/__init__.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/argspec.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/coerce.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/command_contract.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/conditions.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/dashboard_pages.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/dashboard_review.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/dispatch.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/factory.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/input_fields.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/mapping.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/openapi_types.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/positionals.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/protocol.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/testing.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/services/type_system.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/skills/__init__.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/skills/installer.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/skills/steering.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/testing.py +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/activity.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/addon.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/agent.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/ai.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/annotation.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/auth.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/automation.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/batch.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/billing.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/browse.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/calc.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/capability.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/client-app.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/completion.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/config.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/connector.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/context.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/dashboard.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/data-app.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/dataset.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/doctor.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/external-key.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/file.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/folder.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/job.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/log.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/notification.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/parameter.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/project.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/report.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/schedule.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/schema.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/skill.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/snippet.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/support.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/template.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/trash.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/upgrade.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/user.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/version.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/view.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/webhook.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/workflow.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/commands/workspace.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/openapi-operations.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/schema-v1.json +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/sdk-catalog.source.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/manifests/sdk-methods.yaml +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/openapi/metadata.json +0 -0
- {mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/spec/openapi/openapi.json +0 -0
|
@@ -200,6 +200,8 @@ Example: `mammoth connector list`. Placeholders are illustrative; resolve IDs an
|
|
|
200
200
|
|
|
201
201
|
Result: `ConnectorListResult`; mutation `read`, confirmation `none`, wait policy `not_async`.
|
|
202
202
|
|
|
203
|
+
`is_premium: true` with `is_added: false` means the connector must be enabled by Mammoth sales before this workspace can use it -- not that nobody has set it up yet. Such an item also carries `availability: "premium_not_enabled"` and an `availability_note` saying so. Tell the user why (premium, not enabled here) and offer to contact Mammoth sales; there is no CLI command that enables a premium connector or contacts sales on the user's behalf.
|
|
204
|
+
|
|
203
205
|
Status on release: ran once on CLI 2.0.18 — fixture-lifecycle sweep 2026-09-19: exit 0 on release with CLI 2.0.18. Listed 42 available connectors (name_key, disp_name, api_type, is_added, is_premium). Single invocation only.
|
|
204
206
|
|
|
205
207
|
### `connector.query.generate`
|
|
@@ -47,6 +47,43 @@ mammoth automation create 'Nightly refresh' --yes --input '{
|
|
|
47
47
|
from an uploaded file or a URL (`weburl`) is a one-time copy with no source
|
|
48
48
|
details to refresh from — say so rather than scheduling one.
|
|
49
49
|
|
|
50
|
+
That is not the same as "this dataset can never be recurring." A
|
|
51
|
+
file-backed dataset's pipeline re-runs on new data exactly like any other
|
|
52
|
+
dataset's ([how Mammoth organises
|
|
53
|
+
data](../about-mammoth.md#how-mammoth-organises-data)); the only difference
|
|
54
|
+
is that nothing pulls the next file in for you — each new batch has to be
|
|
55
|
+
uploaded with `append_to_ds_id` rather than fetched automatically. Once a
|
|
56
|
+
new file lands that way, every view on that dataset recomputes over it
|
|
57
|
+
automatically, so a monthly "email this view as CSV" delivery is buildable
|
|
58
|
+
today, with no connector:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# each month, once the new file is ready (a person or an outside script runs this):
|
|
62
|
+
mammoth file upload ./brand_spend_2026_10.csv --input '{"append_to_ds_id": DATASET_ID}'
|
|
63
|
+
# one-time setup: the recurring delivery itself, via send_an_alert
|
|
64
|
+
mammoth automation create 'Monthly brand spend to client' --yes --input '{
|
|
65
|
+
"tasks": [
|
|
66
|
+
{"task_type": "send_an_alert", "details": {
|
|
67
|
+
"alert_type": "email", "recipients": ["client@example.com"],
|
|
68
|
+
"subject": "Monthly brand spend report",
|
|
69
|
+
"attachments": {"dataview_ids": [VIEW_ID]}
|
|
70
|
+
}}
|
|
71
|
+
],
|
|
72
|
+
"conditions": [
|
|
73
|
+
{"condition_type": "at_specific_time", "details": {
|
|
74
|
+
"frequency": "monthly", "interval": 1, "start_at": "2026-01-01T02:00:00Z"
|
|
75
|
+
}}
|
|
76
|
+
]
|
|
77
|
+
}'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`send_an_alert`'s CSV attachment is exported from the view's prepared rows
|
|
81
|
+
at the moment the automation fires, not a copy taken at creation time (every
|
|
82
|
+
output in Mammoth is computed from prepared data, never cached — see
|
|
83
|
+
[about-mammoth.md](../about-mammoth.md)), so once this automation exists it
|
|
84
|
+
delivers whatever the view currently holds each month; only the file-upload
|
|
85
|
+
step above still needs to happen before each run.
|
|
86
|
+
|
|
50
87
|
The other three task types take the shapes documented in `schema get
|
|
51
88
|
automation.create` under `tasks[].details`:
|
|
52
89
|
|
|
@@ -145,11 +145,51 @@ def connector_get(invocation: Invocation) -> HandlerResult:
|
|
|
145
145
|
return data, _meta(invocation, auth.workspace_id, resolved_project(invocation))
|
|
146
146
|
|
|
147
147
|
|
|
148
|
+
#: ``availability`` a premium connector gets when it is not yet added to the
|
|
149
|
+
#: workspace -- distinct from an ordinary connector nobody has added yet.
|
|
150
|
+
_PREMIUM_NOT_ENABLED = "premium_not_enabled"
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
_PREMIUM_NOT_ENABLED_NOTE = (
|
|
154
|
+
"Premium connector, not enabled in this workspace: Mammoth sales must enable"
|
|
155
|
+
" it before it can be connected. Tell the user so and offer to contact sales."
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _with_premium_availability(data: Any) -> Any:
|
|
160
|
+
"""Mark each premium, not-yet-added connector with why it cannot be connected.
|
|
161
|
+
|
|
162
|
+
``is_premium``/``is_added`` alone said a connector could not be connected
|
|
163
|
+
but never why: a premium connector must be enabled by Mammoth sales
|
|
164
|
+
before this workspace can use it, unlike an ordinary connector nobody has
|
|
165
|
+
added yet. Without this, an agent that reads ``connector list`` can route
|
|
166
|
+
a user to sales but never explain why (T2-WPP-W7). ``ConnectorsAPI.list``
|
|
167
|
+
returns a list, which stays a list.
|
|
168
|
+
"""
|
|
169
|
+
if not isinstance(data, list):
|
|
170
|
+
return data
|
|
171
|
+
return [
|
|
172
|
+
(
|
|
173
|
+
{
|
|
174
|
+
**item,
|
|
175
|
+
"availability": _PREMIUM_NOT_ENABLED,
|
|
176
|
+
"availability_note": _PREMIUM_NOT_ENABLED_NOTE,
|
|
177
|
+
}
|
|
178
|
+
if isinstance(item, dict) and item.get("is_premium") and not item.get("is_added")
|
|
179
|
+
else item
|
|
180
|
+
)
|
|
181
|
+
for item in data
|
|
182
|
+
]
|
|
183
|
+
|
|
184
|
+
|
|
148
185
|
def connector_list(invocation: Invocation) -> HandlerResult:
|
|
149
186
|
"""List all available connectors (workspace-scoped, no project)."""
|
|
150
187
|
with open_service(invocation) as (service, auth):
|
|
151
188
|
data = service.call(_symbol(invocation))
|
|
152
|
-
return
|
|
189
|
+
return (
|
|
190
|
+
_with_premium_availability(data),
|
|
191
|
+
_meta(invocation, auth.workspace_id, resolved_project(invocation)),
|
|
192
|
+
)
|
|
153
193
|
|
|
154
194
|
|
|
155
195
|
def connector_ai_chat(invocation: Invocation) -> HandlerResult:
|
|
@@ -10,15 +10,21 @@ what it should" instead of re-deriving it per command.
|
|
|
10
10
|
|
|
11
11
|
from __future__ import annotations
|
|
12
12
|
|
|
13
|
-
from typing import
|
|
13
|
+
from typing import Any
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
from mammoth_cli.runtime.invocation import Invocation
|
|
15
|
+
from mammoth_cli.runtime.invocation import Invocation
|
|
17
16
|
|
|
18
17
|
#: Below this share of matched rows, a join has enough misses to flag before
|
|
19
18
|
#: the caller builds anything further on it.
|
|
20
19
|
JOIN_MATCH_RATE_THRESHOLD = 0.8
|
|
21
20
|
|
|
21
|
+
#: The one ``mutation_class`` (see ``spec/manifests/commands/view.yaml``) a
|
|
22
|
+
#: view write can carry that runs a pipeline -- the only kind that can leave a
|
|
23
|
+
#: downstream export referencing something the write just changed or removed.
|
|
24
|
+
_VIEW_PIPELINE_MUTATION_CLASS = "reversible_pipeline"
|
|
25
|
+
|
|
26
|
+
_VIEW_EXPORT_LIST_COMMAND = "view.export.list"
|
|
27
|
+
|
|
22
28
|
_FAILURE_STATUSES = frozenset({"error", "failed", "failure"})
|
|
23
29
|
_FAILURE_PIPELINE_STATES = frozenset({"error", "ref_error"})
|
|
24
30
|
|
|
@@ -83,6 +89,7 @@ def with_verify(data: Any, invocation: Invocation | None = None) -> Any:
|
|
|
83
89
|
verify["needs_user"] = _needs_user(
|
|
84
90
|
data, verified=verified, rows_before=rows_before, rows_after=rows_after
|
|
85
91
|
)
|
|
92
|
+
_apply_downstream_export_check(verify, invocation)
|
|
86
93
|
return {**data, "verify": verify}
|
|
87
94
|
|
|
88
95
|
|
|
@@ -270,7 +277,10 @@ def _needs_user(
|
|
|
270
277
|
``needs_user`` means "a serious outcome the user must hear about before
|
|
271
278
|
anything is built on it" -- never "something went wrong that you can
|
|
272
279
|
retry". A failed write is reported through ``verified``/``reason``
|
|
273
|
-
instead (see :func:`_verify_and_reason`); it never sets this
|
|
280
|
+
instead (see :func:`_verify_and_reason`); it never sets this -- except a
|
|
281
|
+
downstream export broken by this very write (see
|
|
282
|
+
:func:`_apply_downstream_export_check`), which is serious enough to set
|
|
283
|
+
both.
|
|
274
284
|
"""
|
|
275
285
|
if not verified:
|
|
276
286
|
return None
|
|
@@ -285,3 +295,135 @@ def _needs_user(
|
|
|
285
295
|
"confirm before building on it."
|
|
286
296
|
)
|
|
287
297
|
return None
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
# ---------------------------------------------------------------------------
|
|
301
|
+
# A pipeline write can succeed on the view itself while leaving a saved
|
|
302
|
+
# export broken -- its target_properties still reference a column the write
|
|
303
|
+
# just deleted or renamed. The write's own response never carries that; only
|
|
304
|
+
# a fresh read of the view's exports does (T2-WPP-W2: `view export dataset`
|
|
305
|
+
# then `view transform delete-columns` left the export in error_info 7001,
|
|
306
|
+
# but the delete's own verify said verified: true with no warnings).
|
|
307
|
+
# ---------------------------------------------------------------------------
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def _apply_downstream_export_check(verify: dict[str, Any], invocation: Invocation | None) -> None:
|
|
311
|
+
"""Flag a view-pipeline write that left one of its exports in error.
|
|
312
|
+
|
|
313
|
+
Runs at most once per write, and only when the write's own signals
|
|
314
|
+
already say a clean, settled success -- a write already reported
|
|
315
|
+
unverified for its own reason spends no extra read here.
|
|
316
|
+
"""
|
|
317
|
+
if invocation is None or not verify["verified"] or verify["reason"] is not None:
|
|
318
|
+
return
|
|
319
|
+
view_id = _pipeline_view_id(invocation)
|
|
320
|
+
if view_id is None:
|
|
321
|
+
return
|
|
322
|
+
broken = _broken_downstream_exports(invocation, view_id)
|
|
323
|
+
if isinstance(broken, str):
|
|
324
|
+
verify["warnings"] = [*verify["warnings"], broken]
|
|
325
|
+
return
|
|
326
|
+
if not broken:
|
|
327
|
+
return
|
|
328
|
+
details = [_export_error_detail(item) for item in broken]
|
|
329
|
+
verify["verified"] = False
|
|
330
|
+
verify["reason"] = "a saved export on this view is in error: " + "; ".join(details)
|
|
331
|
+
verify["warnings"] = [*verify["warnings"], *details]
|
|
332
|
+
verify["needs_user"] = (
|
|
333
|
+
"A saved export on this view is in error after this change; its data will "
|
|
334
|
+
"not reach its destination until this is fixed: " + "; ".join(details)
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _pipeline_view_id(invocation: Invocation) -> int | None:
|
|
339
|
+
"""The view id a ``reversible_pipeline`` write acted on, or None otherwise."""
|
|
340
|
+
from mammoth_cli.manifest.loader import command_by_id
|
|
341
|
+
|
|
342
|
+
record = command_by_id(invocation.command_id) or {}
|
|
343
|
+
if record.get("mutation_class") != _VIEW_PIPELINE_MUTATION_CLASS:
|
|
344
|
+
return None
|
|
345
|
+
for name in ("view_id", "dataview_id"):
|
|
346
|
+
value = invocation.positional(name)
|
|
347
|
+
if isinstance(value, bool):
|
|
348
|
+
continue
|
|
349
|
+
if isinstance(value, int):
|
|
350
|
+
return value
|
|
351
|
+
if isinstance(value, str) and value.strip().lstrip("-").isdigit():
|
|
352
|
+
return int(value)
|
|
353
|
+
return None
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def _broken_downstream_exports(invocation: Invocation, view_id: int) -> list[dict[str, Any]] | str:
|
|
357
|
+
"""Exports on ``view_id`` now in error, read via the existing ``view.export.list``.
|
|
358
|
+
|
|
359
|
+
Each export the backend hands back already carries its own ``error_info``
|
|
360
|
+
when it can no longer run (see ``mammoth.models.exports.ItemExportInfo``);
|
|
361
|
+
this is the one read that surfaces it. A read that itself fails returns
|
|
362
|
+
the warning to show instead: it must not hide the write's own,
|
|
363
|
+
already-settled success, nor pass silently.
|
|
364
|
+
"""
|
|
365
|
+
from mammoth_cli.commands.registry import HANDLERS
|
|
366
|
+
|
|
367
|
+
handler = HANDLERS.get(_VIEW_EXPORT_LIST_COMMAND)
|
|
368
|
+
if handler is None:
|
|
369
|
+
return []
|
|
370
|
+
read_invocation = Invocation(
|
|
371
|
+
command_id=_VIEW_EXPORT_LIST_COMMAND,
|
|
372
|
+
output="json",
|
|
373
|
+
profile=invocation.profile,
|
|
374
|
+
project=invocation.project,
|
|
375
|
+
timeout=invocation.timeout,
|
|
376
|
+
job_timeout=invocation.job_timeout,
|
|
377
|
+
pipeline_timeout=invocation.pipeline_timeout,
|
|
378
|
+
no_input=True,
|
|
379
|
+
positionals={"dataview_id": view_id},
|
|
380
|
+
extra_args=[str(view_id)],
|
|
381
|
+
)
|
|
382
|
+
try:
|
|
383
|
+
read_data, _meta = handler(read_invocation)
|
|
384
|
+
except Exception as exc: # noqa: BLE001 -- the write already succeeded
|
|
385
|
+
return f"could not check this view's saved exports: {exc}"
|
|
386
|
+
exports = read_data.get("exports") if isinstance(read_data, dict) else None
|
|
387
|
+
if not isinstance(exports, list):
|
|
388
|
+
return []
|
|
389
|
+
return [item for item in exports if isinstance(item, dict) and item.get("error_info")]
|
|
390
|
+
|
|
391
|
+
|
|
392
|
+
def _export_error_detail(item: dict[str, Any]) -> str:
|
|
393
|
+
"""Human-readable ``"export <id> (<handler>): ..."`` line for one broken export."""
|
|
394
|
+
error_info = item.get("error_info")
|
|
395
|
+
error_info = error_info if isinstance(error_info, dict) else {}
|
|
396
|
+
# the backend nests the details: error_info.additional_info.{error_code,
|
|
397
|
+
# reference_errors} (live `view export get`, T2-WPP-W2)
|
|
398
|
+
nested = error_info.get("additional_info")
|
|
399
|
+
error_info = {**error_info, **nested} if isinstance(nested, dict) else error_info
|
|
400
|
+
what = f"export {item.get('id')}"
|
|
401
|
+
handler_type = item.get("handler_type")
|
|
402
|
+
if handler_type:
|
|
403
|
+
what += f" ({handler_type})"
|
|
404
|
+
error_code = error_info.get("error_code")
|
|
405
|
+
detail = f"error {error_code}" if error_code is not None else "an error"
|
|
406
|
+
columns = _export_error_columns(error_info)
|
|
407
|
+
if columns:
|
|
408
|
+
detail += f", column(s) {', '.join(columns)}"
|
|
409
|
+
return f"{what} is in error: {detail}"
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _export_error_columns(error_info: dict[str, Any]) -> list[str]:
|
|
413
|
+
"""Column display names named by an export's ``error_info.reference_errors``."""
|
|
414
|
+
reference_errors = error_info.get("reference_errors")
|
|
415
|
+
if isinstance(reference_errors, dict):
|
|
416
|
+
entries = reference_errors.get("reference_errors") or []
|
|
417
|
+
elif isinstance(reference_errors, list):
|
|
418
|
+
entries = reference_errors
|
|
419
|
+
else:
|
|
420
|
+
entries = []
|
|
421
|
+
names: list[str] = []
|
|
422
|
+
for entry in entries:
|
|
423
|
+
if not isinstance(entry, dict):
|
|
424
|
+
continue
|
|
425
|
+
column = entry.get("column")
|
|
426
|
+
name = column.get("display_name") if isinstance(column, dict) else column
|
|
427
|
+
if isinstance(name, str):
|
|
428
|
+
names.append(name)
|
|
429
|
+
return names
|
|
@@ -11,6 +11,7 @@ from __future__ import annotations
|
|
|
11
11
|
|
|
12
12
|
import json
|
|
13
13
|
import re
|
|
14
|
+
from collections import Counter
|
|
14
15
|
from collections.abc import Iterable, Mapping
|
|
15
16
|
from datetime import datetime
|
|
16
17
|
from typing import Any
|
|
@@ -20,8 +21,14 @@ _MOSTLY = 0.8
|
|
|
20
21
|
#: Fewer non-blank values than this are too few to call a pattern.
|
|
21
22
|
_MIN_VALUES = 3
|
|
22
23
|
_MAX_EXAMPLES = 3
|
|
24
|
+
#: Most groups of variant spellings to name in one warning.
|
|
25
|
+
_MAX_VARIANT_GROUPS = 5
|
|
23
26
|
|
|
24
27
|
_NUMBER = re.compile(r"^[-+]?[$€£]?\s*\d[\d,]*(\.\d+)?\s*%?$|^[-+]?[$€£]?\s*\.\d+\s*%?$")
|
|
28
|
+
#: Punctuation is normalised to a space, not dropped, so "Pepsi,Cola" and
|
|
29
|
+
#: "Pepsi Cola" collapse the same way as "Pepsi Cola" itself.
|
|
30
|
+
_PUNCTUATION = re.compile(r"[^\w\s]", re.UNICODE)
|
|
31
|
+
_WHITESPACE = re.compile(r"\s+")
|
|
25
32
|
_DATE_FORMATS = (
|
|
26
33
|
"%Y-%m-%d",
|
|
27
34
|
"%Y/%m/%d",
|
|
@@ -61,6 +68,82 @@ def _convert_hint(view_id: int | None, column: str, to: str) -> str:
|
|
|
61
68
|
return f"mammoth view transform convert-type {target} --input '{spec}'"
|
|
62
69
|
|
|
63
70
|
|
|
71
|
+
def _bulk_replace_hint(view_id: int | None, column: str, mapping: list[dict[str, Any]]) -> str:
|
|
72
|
+
target = str(view_id) if view_id is not None else "VIEW_ID"
|
|
73
|
+
spec = json.dumps({"columns": [column], "mapping": mapping})
|
|
74
|
+
return f"mammoth view transform bulk-replace {target} --input '{spec}'"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _spelling_key(text: str) -> str:
|
|
78
|
+
"""Case-, whitespace- and punctuation-insensitive grouping key.
|
|
79
|
+
|
|
80
|
+
Punctuation is replaced with a space (not dropped) before whitespace is
|
|
81
|
+
collapsed, so inner punctuation ("Pepsi, Co.") normalises the same way
|
|
82
|
+
as a plain space ("Pepsi Co"). This is normalisation, not fuzzy
|
|
83
|
+
matching: different words ("pepsi cola") get a different key than
|
|
84
|
+
("pepsi") and are never grouped.
|
|
85
|
+
"""
|
|
86
|
+
despaced = _WHITESPACE.sub(" ", _PUNCTUATION.sub(" ", text))
|
|
87
|
+
return despaced.strip().lower()
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _variant_spelling_groups(texts: list[str]) -> list[tuple[str, list[str]]]:
|
|
91
|
+
"""Group distinct values that share a spelling key, keys sorted for stable output.
|
|
92
|
+
|
|
93
|
+
Only keys with more than one distinct raw value are a "group" worth
|
|
94
|
+
reporting; a single spelling of a value is not a finding.
|
|
95
|
+
"""
|
|
96
|
+
by_key: dict[str, list[str]] = {}
|
|
97
|
+
for text in dict.fromkeys(texts): # distinct values, first-seen order
|
|
98
|
+
key = _spelling_key(text)
|
|
99
|
+
if not key:
|
|
100
|
+
continue
|
|
101
|
+
by_key.setdefault(key, []).append(text)
|
|
102
|
+
groups = [(key, values) for key, values in by_key.items() if len(values) > 1]
|
|
103
|
+
return sorted(groups, key=lambda item: item[0])
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _most_used_spelling(values: list[str], counts: Counter[str]) -> str:
|
|
107
|
+
"""The group's most frequent real spelling (first seen on a tie), trimmed.
|
|
108
|
+
|
|
109
|
+
Never a synthesised form such as ``str.title()``, which would rewrite
|
|
110
|
+
"McDonald's" as "Mcdonald'S".
|
|
111
|
+
"""
|
|
112
|
+
return max(values, key=lambda value: counts[value]).strip()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _variant_spellings_warning(
|
|
116
|
+
column: str,
|
|
117
|
+
texts: list[str],
|
|
118
|
+
view_id: int | None,
|
|
119
|
+
checked: int,
|
|
120
|
+
) -> dict[str, Any] | None:
|
|
121
|
+
groups = _variant_spelling_groups(texts)
|
|
122
|
+
if not groups:
|
|
123
|
+
return None
|
|
124
|
+
shown = groups[:_MAX_VARIANT_GROUPS]
|
|
125
|
+
counts = Counter(texts)
|
|
126
|
+
canonical = {key: _most_used_spelling(values, counts) for key, values in shown}
|
|
127
|
+
mapping = [{"search": sorted(values), "replace": canonical[key]} for key, values in shown]
|
|
128
|
+
examples = "; ".join(
|
|
129
|
+
"{" + ", ".join(repr(v) for v in sorted(values)) + "}" + f" -> {canonical[key]!r}"
|
|
130
|
+
for key, values in shown
|
|
131
|
+
)
|
|
132
|
+
detail = (
|
|
133
|
+
f"{len(groups)} group(s) of values in this TEXT column differ only in case, "
|
|
134
|
+
f"whitespace or punctuation and will be counted separately unless unified: {examples}."
|
|
135
|
+
)
|
|
136
|
+
if len(groups) > _MAX_VARIANT_GROUPS:
|
|
137
|
+
detail += f" ({len(groups) - _MAX_VARIANT_GROUPS} more not shown)."
|
|
138
|
+
return {
|
|
139
|
+
"column": column,
|
|
140
|
+
"issue": "variant_spellings",
|
|
141
|
+
"detail": detail,
|
|
142
|
+
"fix": _bulk_replace_hint(view_id, column, mapping),
|
|
143
|
+
"rows_checked": checked,
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
|
|
64
147
|
def _duplicate_rows_warning(
|
|
65
148
|
rows: list[Mapping[str, Any]],
|
|
66
149
|
view_id: int | None,
|
|
@@ -111,9 +194,10 @@ def column_warnings(
|
|
|
111
194
|
Returns:
|
|
112
195
|
One record per finding: ``column``, ``issue``
|
|
113
196
|
(``numbers_stored_as_text``, ``dates_stored_as_text``,
|
|
114
|
-
``blank_values`` or the table-level
|
|
115
|
-
and, where one command fixes it,
|
|
116
|
-
given (``rows_checked`` on each
|
|
197
|
+
``variant_spellings``, ``blank_values`` or the table-level
|
|
198
|
+
``duplicate_rows``), ``detail`` and, where one command fixes it,
|
|
199
|
+
``fix``. Counts are over the rows given (``rows_checked`` on each
|
|
200
|
+
record).
|
|
117
201
|
"""
|
|
118
202
|
materialised = [row for row in rows if isinstance(row, Mapping)]
|
|
119
203
|
if not materialised:
|
|
@@ -166,6 +250,9 @@ def column_warnings(
|
|
|
166
250
|
}
|
|
167
251
|
)
|
|
168
252
|
break
|
|
253
|
+
variant_warning = _variant_spellings_warning(column, texts, view_id, checked)
|
|
254
|
+
if variant_warning is not None:
|
|
255
|
+
warnings.append(variant_warning)
|
|
169
256
|
if blanks:
|
|
170
257
|
warnings.append(
|
|
171
258
|
{
|
|
@@ -489,6 +489,7 @@ class SdkMammothService:
|
|
|
489
489
|
local_columns = getattr(view, "columns", {}) or {}
|
|
490
490
|
local_internal = {value for value in local_columns.values() if isinstance(value, str)}
|
|
491
491
|
local_display = set(local_columns)
|
|
492
|
+
local_types = getattr(view, "column_types", {}) or {}
|
|
492
493
|
|
|
493
494
|
def check(value: Any, *, scope: str, columns: dict[str, str], display: set[str]) -> None:
|
|
494
495
|
internal = {item for item in columns.values() if isinstance(item, str)}
|
|
@@ -583,6 +584,8 @@ class SdkMammothService:
|
|
|
583
584
|
columns=local_columns,
|
|
584
585
|
display=local_display,
|
|
585
586
|
)
|
|
587
|
+
if not spec.get("value_is_column") and "value" in spec:
|
|
588
|
+
SdkMammothService._coerce_numeric_condition_value(spec, local_types)
|
|
586
589
|
if spec.get("value_is_column") and "value" in spec:
|
|
587
590
|
check(
|
|
588
591
|
spec["value"],
|
|
@@ -639,6 +642,54 @@ class SdkMammothService:
|
|
|
639
642
|
display=foreign_display,
|
|
640
643
|
)
|
|
641
644
|
|
|
645
|
+
@staticmethod
|
|
646
|
+
def _coerce_numeric_condition_value(spec: dict[str, Any], column_types: dict[str, str]) -> None:
|
|
647
|
+
"""Convert a numeric-string condition value against a NUMERIC column.
|
|
648
|
+
|
|
649
|
+
The backend accepts a filter/set-values/pivot condition whose value is
|
|
650
|
+
a string even when the column is NUMERIC, then puts the whole pipeline
|
|
651
|
+
into ref_error (7003 "type mismatch") on the next read. The CLI already
|
|
652
|
+
knows the column's type, so it converts what it can and fails locally,
|
|
653
|
+
before any request, for what it can't. DATE columns are left as is.
|
|
654
|
+
"""
|
|
655
|
+
column = spec.get("column")
|
|
656
|
+
if not isinstance(column, str) or column_types.get(column) != "NUMERIC":
|
|
657
|
+
return
|
|
658
|
+
|
|
659
|
+
def convert(item: Any) -> Any:
|
|
660
|
+
if not isinstance(item, str):
|
|
661
|
+
return item
|
|
662
|
+
try:
|
|
663
|
+
return int(item)
|
|
664
|
+
except ValueError:
|
|
665
|
+
pass
|
|
666
|
+
try:
|
|
667
|
+
number = float(item)
|
|
668
|
+
except ValueError:
|
|
669
|
+
raise SdkMammothService._condition_value_type_error(
|
|
670
|
+
column, "NUMERIC", item
|
|
671
|
+
) from None
|
|
672
|
+
return int(number) if number.is_integer() else number
|
|
673
|
+
|
|
674
|
+
value = spec["value"]
|
|
675
|
+
spec["value"] = (
|
|
676
|
+
[convert(item) for item in value] if isinstance(value, list) else convert(value)
|
|
677
|
+
)
|
|
678
|
+
|
|
679
|
+
@staticmethod
|
|
680
|
+
def _condition_value_type_error(column: str, column_type: str, value: str) -> CliError:
|
|
681
|
+
"""Build a stable pre-mutation condition-value type error."""
|
|
682
|
+
return CliError(
|
|
683
|
+
code="invalid_condition_value",
|
|
684
|
+
message=(
|
|
685
|
+
f"Condition value {value!r} for column '{column}' ({column_type}) "
|
|
686
|
+
"is not a valid number."
|
|
687
|
+
),
|
|
688
|
+
exit_status=EXIT_USAGE,
|
|
689
|
+
hint="Use a numeric value for a NUMERIC column.",
|
|
690
|
+
details={"column": column, "column_type": column_type, "value": value},
|
|
691
|
+
)
|
|
692
|
+
|
|
642
693
|
@staticmethod
|
|
643
694
|
def column_input_error(
|
|
644
695
|
reference: str,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mammoth_cli-2.0.79 → mammoth_cli-2.0.81}/mammoth_cli/bundled_skill/mammoth-cli/references/auth.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|