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.
Files changed (306) hide show
  1. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/CHANGELOG.md +193 -1
  2. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/CONTRIBUTING.md +9 -1
  3. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/MANIFEST.in +2 -0
  4. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/NOTICE +11 -0
  5. focus_data_toolkit-0.14.0/PKG-INFO +185 -0
  6. focus_data_toolkit-0.14.0/README.md +132 -0
  7. focus_data_toolkit-0.14.0/docs/conversion-rules.md +176 -0
  8. focus_data_toolkit-0.14.0/docs/generator-corrections.md +198 -0
  9. focus_data_toolkit-0.14.0/docs/generator-statistics.json +77 -0
  10. focus_data_toolkit-0.14.0/docs/pr53-review-followup.md +61 -0
  11. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/supplements.md +28 -4
  12. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/versioning.md +17 -2
  13. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/pyproject.toml +4 -1
  14. focus_data_toolkit-0.14.0/scripts/__init__.py +1 -0
  15. focus_data_toolkit-0.14.0/scripts/describe_generated_samples.py +42 -0
  16. focus_data_toolkit-0.14.0/scripts/sample_audit.py +162 -0
  17. focus_data_toolkit-0.14.0/scripts/validate_official_samples.py +354 -0
  18. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/__init__.py +27 -0
  19. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/_version.py +1 -1
  20. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/cli.py +40 -4
  21. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/context/__init__.py +9 -3
  22. focus_data_toolkit-0.14.0/src/focus_data_toolkit/context/provider.py +173 -0
  23. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/convert/__init__.py +46 -23
  24. focus_data_toolkit-0.14.0/src/focus_data_toolkit/convert/contract_commitment.py +296 -0
  25. focus_data_toolkit-0.14.0/src/focus_data_toolkit/convert/cost_and_usage.py +732 -0
  26. focus_data_toolkit-0.14.0/src/focus_data_toolkit/convert/exceptions.py +20 -0
  27. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/convert/invoice_detail.py +4 -1
  28. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/convert/streaming.py +42 -9
  29. focus_data_toolkit-0.14.0/src/focus_data_toolkit/generators/engine/context.py +36 -0
  30. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/engine/determinism.py +35 -21
  31. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/engine/json_focus.py +11 -10
  32. focus_data_toolkit-0.14.0/src/focus_data_toolkit/generators/engine/ladder.py +86 -0
  33. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/engine/scenarios_core.py +68 -53
  34. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/engine/serialize.py +67 -50
  35. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/providers/aws.py +6 -9
  36. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/providers/azure.py +1 -6
  37. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/providers/gcp.py +0 -5
  38. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/providers/profile.py +2 -2
  39. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/versions/v1_3.py +9 -25
  40. focus_data_toolkit-0.14.0/src/focus_data_toolkit/model/focus_1_4_decimal_scale.json +49 -0
  41. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/validator.py +164 -8
  42. focus_data_toolkit-0.14.0/src/focus_data_toolkit/official_report.py +66 -0
  43. focus_data_toolkit-0.14.0/src/focus_data_toolkit/official_validator.py +117 -0
  44. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/adapters_provenance.json +18 -2
  45. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/aws_savings_plans.json +7 -5
  46. focus_data_toolkit-0.14.0/src/focus_data_toolkit/supplement/adapters/azure_reservation_orders.json +33 -0
  47. focus_data_toolkit-0.14.0/src/focus_data_toolkit/supplement/adapters/azure_savings_plan_orders.json +35 -0
  48. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/registry.py +45 -0
  49. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/apply.py +2 -0
  50. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/gaps.py +18 -1
  51. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/kinds.py +4 -1
  52. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/loader.py +10 -1
  53. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/codes.py +22 -0
  54. focus_data_toolkit-0.14.0/src/focus_data_toolkit.egg-info/PKG-INFO +185 -0
  55. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit.egg-info/SOURCES.txt +41 -0
  56. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit.egg-info/requires.txt +1 -1
  57. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/client_like/SOURCES.md +1 -1
  58. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/client_like/consolidated_multi_provider_1_3.csv +4 -4
  59. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/golden/README.md +8 -2
  60. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_2_cost_and_usage_rows100_seed42.csv +101 -0
  61. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_2_cost_and_usage_rows25_seed7.csv +26 -0
  62. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_2_cost_and_usage_rows25_seed7_credits.csv +26 -0
  63. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_3_contract_commitment_rows100_seed42.csv +12 -0
  64. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_3_cost_and_usage_rows100_seed42.csv +101 -0
  65. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_3_cost_and_usage_rows25_seed7.csv +26 -0
  66. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/aws_1_3_cost_and_usage_rows25_seed7_credits.csv +26 -0
  67. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_2_cost_and_usage_rows100_seed42.csv +101 -0
  68. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_2_cost_and_usage_rows25_seed7.csv +26 -0
  69. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_2_cost_and_usage_rows25_seed7_credits.csv +26 -0
  70. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_3_contract_commitment_rows100_seed42.csv +10 -0
  71. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_3_cost_and_usage_rows100_seed42.csv +101 -0
  72. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_3_cost_and_usage_rows25_seed7.csv +26 -0
  73. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/azure_1_3_cost_and_usage_rows25_seed7_credits.csv +26 -0
  74. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_2_cost_and_usage_rows100_seed42.csv +101 -0
  75. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_2_cost_and_usage_rows25_seed7.csv +26 -0
  76. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_2_cost_and_usage_rows25_seed7_credits.csv +26 -0
  77. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_contract_commitment_rows100_seed42.csv +12 -0
  78. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_cost_and_usage_rows100_seed42.csv +101 -0
  79. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_cost_and_usage_rows25_seed7.csv +26 -0
  80. focus_data_toolkit-0.14.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_cost_and_usage_rows25_seed7_credits.csv +26 -0
  81. focus_data_toolkit-0.14.0/tests/fixtures/golden/correctness_migration/README.md +10 -0
  82. focus_data_toolkit-0.14.0/tests/fixtures/golden/correctness_migration/pr67_before.json +815 -0
  83. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/README.md +15 -0
  84. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/aws_1.2_CostAndUsage.log +625 -0
  85. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/aws_1.2_CostAndUsage.stderr.log +0 -0
  86. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/aws_1.3_ContractCommitment.log +117 -0
  87. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/aws_1.3_ContractCommitment.stderr.log +0 -0
  88. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/aws_1.3_CostAndUsage.log +865 -0
  89. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/aws_1.3_CostAndUsage.stderr.log +0 -0
  90. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/azure_1.2_CostAndUsage.log +617 -0
  91. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/azure_1.2_CostAndUsage.stderr.log +0 -0
  92. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/azure_1.3_ContractCommitment.log +117 -0
  93. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/azure_1.3_ContractCommitment.stderr.log +0 -0
  94. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/azure_1.3_CostAndUsage.log +857 -0
  95. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/azure_1.3_CostAndUsage.stderr.log +0 -0
  96. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/baseline.json +35853 -0
  97. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/gcp_1.2_CostAndUsage.log +617 -0
  98. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/gcp_1.2_CostAndUsage.stderr.log +0 -0
  99. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/gcp_1.3_ContractCommitment.log +117 -0
  100. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/gcp_1.3_ContractCommitment.stderr.log +0 -0
  101. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/gcp_1.3_CostAndUsage.log +857 -0
  102. focus_data_toolkit-0.14.0/tests/fixtures/official/generator_validation/gcp_1.3_CostAndUsage.stderr.log +0 -0
  103. focus_data_toolkit-0.14.0/tests/test_adapters_aws.py +321 -0
  104. focus_data_toolkit-0.14.0/tests/test_adapters_azure_gcp.py +402 -0
  105. focus_data_toolkit-0.14.0/tests/test_contract_commitment_duration.py +357 -0
  106. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_contract_commitment_semantics.py +17 -7
  107. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_convert_roundtrip.py +1 -1
  108. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_cross_provider.py +2 -2
  109. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_fake_provider.py +0 -1
  110. focus_data_toolkit-0.14.0/tests/test_focus_1_4_value_migrations.py +276 -0
  111. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_generated_conformance.py +35 -17
  112. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_generators.py +3 -1
  113. focus_data_toolkit-0.14.0/tests/test_lint_focus_1_4_rules.py +322 -0
  114. focus_data_toolkit-0.14.0/tests/test_official_report.py +146 -0
  115. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_official_validator.py +2 -2
  116. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_parquet.py +3 -3
  117. focus_data_toolkit-0.14.0/tests/test_provider_roles_1_2.py +230 -0
  118. focus_data_toolkit-0.14.0/tests/test_public_api.py +83 -0
  119. focus_data_toolkit-0.14.0/tests/test_reviewed_roundtrip.py +78 -0
  120. focus_data_toolkit-0.14.0/tests/test_reviewed_sample_semantics.py +150 -0
  121. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_streaming.py +63 -0
  122. focus_data_toolkit-0.12.0/PKG-INFO +0 -196
  123. focus_data_toolkit-0.12.0/README.md +0 -143
  124. focus_data_toolkit-0.12.0/scripts/validate_official_samples.py +0 -353
  125. focus_data_toolkit-0.12.0/src/focus_data_toolkit/context/provider.py +0 -90
  126. focus_data_toolkit-0.12.0/src/focus_data_toolkit/convert/contract_commitment.py +0 -182
  127. focus_data_toolkit-0.12.0/src/focus_data_toolkit/convert/cost_and_usage.py +0 -212
  128. focus_data_toolkit-0.12.0/src/focus_data_toolkit/generators/engine/context.py +0 -12
  129. focus_data_toolkit-0.12.0/src/focus_data_toolkit/generators/engine/ladder.py +0 -71
  130. focus_data_toolkit-0.12.0/src/focus_data_toolkit/model/focus_1_4_decimal_scale.json +0 -10
  131. focus_data_toolkit-0.12.0/src/focus_data_toolkit/official_validator.py +0 -61
  132. focus_data_toolkit-0.12.0/src/focus_data_toolkit.egg-info/PKG-INFO +0 -196
  133. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_2_cost_and_usage_rows100_seed42.csv +0 -101
  134. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_2_cost_and_usage_rows25_seed7.csv +0 -26
  135. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_2_cost_and_usage_rows25_seed7_credits.csv +0 -26
  136. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_3_contract_commitment_rows100_seed42.csv +0 -11
  137. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_3_cost_and_usage_rows100_seed42.csv +0 -101
  138. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_3_cost_and_usage_rows25_seed7.csv +0 -26
  139. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/aws_1_3_cost_and_usage_rows25_seed7_credits.csv +0 -26
  140. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_2_cost_and_usage_rows100_seed42.csv +0 -101
  141. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_2_cost_and_usage_rows25_seed7.csv +0 -26
  142. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_2_cost_and_usage_rows25_seed7_credits.csv +0 -26
  143. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_3_contract_commitment_rows100_seed42.csv +0 -11
  144. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_3_cost_and_usage_rows100_seed42.csv +0 -101
  145. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_3_cost_and_usage_rows25_seed7.csv +0 -26
  146. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/azure_1_3_cost_and_usage_rows25_seed7_credits.csv +0 -26
  147. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_2_cost_and_usage_rows100_seed42.csv +0 -101
  148. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_2_cost_and_usage_rows25_seed7.csv +0 -26
  149. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_2_cost_and_usage_rows25_seed7_credits.csv +0 -26
  150. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_contract_commitment_rows100_seed42.csv +0 -12
  151. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_cost_and_usage_rows100_seed42.csv +0 -101
  152. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_cost_and_usage_rows25_seed7.csv +0 -26
  153. focus_data_toolkit-0.12.0/tests/fixtures/golden/compatibility_golden/gcp_1_3_cost_and_usage_rows25_seed7_credits.csv +0 -26
  154. focus_data_toolkit-0.12.0/tests/test_adapters_aws.py +0 -179
  155. focus_data_toolkit-0.12.0/tests/test_adapters_azure_gcp.py +0 -142
  156. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/LICENSE +0 -0
  157. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/LICENSES/CC-BY-4.0.txt +0 -0
  158. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/SECURITY.md +0 -0
  159. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/audit/2026-07-improvement-plan.md +0 -0
  160. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/audit/2026-07-repo-audit.md +0 -0
  161. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/compatibility.md +0 -0
  162. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/model-provenance.md +0 -0
  163. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/releasing.md +0 -0
  164. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/runner.md +0 -0
  165. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/security-model.md +0 -0
  166. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/docs/studio.md +0 -0
  167. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/schema/model_provenance.schema.json +0 -0
  168. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/scripts/check_pinned_actions.py +0 -0
  169. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/scripts/generate_resolved_sbom.py +0 -0
  170. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/scripts/generate_sbom.py +0 -0
  171. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/scripts/regenerate_golden_fixtures.py +0 -0
  172. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/scripts/verify_model_provenance.py +0 -0
  173. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/scripts/verify_release.py +0 -0
  174. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/setup.cfg +0 -0
  175. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/__main__.py +0 -0
  176. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/context/billing.py +0 -0
  177. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/convert/billing_period.py +0 -0
  178. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/convert/contract_applied.py +0 -0
  179. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/convert/detect.py +0 -0
  180. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/errors.py +0 -0
  181. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/focus_json.py +0 -0
  182. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/__init__.py +0 -0
  183. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/_shim.py +0 -0
  184. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/engine/__init__.py +0 -0
  185. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/engine/allocation_math.py +0 -0
  186. {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
  187. {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
  188. {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
  189. {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
  190. {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
  191. {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
  192. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/providers/__init__.py +0 -0
  193. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/scenarios.py +0 -0
  194. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/versions/__init__.py +0 -0
  195. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/versions/adapter.py +0 -0
  196. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/generators/versions/v1_2.py +0 -0
  197. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/io/__init__.py +0 -0
  198. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/io/atomic_writer.py +0 -0
  199. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/io/csv_io.py +0 -0
  200. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/io/parquet_io.py +0 -0
  201. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/io/records.py +0 -0
  202. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/io/row_source.py +0 -0
  203. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/lifecycle.py +0 -0
  204. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/manifest.py +0 -0
  205. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/__init__.py +0 -0
  206. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/capabilities.py +0 -0
  207. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/focus_1_4_model.json +0 -0
  208. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/focus_1_4_servicesubcategory.json +0 -0
  209. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/focus_json_keys.py +0 -0
  210. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/iso_4217_currencies.json +0 -0
  211. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/json_schema_check.py +0 -0
  212. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/json_schemas/allocatedmethoddetailsobjectschema.json +0 -0
  213. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/json_schemas/commitmentprogrameligibilitydetailsobjectschema.json +0 -0
  214. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/json_schemas/contractappliedobjectschema.json +0 -0
  215. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/json_schemas/contractcommitmentapplicabilityobjectschema.json +0 -0
  216. {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
  217. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/model/model_provenance.json +0 -0
  218. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/modes.py +0 -0
  219. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/progress.py +0 -0
  220. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/provenance.py +0 -0
  221. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/py.typed +0 -0
  222. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/runtime.py +0 -0
  223. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/schema/__init__.py +0 -0
  224. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/schema/detection.py +0 -0
  225. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/schema/registry.py +0 -0
  226. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/storage/__init__.py +0 -0
  227. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/storage/external_index.py +0 -0
  228. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/storage/spill.py +0 -0
  229. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/__init__.py +0 -0
  230. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/app.py +0 -0
  231. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/config.py +0 -0
  232. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/frontend/app.js +0 -0
  233. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/frontend/index.html +0 -0
  234. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/frontend/style.css +0 -0
  235. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/jobs.py +0 -0
  236. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/preview.py +0 -0
  237. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/security.py +0 -0
  238. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/studio/server.py +0 -0
  239. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/__init__.py +0 -0
  240. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/__init__.py +0 -0
  241. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/aws_invoice_summary.json +0 -0
  242. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/azure_invoice.json +0 -0
  243. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/adapters/gcp_compute_commitments.json +0 -0
  244. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/spec.py +0 -0
  245. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/supplement/validate.py +0 -0
  246. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/__init__.py +0 -0
  247. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/allocation.py +0 -0
  248. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/bundle.py +0 -0
  249. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/corrections.py +0 -0
  250. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/reconciliation.py +0 -0
  251. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit/validate/referential.py +0 -0
  252. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit.egg-info/dependency_links.txt +0 -0
  253. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit.egg-info/entry_points.txt +0 -0
  254. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/src/focus_data_toolkit.egg-info/top_level.txt +0 -0
  255. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/conftest.py +0 -0
  256. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/golden/compatibility_golden/scenarios_correction_set.csv +0 -0
  257. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/golden/compatibility_golden/scenarios_sca_equal.csv +0 -0
  258. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/golden/compatibility_golden/scenarios_sca_negative.csv +0 -0
  259. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/golden/compatibility_golden/scenarios_sca_weighted.csv +0 -0
  260. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/official/SOURCES.md +0 -0
  261. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/official/invoice_detail_grain_example.json +0 -0
  262. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/fixtures/official/numeric_format_examples.json +0 -0
  263. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_atomic_write.py +0 -0
  264. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_billing_lifecycle.py +0 -0
  265. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_bundle_gate.py +0 -0
  266. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_bundle_validation.py +0 -0
  267. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_capabilities.py +0 -0
  268. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_cli.py +0 -0
  269. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_client_like.py +0 -0
  270. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_container.py +0 -0
  271. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_contract_applied.py +0 -0
  272. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_corrections.py +0 -0
  273. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_detect.py +0 -0
  274. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_fixtures_are_synthetic.py +0 -0
  275. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_focus_json.py +0 -0
  276. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_gaps.py +0 -0
  277. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_generator_golden.py +0 -0
  278. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_grouping_keys.py +0 -0
  279. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_lifecycle_chains.py +0 -0
  280. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_lineage_counters.py +0 -0
  281. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_lint_focus.py +0 -0
  282. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_manifest.py +0 -0
  283. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_model_provenance.py +0 -0
  284. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_multi_provider.py +0 -0
  285. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_official_json_schemas.py +0 -0
  286. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_packaging.py +0 -0
  287. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_parquet_input.py +0 -0
  288. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_participant_entities.py +0 -0
  289. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_partitioning.py +0 -0
  290. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_progress_cancel.py +0 -0
  291. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_release_tooling.py +0 -0
  292. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_replace_recovery.py +0 -0
  293. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_runtime.py +0 -0
  294. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_schema_detection.py +0 -0
  295. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_single_source_focus_rules.py +0 -0
  296. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_spill.py +0 -0
  297. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_split_allocation.py +0 -0
  298. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_strict_conversion.py +0 -0
  299. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_studio.py +0 -0
  300. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_supplement_apply.py +0 -0
  301. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_supplement_loader.py +0 -0
  302. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_supplement_streaming.py +0 -0
  303. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_synthetic_conversion.py +0 -0
  304. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_validator.py +0 -0
  305. {focus_data_toolkit-0.12.0 → focus_data_toolkit-0.14.0}/tests/test_workflow_pins.py +0 -0
  306. {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.12.0...HEAD
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/` — outputs verified correct against the FOCUS spec.
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
+ [![PyPI](https://img.shields.io/pypi/v/focus-data-toolkit)](https://pypi.org/project/focus-data-toolkit/)
60
+ [![Python](https://img.shields.io/pypi/pyversions/focus-data-toolkit)](https://pypi.org/project/focus-data-toolkit/)
61
+ [![License: MIT AND CC-BY-4.0](https://img.shields.io/badge/license-MIT%20AND%20CC--BY--4.0-blue)](LICENSE)
62
+ [![CI](https://github.com/guymano/focus-data-toolkit/actions/workflows/ci.yml/badge.svg)](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
+ [![PyPI](https://img.shields.io/pypi/v/focus-data-toolkit)](https://pypi.org/project/focus-data-toolkit/)
7
+ [![Python](https://img.shields.io/pypi/pyversions/focus-data-toolkit)](https://pypi.org/project/focus-data-toolkit/)
8
+ [![License: MIT AND CC-BY-4.0](https://img.shields.io/badge/license-MIT%20AND%20CC--BY--4.0-blue)](LICENSE)
9
+ [![CI](https://github.com/guymano/focus-data-toolkit/actions/workflows/ci.yml/badge.svg)](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.