focus-data-toolkit 0.12.0__tar.gz → 0.14.0__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.
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/CHANGELOG.md +193 -1
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/CONTRIBUTING.md +9 -1
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/MANIFEST.in +2 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/NOTICE +11 -0
- focus_data_toolkit-0.14.0/PKG-INFO +185 -0
- focus_data_toolkit-0.14.0/README.md +132 -0
- focus_data_toolkit-0.14.0/docs/conversion-rules.md +176 -0
- focus_data_toolkit-0.14.0/docs/generator-corrections.md +198 -0
- focus_data_toolkit-0.14.0/docs/generator-statistics.json +77 -0
- focus_data_toolkit-0.14.0/docs/pr53-review-followup.md +61 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/supplements.md +28 -4
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/versioning.md +17 -2
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/pyproject.toml +4 -1
- focus_data_toolkit-0.14.0/scripts/__init__.py +1 -0
- focus_data_toolkit-0.14.0/scripts/describe_generated_samples.py +42 -0
- focus_data_toolkit-0.14.0/scripts/sample_audit.py +162 -0
- focus_data_toolkit-0.14.0/scripts/validate_official_samples.py +354 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/__init__.py +27 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/_version.py +1 -1
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/cli.py +40 -4
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/context/__init__.py +9 -3
- focus_data_toolkit-0.14.0/src/focus_data_toolkit/context/provider.py +173 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/convert/__init__.py +46 -23
- focus_data_toolkit-0.14.0/src/focus_data_toolkit/convert/contract_commitment.py +296 -0
- focus_data_toolkit-0.14.0/src/focus_data_toolkit/convert/cost_and_usage.py +732 -0
- focus_data_toolkit-0.14.0/src/focus_data_toolkit/convert/exceptions.py +20 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/convert/invoice_detail.py +4 -1
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/convert/streaming.py +42 -9
- focus_data_toolkit-0.14.0/src/focus_data_toolkit/generators/engine/context.py +36 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/engine/determinism.py +35 -21
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/engine/json_focus.py +11 -10
- focus_data_toolkit-0.14.0/src/focus_data_toolkit/generators/engine/ladder.py +86 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/engine/scenarios_core.py +68 -53
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/engine/serialize.py +67 -50
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/providers/aws.py +6 -9
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/providers/azure.py +1 -6
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/providers/gcp.py +0 -5
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/providers/profile.py +2 -2
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/versions/v1_3.py +9 -25
- focus_data_toolkit-0.14.0/src/focus_data_toolkit/model/focus_1_4_decimal_scale.json +49 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/validator.py +164 -8
- focus_data_toolkit-0.14.0/src/focus_data_toolkit/official_report.py +66 -0
- focus_data_toolkit-0.14.0/src/focus_data_toolkit/official_validator.py +117 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/adapters_provenance.json +18 -2
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/aws_savings_plans.json +7 -5
- focus_data_toolkit-0.14.0/src/focus_data_toolkit/supplement/adapters/azure_reservation_orders.json +33 -0
- focus_data_toolkit-0.14.0/src/focus_data_toolkit/supplement/adapters/azure_savings_plan_orders.json +35 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/registry.py +45 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/apply.py +2 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/gaps.py +18 -1
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/kinds.py +4 -1
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/loader.py +10 -1
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/codes.py +22 -0
- focus_data_toolkit-0.14.0/src/focus_data_toolkit.egg-info/PKG-INFO +185 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit.egg-info/SOURCES.txt +41 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit.egg-info/requires.txt +1 -1
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/client_like/SOURCES.md +1 -1
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/client_like/consolidated_multi_provider_1_3.csv +4 -4
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/golden/README.md +8 -2
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_2_cost_and_usage_rows100_seed42.csv +101 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_2_cost_and_usage_rows25_seed7.csv +26 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_2_cost_and_usage_rows25_seed7_credits.csv +26 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_3_contract_commitment_rows100_seed42.csv +12 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_3_cost_and_usage_rows100_seed42.csv +101 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_3_cost_and_usage_rows25_seed7.csv +26 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_3_cost_and_usage_rows25_seed7_credits.csv +26 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_2_cost_and_usage_rows100_seed42.csv +101 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_2_cost_and_usage_rows25_seed7.csv +26 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_2_cost_and_usage_rows25_seed7_credits.csv +26 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_3_contract_commitment_rows100_seed42.csv +10 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_3_cost_and_usage_rows100_seed42.csv +101 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_3_cost_and_usage_rows25_seed7.csv +26 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_3_cost_and_usage_rows25_seed7_credits.csv +26 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_2_cost_and_usage_rows100_seed42.csv +101 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_2_cost_and_usage_rows25_seed7.csv +26 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_2_cost_and_usage_rows25_seed7_credits.csv +26 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_contract_commitment_rows100_seed42.csv +12 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_cost_and_usage_rows100_seed42.csv +101 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_cost_and_usage_rows25_seed7.csv +26 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_cost_and_usage_rows25_seed7_credits.csv +26 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/correctness_migration/README.md +10 -0
- focus_data_toolkit-0.14.0/tests/fixtures/golden/correctness_migration/pr67_before.json +815 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/README.md +15 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/aws_1.2_CostAndUsage.log +625 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/aws_1.2_CostAndUsage.stderr.log +0 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/aws_1.3_ContractCommitment.log +117 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/aws_1.3_ContractCommitment.stderr.log +0 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/aws_1.3_CostAndUsage.log +865 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/aws_1.3_CostAndUsage.stderr.log +0 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/azure_1.2_CostAndUsage.log +617 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/azure_1.2_CostAndUsage.stderr.log +0 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/azure_1.3_ContractCommitment.log +117 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/azure_1.3_ContractCommitment.stderr.log +0 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/azure_1.3_CostAndUsage.log +857 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/azure_1.3_CostAndUsage.stderr.log +0 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/baseline.json +35853 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/gcp_1.2_CostAndUsage.log +617 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/gcp_1.2_CostAndUsage.stderr.log +0 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/gcp_1.3_ContractCommitment.log +117 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/gcp_1.3_ContractCommitment.stderr.log +0 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/gcp_1.3_CostAndUsage.log +857 -0
- focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/gcp_1.3_CostAndUsage.stderr.log +0 -0
- focus_data_toolkit-0.14.0/tests/test_adapters_aws.py +321 -0
- focus_data_toolkit-0.14.0/tests/test_adapters_azure_gcp.py +402 -0
- focus_data_toolkit-0.14.0/tests/test_contract_commitment_duration.py +357 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_contract_commitment_semantics.py +17 -7
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_convert_roundtrip.py +1 -1
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_cross_provider.py +2 -2
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_fake_provider.py +0 -1
- focus_data_toolkit-0.14.0/tests/test_focus_1_4_value_migrations.py +276 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_generated_conformance.py +35 -17
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_generators.py +3 -1
- focus_data_toolkit-0.14.0/tests/test_lint_focus_1_4_rules.py +322 -0
- focus_data_toolkit-0.14.0/tests/test_official_report.py +146 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_official_validator.py +2 -2
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_parquet.py +3 -3
- focus_data_toolkit-0.14.0/tests/test_provider_roles_1_2.py +230 -0
- focus_data_toolkit-0.14.0/tests/test_public_api.py +83 -0
- focus_data_toolkit-0.14.0/tests/test_reviewed_roundtrip.py +78 -0
- focus_data_toolkit-0.14.0/tests/test_reviewed_sample_semantics.py +150 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_streaming.py +63 -0
- focus_data_toolkit-0.12.0/PKG-INFO +0 -196
- focus_data_toolkit-0.12.0/README.md +0 -143
- focus_data_toolkit-0.12.0/scripts/validate_official_samples.py +0 -353
- focus_data_toolkit-0.12.0/src/focus_data_toolkit/context/provider.py +0 -90
- focus_data_toolkit-0.12.0/src/focus_data_toolkit/convert/contract_commitment.py +0 -182
- focus_data_toolkit-0.12.0/src/focus_data_toolkit/convert/cost_and_usage.py +0 -212
- focus_data_toolkit-0.12.0/src/focus_data_toolkit/generators/engine/context.py +0 -12
- focus_data_toolkit-0.12.0/src/focus_data_toolkit/generators/engine/ladder.py +0 -71
- focus_data_toolkit-0.12.0/src/focus_data_toolkit/model/focus_1_4_decimal_scale.json +0 -10
- focus_data_toolkit-0.12.0/src/focus_data_toolkit/official_validator.py +0 -61
- focus_data_toolkit-0.12.0/src/focus_data_toolkit.egg-info/PKG-INFO +0 -196
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_2_cost_and_usage_rows100_seed42.csv +0 -101
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_2_cost_and_usage_rows25_seed7.csv +0 -26
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_2_cost_and_usage_rows25_seed7_credits.csv +0 -26
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_3_contract_commitment_rows100_seed42.csv +0 -11
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_3_cost_and_usage_rows100_seed42.csv +0 -101
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_3_cost_and_usage_rows25_seed7.csv +0 -26
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_3_cost_and_usage_rows25_seed7_credits.csv +0 -26
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_2_cost_and_usage_rows100_seed42.csv +0 -101
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_2_cost_and_usage_rows25_seed7.csv +0 -26
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_2_cost_and_usage_rows25_seed7_credits.csv +0 -26
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_3_contract_commitment_rows100_seed42.csv +0 -11
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_3_cost_and_usage_rows100_seed42.csv +0 -101
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_3_cost_and_usage_rows25_seed7.csv +0 -26
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_3_cost_and_usage_rows25_seed7_credits.csv +0 -26
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_2_cost_and_usage_rows100_seed42.csv +0 -101
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_2_cost_and_usage_rows25_seed7.csv +0 -26
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_2_cost_and_usage_rows25_seed7_credits.csv +0 -26
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_contract_commitment_rows100_seed42.csv +0 -12
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_cost_and_usage_rows100_seed42.csv +0 -101
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_cost_and_usage_rows25_seed7.csv +0 -26
- focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_cost_and_usage_rows25_seed7_credits.csv +0 -26
- focus_data_toolkit-0.12.0/tests/test_adapters_aws.py +0 -179
- focus_data_toolkit-0.12.0/tests/test_adapters_azure_gcp.py +0 -142
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/LICENSE +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/LICENSES/CC-BY-4.0.txt +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/SECURITY.md +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/audit/2026-07-improvement-plan.md +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/audit/2026-07-repo-audit.md +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/compatibility.md +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/model-provenance.md +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/releasing.md +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/runner.md +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/security-model.md +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/studio.md +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/schema/model_provenance.schema.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/scripts/check_pinned_actions.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/scripts/generate_resolved_sbom.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/scripts/generate_sbom.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/scripts/regenerate_golden_fixtures.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/scripts/verify_model_provenance.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/scripts/verify_release.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/setup.cfg +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/__main__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/context/billing.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/convert/billing_period.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/convert/contract_applied.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/convert/detect.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/errors.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/focus_json.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/__init__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/_shim.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/engine/__init__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/engine/allocation_math.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/generate_aws_focus_1_2.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/generate_aws_focus_1_3.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/generate_azure_focus_1_2.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/generate_azure_focus_1_3.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/generate_gcp_focus_1_2.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/generate_gcp_focus_1_3.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/providers/__init__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/scenarios.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/versions/__init__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/versions/adapter.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/versions/v1_2.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/io/__init__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/io/atomic_writer.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/io/csv_io.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/io/parquet_io.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/io/records.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/io/row_source.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/lifecycle.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/manifest.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/__init__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/capabilities.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/focus_1_4_model.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/focus_1_4_servicesubcategory.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/focus_json_keys.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/iso_4217_currencies.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/json_schema_check.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/json_schemas/allocatedmethoddetailsobjectschema.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/json_schemas/commitmentprogrameligibilitydetailsobjectschema.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/json_schemas/contractappliedobjectschema.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/json_schemas/contractcommitmentapplicabilityobjectschema.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/json_schemas/json_schemas_provenance.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/model_provenance.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/modes.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/progress.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/provenance.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/py.typed +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/runtime.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/schema/__init__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/schema/detection.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/schema/registry.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/storage/__init__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/storage/external_index.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/storage/spill.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/__init__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/app.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/config.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/frontend/app.js +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/frontend/index.html +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/frontend/style.css +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/jobs.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/preview.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/security.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/server.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/__init__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/__init__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/aws_invoice_summary.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/azure_invoice.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/gcp_compute_commitments.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/spec.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/validate.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/__init__.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/allocation.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/bundle.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/corrections.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/reconciliation.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/referential.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit.egg-info/dependency_links.txt +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit.egg-info/entry_points.txt +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit.egg-info/top_level.txt +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/conftest.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/golden/compatibility_golden/scenarios_correction_set.csv +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/golden/compatibility_golden/scenarios_sca_equal.csv +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/golden/compatibility_golden/scenarios_sca_negative.csv +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/golden/compatibility_golden/scenarios_sca_weighted.csv +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/official/SOURCES.md +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/official/invoice_detail_grain_example.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/official/numeric_format_examples.json +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_atomic_write.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_billing_lifecycle.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_bundle_gate.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_bundle_validation.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_capabilities.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_cli.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_client_like.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_container.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_contract_applied.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_corrections.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_detect.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_fixtures_are_synthetic.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_focus_json.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_gaps.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_generator_golden.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_grouping_keys.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_lifecycle_chains.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_lineage_counters.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_lint_focus.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_manifest.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_model_provenance.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_multi_provider.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_official_json_schemas.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_packaging.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_parquet_input.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_participant_entities.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_partitioning.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_progress_cancel.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_release_tooling.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_replace_recovery.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_runtime.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_schema_detection.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_single_source_focus_rules.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_spill.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_split_allocation.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_strict_conversion.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_studio.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_supplement_apply.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_supplement_loader.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_supplement_streaming.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_synthetic_conversion.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_validator.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_workflow_pins.py +0 -0
- {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tools/extract_focus_1_4_model.py +0 -0
|
@@ -9,6 +9,196 @@ policy.
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [0.14.0] — 2026-10-06
|
|
13
|
+
|
|
14
|
+
FOCUS 1.2/1.3 to 1.4 conversion follows the specification more closely, and commitment
|
|
15
|
+
terms and provider-native commitment inventories can complete the 1.4 datasets.
|
|
16
|
+
|
|
17
|
+
- **Breaking:**
|
|
18
|
+
- the `aws-savings-plans` adapter (v2) is keyed by `savingsPlanArn`;
|
|
19
|
+
- a unit-price column the source does not carry is omitted from the 1.4 Cost and Usage
|
|
20
|
+
output instead of being written null.
|
|
21
|
+
- **New byte baselines for converted output:**
|
|
22
|
+
- conversion manifests label migrated columns `DERIVED`;
|
|
23
|
+
- `ContractCommitmentDurationType` is written in whole years (`1 Year`, `3 Years`).
|
|
24
|
+
|
|
25
|
+
Generated samples are unchanged.
|
|
26
|
+
- **Stricter lint:** output that passed with 0.13.0 can now be refused; see the upgrade
|
|
27
|
+
notes under Changed.
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- The supplement API and the documented failures are now public, re-exported from the
|
|
32
|
+
package root: `SupplementBundle`, `SupplementFileSpec`, `load_bundle_dir`,
|
|
33
|
+
`compute_gaps`, `GapReport`, `Mode`, `ConversionError`, `SupplementError` and
|
|
34
|
+
`AtomicWriteError`. Library callers no longer import internal modules, which
|
|
35
|
+
docs/versioning.md leaves free to change. Existing import paths keep working.
|
|
36
|
+
- `ContractCommitmentDurationType` is a `contract_commitment` supplement column. A
|
|
37
|
+
supplied term wins over the derived value. It must follow the format v1.4 recommends, a
|
|
38
|
+
positive whole number and a listed unit (`1 Year`, `3 Years`, `36 Months`); anything
|
|
39
|
+
else is refused (`FDT-SUPP-004`). That format is a SHOULD in the specification, so the
|
|
40
|
+
lint of a 1.4 file does not fail on it.
|
|
41
|
+
- `fdt gaps` reports `ContractCommitmentDurationType` as a conditional advisory: it is
|
|
42
|
+
derived only for periods spanning whole calendar months, so the other terms must be
|
|
43
|
+
supplied. Without a commitment period in the source it is a blocking gap.
|
|
44
|
+
- A supplement that almost matches a provider adapter now names what it lacks, e.g.
|
|
45
|
+
`aws-savings-plans@2 requires savingsPlanArn` for an export from the v1 adapter era.
|
|
46
|
+
- Azure commitment adapters `azure-reservation-orders` and `azure-savings-plan-orders`
|
|
47
|
+
(REST shape, api-version 2022-11-01). One row per order, keyed by the lowercased order
|
|
48
|
+
ARM id. Each provides the term (`1 Year`, `3 Years`, `5 Years`, or `1 Month` for a
|
|
49
|
+
monthly savings plan), the payment model and interval, the lifecycle status where the
|
|
50
|
+
provisioning state determines one, the creation time where the response carries it,
|
|
51
|
+
and the benefit category. The commitment model is
|
|
52
|
+
not emitted: the order does not carry the commitment grain. A savings plan order is
|
|
53
|
+
recognised by its `sku.name`; a new `none_of` detection clause rules out the plan- and
|
|
54
|
+
reservation-level lists, which share it. A `lower` adapter transform supports
|
|
55
|
+
case-insensitive provider identifiers.
|
|
56
|
+
- A FOCUS 1.2 source can declare who issued it (`provider_role="csp"` or `"msp"`, CLI
|
|
57
|
+
`--provider-role`), with the cloud provider's own publisher names
|
|
58
|
+
(`first_party_publishers`, CLI `--first-party-publisher`).
|
|
59
|
+
- With `csp`, a row whose `PublisherName` differs from `ProviderName` and is no declared
|
|
60
|
+
first-party name is a Marketplace charge. Its `ServiceProviderName` and
|
|
61
|
+
`HostProviderName` become the seller, as FOCUS 1.4 requires (`FDT-MIG-005`).
|
|
62
|
+
- With `msp`, they stay `ProviderName`, the MSP.
|
|
63
|
+
- Without a declaration, `ProviderName` is kept as before, and those rows are reported
|
|
64
|
+
(`FDT-CTX-005`). Nothing is refused.
|
|
65
|
+
- Only official FOCUS columns are read. See
|
|
66
|
+
[conversion rules](docs/conversion-rules.md#participating-entities-of-a-12-source),
|
|
67
|
+
including why `HostProviderName` = `ServiceProviderName` is an interpretation for a 1.2
|
|
68
|
+
source.
|
|
69
|
+
|
|
70
|
+
### Changed
|
|
71
|
+
|
|
72
|
+
- **Breaking:** the `aws-savings-plans` adapter (now v2) is keyed by `savingsPlanArn`,
|
|
73
|
+
the fully qualified identifier FOCUS recommends and the one Contract Commitment and
|
|
74
|
+
`CommitmentDiscountId` carry. v1 keyed by the bare `savingsPlanId`, so its rows could
|
|
75
|
+
never join. Exports without `savingsPlanArn` are no longer auto-detected; re-export
|
|
76
|
+
with the ARN (as `describe-savings-plans` returns it). The adapter also maps
|
|
77
|
+
`termDurationInSeconds` (1 and 3 years) to the duration.
|
|
78
|
+
- **New byte baseline:** `ContractCommitmentDurationType` is written in whole years when
|
|
79
|
+
the term is a multiple of 12 months (`1 Year`, `3 Years`), as in the specification's
|
|
80
|
+
examples, instead of `12 Months` / `36 Months`. Other terms stay in months.
|
|
81
|
+
- The FOCUS 1.4 linter now enforces more of the static Cost and Usage rules of the v1.4
|
|
82
|
+
requirements model. Output that passed before can now fail the lint, and strict
|
|
83
|
+
publication refuses it:
|
|
84
|
+
- Tax and Credit `EffectiveCost` equals `BilledCost` (CAU-EffectiveCost-C-017);
|
|
85
|
+
- `ListCost` and `ContractedCost` equal unit price × `PricingQuantity` when both are
|
|
86
|
+
present, Correction rows included (C-011). The relative tolerance is the official
|
|
87
|
+
focus-validator's (1e-9 × max(|cost|, 1)), and the arithmetic is exact whatever the
|
|
88
|
+
caller's Decimal context. An operand whose exponent lies beyond ±1000 is reported as
|
|
89
|
+
not computable instead of being computed;
|
|
90
|
+
- a unit is null exactly when its quantity is (`PricingUnit`, `ConsumedUnit`,
|
|
91
|
+
`CommitmentDiscountUnit`, C-005/C-006);
|
|
92
|
+
- `ListUnitPrice` and `ContractedUnitPrice` are present when `SkuPriceId` is set
|
|
93
|
+
(C-013/C-015). This applies in every profile, as these rules have no applicability
|
|
94
|
+
criteria, except for a column the source did not carry.
|
|
95
|
+
`lint_focus_1_4_structure(source_absent_columns=...)` lets a producer name such
|
|
96
|
+
columns that it writes all null, and a column missing from the rows counts as absent;
|
|
97
|
+
- under a declared `SupportsUnitPricing` condition, the pricing and quantity columns
|
|
98
|
+
are null when `SkuPriceId` is null (C-009 to C-016).
|
|
99
|
+
|
|
100
|
+
Other static rules of the model are not checked. These include the pricing-currency
|
|
101
|
+
unit prices' C-012, whose presence is not visible on a row and whose model condition
|
|
102
|
+
for `PricingCurrencyContractedUnitPrice` contradicts its text.
|
|
103
|
+
**Upgrade note:** expect refusals for:
|
|
104
|
+
- provider costs rounded to the cent (exact identity), and 1.2 Correction rows, which
|
|
105
|
+
1.2 exempted from the identity;
|
|
106
|
+
- Credit rows whose `EffectiveCost` differs from `BilledCost` (C-017), which the
|
|
107
|
+
converter copies as is (Tax rows are migrated, `FDT-MIG-001`);
|
|
108
|
+
- rows with a `SkuPriceId` but a null `ListUnitPrice` or `ContractedUnitPrice` in a
|
|
109
|
+
column the source carries (C-013/C-015).
|
|
110
|
+
- **Breaking (output layout):** a unit-price column the source does not carry
|
|
111
|
+
(`ListUnitPrice`, `ContractedUnitPrice`, `PricingCurrencyListUnitPrice`,
|
|
112
|
+
`PricingCurrencyContractedUnitPrice`) is no longer written all null into the 1.4 Cost
|
|
113
|
+
and Usage output; it is omitted, as Invoice Detail already omits its unfilled
|
|
114
|
+
conditional columns. Its presence condition is not met, and written null it would break
|
|
115
|
+
the "MUST NOT be null when SkuPriceId is not null" rules in any validator, including
|
|
116
|
+
`focus-toolkit validate` on the published file. A column counts as carried when any
|
|
117
|
+
source row carries it. Sources that carry all four columns, such as the generated
|
|
118
|
+
samples, are unchanged.
|
|
119
|
+
- The client-like test fixture violated the cost identity by a factor of 1000; its
|
|
120
|
+
quantities are corrected. Its Tax row no longer carries a pricing quantity, and its
|
|
121
|
+
pricing-currency effective cost now matches its effective cost.
|
|
122
|
+
- **New byte baseline for conversion manifests.** On sources that can trigger those
|
|
123
|
+
migrations, `EffectiveCost` and the `SkuPriceId`-dependent columns are now labelled
|
|
124
|
+
`DERIVED` at column level. This follows the "weakest lineage the rule can produce"
|
|
125
|
+
convention, and `lineage_summary` records the per-value mix. Converted values of the
|
|
126
|
+
generated samples are unchanged.
|
|
127
|
+
- `ConversionError` and `ConversionCancelled` are defined in `focus_data_toolkit.convert.exceptions`
|
|
128
|
+
and re-exported unchanged from `focus_data_toolkit.convert`.
|
|
129
|
+
|
|
130
|
+
### Fixed
|
|
131
|
+
|
|
132
|
+
- `focus-toolkit convert --stream` printed each diagnostic as `note None:`; it now prints
|
|
133
|
+
its code, as the eager path does.
|
|
134
|
+
- `ContractCommitmentDurationType` is no longer rounded from `days / 30.44`, which turned
|
|
135
|
+
any span into a plausible-looking term. It is derived (`DERIVED`) only when the
|
|
136
|
+
commitment period spans a whole number of calendar months, the day clamped at month
|
|
137
|
+
end. Otherwise strict mode leaves it empty (`UNAVAILABLE`): Contract Commitment is not
|
|
138
|
+
produced until the term is supplied, and the other datasets are unaffected. Synthetic
|
|
139
|
+
mode assumes the nearest whole-month value (`ASSUMED`). `FDT-CC-001` lists the
|
|
140
|
+
commitments concerned, at the same point in the eager and streaming pipelines.
|
|
141
|
+
- The duration's lineage is settled per row. A partial supplement no longer blanks, or
|
|
142
|
+
counts as supplied, the terms derived for the rows it does not cover.
|
|
143
|
+
- A naive commitment timestamp is read as UTC, as FOCUS requires, instead of failing to
|
|
144
|
+
compare with an offset one; months are counted in the start's frame.
|
|
145
|
+
- 1.2/1.3 Cost and Usage values that break a FOCUS 1.4 rule the converter can meet without
|
|
146
|
+
inventing a fact are migrated, instead of being copied into non-conformant output. Each
|
|
147
|
+
migration is counted and reported, and each migrated value counts as `DERIVED` in
|
|
148
|
+
`lineage_summary`:
|
|
149
|
+
- Tax `EffectiveCost` equals `BilledCost` (CAU-EffectiveCost-C-017), with the exact net
|
|
150
|
+
change per billing currency (`FDT-MIG-001`). Credit rows, also covered by C-017, are not
|
|
151
|
+
rewritten;
|
|
152
|
+
- pricing and quantity columns and their units are nulled where `SkuPriceId` is null, on
|
|
153
|
+
Tax, Credit, Adjustment and Correction rows (`FDT-MIG-002`). For a 1.2 source this
|
|
154
|
+
migrates values 1.2 allowed; a 1.3 source already broke its own version's rule;
|
|
155
|
+
- on Usage/Purchase rows that are not corrections, FOCUS 1.4 requires the same columns to
|
|
156
|
+
be non-null, so their values are kept and the conflict is reported (`FDT-MIG-004`); so
|
|
157
|
+
are rows whose `ChargeCategory` is missing or not an allowed value;
|
|
158
|
+
- null source pricing-currency values are backfilled with a warning (`FDT-MIG-003`).
|
|
159
|
+
- A null `PricingCurrencyEffectiveCost` is no longer backfilled from `EffectiveCost` when
|
|
160
|
+
`PricingCurrency` differs from `BillingCurrency`, or when `BillingCurrency` is null. The
|
|
161
|
+
copy labelled a billing-currency amount with another, or an unknown, currency.
|
|
162
|
+
**Upgrade note:** a multi-currency source with such nulls used to convert; it now stops
|
|
163
|
+
with `FDT-MIG-011` (CLI exit 2). A Tax restatement across currencies stops with
|
|
164
|
+
`FDT-MIG-010`. See [conversion rules](docs/conversion-rules.md).
|
|
165
|
+
|
|
166
|
+
### Security
|
|
167
|
+
|
|
168
|
+
- The Runner image upgrades `perl-base` to 5.36.0-7+deb12u4 (bookworm-security). The
|
|
169
|
+
pinned base digest ships 5.36.0-7+deb12u3, which the container scan flags with three
|
|
170
|
+
CRITICAL (CVE-2026-13221, CVE-2026-42496, CVE-2026-8376) and four HIGH
|
|
171
|
+
(CVE-2026-42497, CVE-2026-48962, CVE-2026-57432, CVE-2026-57433) vulnerabilities.
|
|
172
|
+
- The Runner image no longer ships the `libpcre2-8-0` 10.42-1 package flagged HIGH by
|
|
173
|
+
the container scan (CVE-2026-103111, CVE-2026-86145, CVE-2026-89157, CVE-2026-89161).
|
|
174
|
+
The base image digest (`python:3.12-slim-bookworm`) is refreshed, bringing
|
|
175
|
+
10.42-1+deb12u1. The runtime stage then installs the pinned bookworm-security fix
|
|
176
|
+
10.42-1+deb12u2, which no base digest ships yet.
|
|
177
|
+
- Lock `urllib3` 2.8.0 (PYSEC-2026-4175, -4176, -4177). It reaches the lock only
|
|
178
|
+
transitively, through the optional `validator` extra (`focus-validator` -> `requests`)
|
|
179
|
+
and the release tooling (`twine`); the core toolkit has no runtime dependency.
|
|
180
|
+
## [0.13.0] — 2026-09-05
|
|
181
|
+
|
|
182
|
+
Prepared in source; publishing is a separate release step.
|
|
183
|
+
|
|
184
|
+
- Correct period-subscription effective costs and derive illustrative 10% taxes
|
|
185
|
+
from earlier untaxed usage with exact amount and currency lineage.
|
|
186
|
+
- Stabilize offer/SKU/price identities and public prices; correct recognized storage
|
|
187
|
+
properties, AWS namespaces and owning subaccounts, including allocations.
|
|
188
|
+
- Model partially used fleets of 500 machines, exact annual capacity and complete
|
|
189
|
+
budget-aware groups. Add targeted 1.3 negotiated contracts, shared contract parents
|
|
190
|
+
and a common generation registry; Contract Commitment APIs accept `include_credits`.
|
|
191
|
+
- Omit inapplicable ContractApplied metric properties while retaining legacy input
|
|
192
|
+
compatibility. Preserve exact invoice aggregates and widen affected Parquet cost
|
|
193
|
+
columns to `decimal128(38,17)` without silent rounding.
|
|
194
|
+
- Replace FAIL-only official gating with complete inventories, violation counts,
|
|
195
|
+
resource/source/data fingerprints, archived reports and separately reviewed
|
|
196
|
+
candidate evidence. Public `validate --official` now rejects false-green exits.
|
|
197
|
+
- **New byte baseline:** regenerated provider goldens, archived before examples,
|
|
198
|
+
independent corruption tests and reproducible statistics. See
|
|
199
|
+
[generator corrections](docs/generator-corrections.md) for assumptions, residual
|
|
200
|
+
official failures and the deliberate differences from upstream PRs 6 and 7.
|
|
201
|
+
|
|
12
202
|
## [0.12.0] — 2026-08-21
|
|
13
203
|
|
|
14
204
|
Back-ports the reviewed FOCUS-Sample-Data conformance fixes into the generator engine.
|
|
@@ -694,7 +884,9 @@ conformance defects.
|
|
|
694
884
|
|
|
695
885
|
<!-- Reference links. 0.2.0/0.3.0 were pre-release development milestones and were never tagged
|
|
696
886
|
or published, so only the first public release (0.9.0) has a tag link. -->
|
|
697
|
-
[Unreleased]: https://github.com/guymano/focus-data-toolkit/compare/v0.
|
|
887
|
+
[Unreleased]: https://github.com/guymano/focus-data-toolkit/compare/v0.14.0...HEAD
|
|
888
|
+
[0.14.0]: https://github.com/guymano/focus-data-toolkit/compare/v0.13.0...v0.14.0
|
|
889
|
+
[0.13.0]: https://github.com/guymano/focus-data-toolkit/compare/v0.12.0...v0.13.0
|
|
698
890
|
[0.12.0]: https://github.com/guymano/focus-data-toolkit/compare/v0.11.0...v0.12.0
|
|
699
891
|
[0.11.0]: https://github.com/guymano/focus-data-toolkit/compare/v0.11.0rc1...v0.11.0
|
|
700
892
|
[0.11.0rc1]: https://github.com/guymano/focus-data-toolkit/compare/v0.9.0...v0.11.0rc1
|
|
@@ -49,6 +49,8 @@ pytest -m packaging # build + install a wheel/sdist (need
|
|
|
49
49
|
|
|
50
50
|
python scripts/check_pinned_actions.py # every GitHub Action is SHA/digest pinned
|
|
51
51
|
python scripts/verify_model_provenance.py # the embedded FOCUS model matches its provenance
|
|
52
|
+
python scripts/describe_generated_samples.py # independently audited sample statistics
|
|
53
|
+
python scripts/validate_official_samples.py --check-existing # archived reports + provenance
|
|
52
54
|
```
|
|
53
55
|
|
|
54
56
|
A PR is expected to be green on lint, types, and the default test suite, and to
|
|
@@ -63,7 +65,8 @@ identical toolkit version and identical parameters (`provider`, `focus_version`,
|
|
|
63
65
|
- Keep generator draws in a fixed order; do not introduce a clock, real RNG, or
|
|
64
66
|
environment-dependent behaviour into generation/conversion.
|
|
65
67
|
- Golden snapshots live in `tests/fixtures/golden/`:
|
|
66
|
-
- `compatibility_golden/` —
|
|
68
|
+
- `compatibility_golden/` — reviewed synthetic outputs with executable semantic checks
|
|
69
|
+
and documented residual official-model/validator failures.
|
|
67
70
|
These are a **byte-for-byte** intra-version contract.
|
|
68
71
|
- `correctness_migration/` — cases with a *known* prior defect: the old output
|
|
69
72
|
is archived for comparison, and the **new** output is the spec-correct one.
|
|
@@ -74,6 +77,11 @@ identical toolkit version and identical parameters (`provider`, `focus_version`,
|
|
|
74
77
|
- If a diff changes a snapshot you did **not** intend to touch, treat it as a
|
|
75
78
|
regression and investigate before updating the fixture.
|
|
76
79
|
|
|
80
|
+
Generator changes also require a fresh official-validation **candidate**, review
|
|
81
|
+
of every changed rule/state/count and explicit evidence promotion. See
|
|
82
|
+
[generator corrections](docs/generator-corrections.md). A successful reference
|
|
83
|
+
comparison does not certify complete FOCUS conformance.
|
|
84
|
+
|
|
77
85
|
## Changing the embedded FOCUS model
|
|
78
86
|
|
|
79
87
|
The FOCUS 1.4 model JSON (`src/focus_data_toolkit/model/focus_1_4_model.json`) is
|
|
@@ -18,7 +18,9 @@ include tools/extract_focus_1_4_model.py
|
|
|
18
18
|
recursive-include scripts *.py
|
|
19
19
|
recursive-include schema *.json
|
|
20
20
|
recursive-include docs *.md
|
|
21
|
+
include docs/generator-statistics.json
|
|
21
22
|
recursive-include tests *.py *.csv *.json *.md
|
|
23
|
+
recursive-include tests/fixtures/official/generator_validation *.log
|
|
22
24
|
|
|
23
25
|
global-exclude *.py[cod]
|
|
24
26
|
global-exclude __pycache__
|
|
@@ -46,6 +46,17 @@ documentation page it was authored against.
|
|
|
46
46
|
|
|
47
47
|
3. Optional dependency: the official FinOps FOCUS validator
|
|
48
48
|
-----------------------------------------------------------
|
|
49
|
+
The source test evidence at tests/fixtures/official/generator_validation includes
|
|
50
|
+
rule expressions from the FOCUS 1.2.0.1 and 1.3.0.1 models (FinOps Foundation,
|
|
51
|
+
CC-BY-4.0, source and license links in section 2 above) and captured validator
|
|
52
|
+
results. Model expressions are reproduced without modification; surrounding
|
|
53
|
+
explanations and synthetic example records are authored by this project.
|
|
54
|
+
|
|
55
|
+
Generator corrections adapt the behavior examined in FOCUS-Sample-Data PRs 6/7,
|
|
56
|
+
at commits e14921a3ba8d5f75a7b899d878e372999482ba9a and
|
|
57
|
+
99917010b27f7c0ca3586f8e0daab7e1c8103dac. See docs/generator-corrections.md for
|
|
58
|
+
source links and the deliberate semantic adaptations.
|
|
59
|
+
|
|
49
60
|
The optional `[validator]` extra installs `focus-validator` from PyPI, which is
|
|
50
61
|
published by the FinOps Foundation under its own license. It is not vendored or
|
|
51
62
|
redistributed by this project; it is resolved by your package manager on
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: focus-data-toolkit
|
|
3
|
+
Version: 0.14.0
|
|
4
|
+
Summary: Generate provider-realistic FOCUS 1.2/1.3 sample data (AWS, Azure, GCP) and convert it to the four FOCUS 1.4 datasets — strictly from source facts, completed by client supplements including native AWS/Azure/GCP exports — with schema detection, gap analysis, a cross-dataset validation gate and atomic writes.
|
|
5
|
+
Author: Guy-Hermann Adiko
|
|
6
|
+
License-Expression: MIT AND CC-BY-4.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/guymano/focus-data-toolkit
|
|
8
|
+
Project-URL: Source, https://github.com/guymano/focus-data-toolkit
|
|
9
|
+
Project-URL: Issues, https://github.com/guymano/focus-data-toolkit/issues
|
|
10
|
+
Project-URL: Changelog, https://github.com/guymano/focus-data-toolkit/blob/main/CHANGELOG.md
|
|
11
|
+
Project-URL: Documentation, https://github.com/guymano/focus-data-toolkit#readme
|
|
12
|
+
Keywords: finops,focus,billing,cost,sample-data
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Office/Business :: Financial
|
|
20
|
+
Classifier: Typing :: Typed
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
License-File: NOTICE
|
|
25
|
+
License-File: LICENSES/CC-BY-4.0.txt
|
|
26
|
+
Provides-Extra: parquet
|
|
27
|
+
Requires-Dist: pyarrow<26,>=15; extra == "parquet"
|
|
28
|
+
Requires-Dist: tzdata>=2024.1; sys_platform == "win32" and extra == "parquet"
|
|
29
|
+
Provides-Extra: scale
|
|
30
|
+
Requires-Dist: focus-data-toolkit[parquet]; extra == "scale"
|
|
31
|
+
Provides-Extra: validator
|
|
32
|
+
Requires-Dist: focus-validator<3,>=2.1; python_version >= "3.12" and extra == "validator"
|
|
33
|
+
Provides-Extra: studio
|
|
34
|
+
Requires-Dist: fastapi<1,>=0.110; extra == "studio"
|
|
35
|
+
Requires-Dist: uvicorn[standard]<1,>=0.29; extra == "studio"
|
|
36
|
+
Requires-Dist: python-multipart<1,>=0.0.9; extra == "studio"
|
|
37
|
+
Provides-Extra: studio-all
|
|
38
|
+
Requires-Dist: focus-data-toolkit[parquet,studio]; extra == "studio-all"
|
|
39
|
+
Provides-Extra: all
|
|
40
|
+
Requires-Dist: focus-data-toolkit[parquet,studio,validator]; extra == "all"
|
|
41
|
+
Provides-Extra: release
|
|
42
|
+
Requires-Dist: build<2,>=1; extra == "release"
|
|
43
|
+
Requires-Dist: twine<8,>=5; extra == "release"
|
|
44
|
+
Provides-Extra: dev
|
|
45
|
+
Requires-Dist: focus-data-toolkit[parquet,release,studio]; extra == "dev"
|
|
46
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
47
|
+
Requires-Dist: pytest-cov<8,>=5; extra == "dev"
|
|
48
|
+
Requires-Dist: ruff>=0.6; extra == "dev"
|
|
49
|
+
Requires-Dist: mypy<2.5,>=1.11; extra == "dev"
|
|
50
|
+
Requires-Dist: jsonschema<5,>=4.21; extra == "dev"
|
|
51
|
+
Requires-Dist: httpx<1,>=0.27; extra == "dev"
|
|
52
|
+
Dynamic: license-file
|
|
53
|
+
|
|
54
|
+
# focus-data-toolkit
|
|
55
|
+
|
|
56
|
+
Generate realistic **FOCUS 1.2 / 1.3** cost and usage data (AWS, Azure, GCP), convert it to
|
|
57
|
+
**FOCUS 1.4**, and validate the result. One dependency-free Python core, usable three ways.
|
|
58
|
+
|
|
59
|
+
[](https://pypi.org/project/focus-data-toolkit/)
|
|
60
|
+
[](https://pypi.org/project/focus-data-toolkit/)
|
|
61
|
+
[](LICENSE)
|
|
62
|
+
[](https://github.com/guymano/focus-data-toolkit/actions/workflows/ci.yml)
|
|
63
|
+
|
|
64
|
+
[FOCUS](https://focus.finops.org) is the open standard for cloud cost and usage data. FOCUS 1.4 defines four datasets: Cost and Usage, Contract Commitment, Billing Period, and Invoice Detail. The toolkit is honest by design: any value it cannot derive from your source is left empty (**strict** mode) or filled with clearly labelled assumptions (**synthetic** mode).
|
|
65
|
+
|
|
66
|
+
The source prepares **0.14.0**, which converts FOCUS 1.2/1.3 sources toward 1.4 more
|
|
67
|
+
faithfully:
|
|
68
|
+
- values are migrated to the 1.4 rules, and a stricter 1.4 lint checks the output;
|
|
69
|
+
- commitment terms can be supplied explicitly;
|
|
70
|
+
- Azure reservation and savings plan orders have adapters;
|
|
71
|
+
- a 1.2 source can declare who issued it, to name its Service Provider.
|
|
72
|
+
|
|
73
|
+
See the [conversion rules](docs/conversion-rules.md) and the [changelog](CHANGELOG.md).
|
|
74
|
+
The [generator corrections and validation evidence](docs/generator-corrections.md)
|
|
75
|
+
describe the generated samples, unchanged since 0.13.0. The Docker examples below
|
|
76
|
+
continue to pin the previously released 0.12.0 image.
|
|
77
|
+
|
|
78
|
+
## Three ways to use it
|
|
79
|
+
|
|
80
|
+
| Interface | Best for | Get started |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| **Studio** | FinOps users who prefer a UI | `pip install "focus-data-toolkit[studio]"` then `focus-toolkit ui` |
|
|
83
|
+
| **Runner** | Automation and large volumes | `docker run --rm ghcr.io/guymano/focus-data-toolkit:0.12.0 version` |
|
|
84
|
+
| **CLI & SDK** | Engineers and scripts | `pip install focus-data-toolkit` then `focus-toolkit --help` |
|
|
85
|
+
|
|
86
|
+
All three run the same core, so their outputs are byte-for-byte identical. Studio is a local web app (binds `127.0.0.1`, token-guarded, no data leaves your machine, see [docs/studio.md](docs/studio.md)). Runner is a non-root OCI image whose entrypoint is the CLI (see [docs/runner.md](docs/runner.md)).
|
|
87
|
+
|
|
88
|
+
## Install
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
pip install focus-data-toolkit
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The core needs only the standard library (Python 3.11+). Optional extras:
|
|
95
|
+
|
|
96
|
+
| Extra | Adds |
|
|
97
|
+
|---|---|
|
|
98
|
+
| `parquet` | Parquet I/O: columnar, decimal128, Hive partitioning (alias: `scale`) |
|
|
99
|
+
| `studio` | the local web UI (`focus-toolkit ui`) |
|
|
100
|
+
| `studio-all` | `studio` + `parquet` |
|
|
101
|
+
| `validator` | the official FinOps validator (`--official`, needs Python 3.12+) |
|
|
102
|
+
| `all` | everything above |
|
|
103
|
+
|
|
104
|
+
Also available via `pipx install`, `uv tool install`, or `docker pull ghcr.io/guymano/focus-data-toolkit:0.12.0`.
|
|
105
|
+
|
|
106
|
+
## Quickstart
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# 1. Generate sample data (aws|azure|gcp, FOCUS 1.2|1.3)
|
|
110
|
+
focus-toolkit generate --provider aws --focus-version 1.3 --rows 1000 --out ./out
|
|
111
|
+
|
|
112
|
+
# 2. Convert a 1.2/1.3 Cost and Usage file to FOCUS 1.4 (source version auto-detected)
|
|
113
|
+
focus-toolkit convert --cost-and-usage out/focus_1_3_cost_and_usage_aws.csv --out ./focus-1.4
|
|
114
|
+
|
|
115
|
+
# 3. Open the Studio web app (needs the studio extra)
|
|
116
|
+
focus-toolkit ui
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Or use it as a Python library, the same engine behind all three interfaces:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
from focus_data_toolkit import convert_files
|
|
123
|
+
|
|
124
|
+
convert_files("cost_and_usage.csv", "focus-1.4", mode="strict")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Everything a library caller needs is importable from the package root, including
|
|
128
|
+
supplements and the failures to handle:
|
|
129
|
+
|
|
130
|
+
```python
|
|
131
|
+
from pathlib import Path
|
|
132
|
+
|
|
133
|
+
from focus_data_toolkit import (
|
|
134
|
+
AtomicWriteError, ConversionError, Mode, SupplementBundle, SupplementError,
|
|
135
|
+
SupplementFileSpec, convert_files,
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
try:
|
|
139
|
+
bundle = SupplementBundle.load([SupplementFileSpec(path=Path("invoices.json"))])
|
|
140
|
+
convert_files("cost_and_usage.csv", "focus-1.4", mode=Mode.STRICT, supplements=bundle)
|
|
141
|
+
except (ConversionError, SupplementError):
|
|
142
|
+
... # invalid source or unusable supplement: nothing was written
|
|
143
|
+
except AtomicWriteError:
|
|
144
|
+
... # the lint or cross-dataset gate refused publication: nothing was written
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## What it does, and what it doesn't
|
|
148
|
+
|
|
149
|
+
- Converts FOCUS 1.2/1.3 Cost and Usage into the four FOCUS 1.4 datasets. Values a 1.4 rule
|
|
150
|
+
fixes deterministically are migrated and reported (`FDT-MIG-*`). Values it cannot fix without
|
|
151
|
+
inventing a fact are refused. See [docs/conversion-rules.md](docs/conversion-rules.md).
|
|
152
|
+
- The three other datasets come only from [supplements](docs/supplements.md) or synthetic mode.
|
|
153
|
+
There is no FOCUS 1.4 generator and no invalid-data generator.
|
|
154
|
+
- Built for real data: bounded-memory streaming (`--stream`), atomic writes, a deterministic
|
|
155
|
+
manifest with per-column lineage, structured `FDT-*` diagnostics, cross-dataset validation.
|
|
156
|
+
- Single-node engine. See [docs/compatibility.md](docs/compatibility.md) for the support matrix.
|
|
157
|
+
|
|
158
|
+
## Exit codes
|
|
159
|
+
|
|
160
|
+
| Code | Meaning |
|
|
161
|
+
|---|---|
|
|
162
|
+
| `0` | success, no assumptions |
|
|
163
|
+
| `1` | lint / validation / write failure |
|
|
164
|
+
| `2` | invalid input or arguments |
|
|
165
|
+
| `3` | strict result intentionally incomplete |
|
|
166
|
+
| `4` | synthetic result contains assumptions |
|
|
167
|
+
| `5` | disk space or budget exhausted (streaming / Runner) |
|
|
168
|
+
| `130` | cancelled (Ctrl-C / SIGTERM), nothing partial is published |
|
|
169
|
+
|
|
170
|
+
Add `--exit-policy pipeline` in `set -e` or CI scripts to treat codes `3` and `4` as `0`.
|
|
171
|
+
|
|
172
|
+
## Trust
|
|
173
|
+
|
|
174
|
+
- Deterministic: same input, same output bytes, all listed in `SHA256SUMS`.
|
|
175
|
+
- The embedded FOCUS 1.4 model is hash-pinned to the FinOps source workbook; see [docs/model-provenance.md](docs/model-provenance.md).
|
|
176
|
+
- Releases are reproducible, signed (Sigstore/cosign), with SBOM and attestations; see [docs/releasing.md](docs/releasing.md).
|
|
177
|
+
- Private: no network I/O, no telemetry, no credentials; see [docs/security-model.md](docs/security-model.md).
|
|
178
|
+
|
|
179
|
+
## Documentation
|
|
180
|
+
|
|
181
|
+
[Studio](docs/studio.md) · [Runner](docs/runner.md) · [Supplements](docs/supplements.md) · [Compatibility](docs/compatibility.md) · [Versioning](docs/versioning.md) · [Security model](docs/security-model.md) · [Model provenance](docs/model-provenance.md) · [Releasing](docs/releasing.md) · [Changelog](CHANGELOG.md) · [Contributing](CONTRIBUTING.md) · [Security policy](SECURITY.md)
|
|
182
|
+
|
|
183
|
+
## License
|
|
184
|
+
|
|
185
|
+
Code is MIT (see [LICENSE](LICENSE)). The embedded FOCUS 1.4 data model derives from the FinOps FOCUS workbook, © the FinOps Foundation, CC-BY-4.0 (see [NOTICE](NOTICE)). "FOCUS" and "FinOps" are FinOps Foundation trademarks; this is an independent community project, not endorsed by the FinOps Foundation.
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# focus-data-toolkit
|
|
2
|
+
|
|
3
|
+
Generate realistic **FOCUS 1.2 / 1.3** cost and usage data (AWS, Azure, GCP), convert it to
|
|
4
|
+
**FOCUS 1.4**, and validate the result. One dependency-free Python core, usable three ways.
|
|
5
|
+
|
|
6
|
+
[](https://pypi.org/project/focus-data-toolkit/)
|
|
7
|
+
[](https://pypi.org/project/focus-data-toolkit/)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://github.com/guymano/focus-data-toolkit/actions/workflows/ci.yml)
|
|
10
|
+
|
|
11
|
+
[FOCUS](https://focus.finops.org) is the open standard for cloud cost and usage data. FOCUS 1.4 defines four datasets: Cost and Usage, Contract Commitment, Billing Period, and Invoice Detail. The toolkit is honest by design: any value it cannot derive from your source is left empty (**strict** mode) or filled with clearly labelled assumptions (**synthetic** mode).
|
|
12
|
+
|
|
13
|
+
The source prepares **0.14.0**, which converts FOCUS 1.2/1.3 sources toward 1.4 more
|
|
14
|
+
faithfully:
|
|
15
|
+
- values are migrated to the 1.4 rules, and a stricter 1.4 lint checks the output;
|
|
16
|
+
- commitment terms can be supplied explicitly;
|
|
17
|
+
- Azure reservation and savings plan orders have adapters;
|
|
18
|
+
- a 1.2 source can declare who issued it, to name its Service Provider.
|
|
19
|
+
|
|
20
|
+
See the [conversion rules](docs/conversion-rules.md) and the [changelog](CHANGELOG.md).
|
|
21
|
+
The [generator corrections and validation evidence](docs/generator-corrections.md)
|
|
22
|
+
describe the generated samples, unchanged since 0.13.0. The Docker examples below
|
|
23
|
+
continue to pin the previously released 0.12.0 image.
|
|
24
|
+
|
|
25
|
+
## Three ways to use it
|
|
26
|
+
|
|
27
|
+
| Interface | Best for | Get started |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| **Studio** | FinOps users who prefer a UI | `pip install "focus-data-toolkit[studio]"` then `focus-toolkit ui` |
|
|
30
|
+
| **Runner** | Automation and large volumes | `docker run --rm ghcr.io/guymano/focus-data-toolkit:0.12.0 version` |
|
|
31
|
+
| **CLI & SDK** | Engineers and scripts | `pip install focus-data-toolkit` then `focus-toolkit --help` |
|
|
32
|
+
|
|
33
|
+
All three run the same core, so their outputs are byte-for-byte identical. Studio is a local web app (binds `127.0.0.1`, token-guarded, no data leaves your machine, see [docs/studio.md](docs/studio.md)). Runner is a non-root OCI image whose entrypoint is the CLI (see [docs/runner.md](docs/runner.md)).
|
|
34
|
+
|
|
35
|
+
## Install
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
pip install focus-data-toolkit
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The core needs only the standard library (Python 3.11+). Optional extras:
|
|
42
|
+
|
|
43
|
+
| Extra | Adds |
|
|
44
|
+
|---|---|
|
|
45
|
+
| `parquet` | Parquet I/O: columnar, decimal128, Hive partitioning (alias: `scale`) |
|
|
46
|
+
| `studio` | the local web UI (`focus-toolkit ui`) |
|
|
47
|
+
| `studio-all` | `studio` + `parquet` |
|
|
48
|
+
| `validator` | the official FinOps validator (`--official`, needs Python 3.12+) |
|
|
49
|
+
| `all` | everything above |
|
|
50
|
+
|
|
51
|
+
Also available via `pipx install`, `uv tool install`, or `docker pull ghcr.io/guymano/focus-data-toolkit:0.12.0`.
|
|
52
|
+
|
|
53
|
+
## Quickstart
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
# 1. Generate sample data (aws|azure|gcp, FOCUS 1.2|1.3)
|
|
57
|
+
focus-toolkit generate --provider aws --focus-version 1.3 --rows 1000 --out ./out
|
|
58
|
+
|
|
59
|
+
# 2. Convert a 1.2/1.3 Cost and Usage file to FOCUS 1.4 (source version auto-detected)
|
|
60
|
+
focus-toolkit convert --cost-and-usage out/focus_1_3_cost_and_usage_aws.csv --out ./focus-1.4
|
|
61
|
+
|
|
62
|
+
# 3. Open the Studio web app (needs the studio extra)
|
|
63
|
+
focus-toolkit ui
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Or use it as a Python library, the same engine behind all three interfaces:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from focus_data_toolkit import convert_files
|
|
70
|
+
|
|
71
|
+
convert_files("cost_and_usage.csv", "focus-1.4", mode="strict")
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Everything a library caller needs is importable from the package root, including
|
|
75
|
+
supplements and the failures to handle:
|
|
76
|
+
|
|
77
|
+
```python
|
|
78
|
+
from pathlib import Path
|
|
79
|
+
|
|
80
|
+
from focus_data_toolkit import (
|
|
81
|
+
AtomicWriteError, ConversionError, Mode, SupplementBundle, SupplementError,
|
|
82
|
+
SupplementFileSpec, convert_files,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
bundle = SupplementBundle.load([SupplementFileSpec(path=Path("invoices.json"))])
|
|
87
|
+
convert_files("cost_and_usage.csv", "focus-1.4", mode=Mode.STRICT, supplements=bundle)
|
|
88
|
+
except (ConversionError, SupplementError):
|
|
89
|
+
... # invalid source or unusable supplement: nothing was written
|
|
90
|
+
except AtomicWriteError:
|
|
91
|
+
... # the lint or cross-dataset gate refused publication: nothing was written
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## What it does, and what it doesn't
|
|
95
|
+
|
|
96
|
+
- Converts FOCUS 1.2/1.3 Cost and Usage into the four FOCUS 1.4 datasets. Values a 1.4 rule
|
|
97
|
+
fixes deterministically are migrated and reported (`FDT-MIG-*`). Values it cannot fix without
|
|
98
|
+
inventing a fact are refused. See [docs/conversion-rules.md](docs/conversion-rules.md).
|
|
99
|
+
- The three other datasets come only from [supplements](docs/supplements.md) or synthetic mode.
|
|
100
|
+
There is no FOCUS 1.4 generator and no invalid-data generator.
|
|
101
|
+
- Built for real data: bounded-memory streaming (`--stream`), atomic writes, a deterministic
|
|
102
|
+
manifest with per-column lineage, structured `FDT-*` diagnostics, cross-dataset validation.
|
|
103
|
+
- Single-node engine. See [docs/compatibility.md](docs/compatibility.md) for the support matrix.
|
|
104
|
+
|
|
105
|
+
## Exit codes
|
|
106
|
+
|
|
107
|
+
| Code | Meaning |
|
|
108
|
+
|---|---|
|
|
109
|
+
| `0` | success, no assumptions |
|
|
110
|
+
| `1` | lint / validation / write failure |
|
|
111
|
+
| `2` | invalid input or arguments |
|
|
112
|
+
| `3` | strict result intentionally incomplete |
|
|
113
|
+
| `4` | synthetic result contains assumptions |
|
|
114
|
+
| `5` | disk space or budget exhausted (streaming / Runner) |
|
|
115
|
+
| `130` | cancelled (Ctrl-C / SIGTERM), nothing partial is published |
|
|
116
|
+
|
|
117
|
+
Add `--exit-policy pipeline` in `set -e` or CI scripts to treat codes `3` and `4` as `0`.
|
|
118
|
+
|
|
119
|
+
## Trust
|
|
120
|
+
|
|
121
|
+
- Deterministic: same input, same output bytes, all listed in `SHA256SUMS`.
|
|
122
|
+
- The embedded FOCUS 1.4 model is hash-pinned to the FinOps source workbook; see [docs/model-provenance.md](docs/model-provenance.md).
|
|
123
|
+
- Releases are reproducible, signed (Sigstore/cosign), with SBOM and attestations; see [docs/releasing.md](docs/releasing.md).
|
|
124
|
+
- Private: no network I/O, no telemetry, no credentials; see [docs/security-model.md](docs/security-model.md).
|
|
125
|
+
|
|
126
|
+
## Documentation
|
|
127
|
+
|
|
128
|
+
[Studio](docs/studio.md) · [Runner](docs/runner.md) · [Supplements](docs/supplements.md) · [Compatibility](docs/compatibility.md) · [Versioning](docs/versioning.md) · [Security model](docs/security-model.md) · [Model provenance](docs/model-provenance.md) · [Releasing](docs/releasing.md) · [Changelog](CHANGELOG.md) · [Contributing](CONTRIBUTING.md) · [Security policy](SECURITY.md)
|
|
129
|
+
|
|
130
|
+
## License
|
|
131
|
+
|
|
132
|
+
Code is MIT (see [LICENSE](LICENSE)). The embedded FOCUS 1.4 data model derives from the FinOps FOCUS workbook, © the FinOps Foundation, CC-BY-4.0 (see [NOTICE](NOTICE)). "FOCUS" and "FinOps" are FinOps Foundation trademarks; this is an independent community project, not endorsed by the FinOps Foundation.
|