diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml new file mode 100644 index 00000000..6b95cd17 --- /dev/null +++ b/.github/workflows/testing.yml @@ -0,0 +1,26 @@ +name: Testing + +on: [push, pull_request] + +permissions: + contents: read + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.13"] + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v3 + with: + python-version: ${{ matrix.python-version }} + - name: Install testing dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -e ".[testing]" + - name: Run unit tests for the finopspp tool + run: | + pytest tools diff --git a/.github/workflows/validation.yml b/.github/workflows/validation.yml index f341c5ea..621c0ddc 100644 --- a/.github/workflows/validation.yml +++ b/.github/workflows/validation.yml @@ -22,7 +22,7 @@ jobs: uses: actions/setup-python@v3 with: python-version: ${{ matrix.python-version }} - - name: Install lint and testing dependencies + - name: Install lint and validation dependencies run: | npm install -g cspell-cli@9.6.0 python -m pip install --upgrade pip @@ -51,4 +51,3 @@ jobs: - name: Validate all action specifications with finopspp tool run: | finopspp specifications validate --specification-type="actions" all - diff --git a/assessments/FinOps Foundation/assessment.xlsx b/assessments/FinOps Foundation/assessment.xlsx index 5a544c01..cfffe537 100644 Binary files a/assessments/FinOps Foundation/assessment.xlsx and b/assessments/FinOps Foundation/assessment.xlsx differ diff --git a/assessments/FinOps Foundation/framework.md b/assessments/FinOps Foundation/framework.md index fe08d8b4..319d1b0f 100644 --- a/assessments/FinOps Foundation/framework.md +++ b/assessments/FinOps Foundation/framework.md @@ -1,6 +1,6 @@ # 001: FinOps Foundation Maturity Assessment Framework -**Creation Date:** 2026-04-04 +**Creation Date:** 2026-04-12 **Specification Version:** 1.0.0 @@ -71,25 +71,25 @@ Classic profile featuring controls provided by the finops foundation 113: Define Assessment Scope & Goals -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

114: Collect Standardized Assessment Inputs -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

115: Establish Baselines & Identify Trends -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

116: Prioritize Actions & Publish Roadmap -

Specification Version: 0.7.1

+

Specification Version: 1.0.0

@@ -111,19 +111,19 @@ Classic profile featuring controls provided by the finops foundation 097: Assess Skills & Training Needs -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

098: Develop Role-Based Learning Content -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

099: Offer Certifications & Informal Learning -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -135,7 +135,7 @@ Classic profile featuring controls provided by the finops foundation 101: Track Training Impact & Refresh Content -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -157,31 +157,31 @@ Classic profile featuring controls provided by the finops foundation 092: Create Implementation Roadmap -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

093: Integrate Cost Data in Workflows -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

094: Publish FinOps KPIs -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

095: Drive Stakeholder Engagement -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

096: Automate Tasks & Review Maturity -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -203,13 +203,13 @@ Classic profile featuring controls provided by the finops foundation 125: Evaluate Build vs Buy Options -

Specification Version: 1.7.0

+

Specification Version: 2.0.0

126: Implement & Configure Tools -

Specification Version: 0.7.0

+

Specification Version: 1.0.0

@@ -237,37 +237,37 @@ Classic profile featuring controls provided by the finops foundation 129: Establish Cross-Team Collab -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

130: Align Goals & Data Models -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

131: Share Reporting & Definitions -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

132: Provide Technology Metrics to Teams -

Specification Version: 0.9.0

+

Specification Version: 1.0.0

133: Coordinate Policy Changes Broadly -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

134: Drive Adoption & Resolve Conflicts -

Specification Version: 0.8.1

+

Specification Version: 1.0.0

@@ -283,13 +283,13 @@ Classic profile featuring controls provided by the finops foundation 107: Map Invoices to Allocation Model -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

108: Reconcile Rates & Track Completion -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -301,13 +301,13 @@ Classic profile featuring controls provided by the finops foundation 110: Automate Chargeback Data Flows -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

111: Distribute Reports & Track Accuracy -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -322,14 +322,14 @@ Classic profile featuring controls provided by the finops foundation 023: Executive Strategy Alignment -

Specification Version: 0.0.1

+

Specification Version: 1.0.0

@@ -409,13 +409,13 @@ Classic profile featuring controls provided by the finops foundation @@ -427,19 +427,19 @@ Classic profile featuring controls provided by the finops foundation
136: Establish Executive Sponsorship -

Specification Version: 0.0.1

+

Specification Version: 1.0.0

118: Create Workload Selection Criteria -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

119: Define Success Criteria & Scope -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

121: Align Timelines with Budgets -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

122: Start with Lower Environments -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

123: Review Outcomes & Adjust Patterns -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -454,38 +454,38 @@ Classic profile featuring controls provided by the finops foundation
- 085: Aggregate emissions data by region/service/account with lineage. -

Specification Version: 0.5.0

+ 085: Aggregate Emissions Data +

Specification Version: 0.6.0

- 086: Define sustainability metrics/targets. -

Specification Version: 0.5.0

+ 086: Define Sustainability Metrics +

Specification Version: 0.6.0

- 087: Include carbon impact in migration/optimization decisions and PRDs. -

Specification Version: 0.5.0

+ 087: Carbon Impact Workload Decisions +

Specification Version: 0.6.0

- 088: Enable engineering with carbon-aware data and scheduling guidance. -

Specification Version: 0.5.0

+ 088: Carbon Aware Data Guidance +

Specification Version: 0.6.0

- 089: Continuously improve data quality and report progress on dashboards. -

Specification Version: 0.5.0

+ 089: Sustainability Data Continuous Improvement +

Specification Version: 0.6.0

- 090: Tie sustainability wins to cost efficiency where possible. -

Specification Version: 0.5.0

+ 090: Tie Sustainability to Cost Efficiency +

Specification Version: 0.6.0

@@ -507,13 +507,13 @@ Classic profile featuring controls provided by the finops foundation 074: Document Licensing Models -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

075: Integrate Billing with Procurement -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -525,13 +525,13 @@ Classic profile featuring controls provided by the finops foundation 077: Influence Workload Design Choices -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

078: Publish License Spend & Utilization -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -593,7 +593,7 @@ Classic profile featuring controls provided by the finops foundation 067: Define Optimization Strategy -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -611,19 +611,19 @@ Classic profile featuring controls provided by the finops foundation 070: Partner on Scheduling Actions -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

071: Gamify Adoption & Celebrate Wins -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

072: Track KPIs & Document Playbooks -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -707,25 +707,25 @@ Classic profile featuring controls provided by the finops foundation 045: Increase Shared Cost Coverage -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

046: Publish Budget vs Actual Reports -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

047: Automate Budget Threshold Alerts -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

048: Review & Adapt Budget Strategy -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -787,25 +787,25 @@ Classic profile featuring controls provided by the finops foundation 030: Define Estimating Scope & Policy -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

031: Agree on Pricing Assumptions -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

032: Select Estimation Methods -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

033: Include Sustainability Impacts -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

@@ -823,7 +823,7 @@ Classic profile featuring controls provided by the finops foundation 036: Coach Engineering on Estimates -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

@@ -845,25 +845,25 @@ Classic profile featuring controls provided by the finops foundation 056: Document Inputs & Formulas -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

057: Build Unit Cost Dashboards -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

058: Set Review Cadence & Targets -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

059: Use Metrics to Prioritize Investments -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

@@ -1063,7 +1063,7 @@ Classic profile featuring controls provided by the finops foundation 016: Gather Requirements & Define KPIs -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -1081,7 +1081,7 @@ Classic profile featuring controls provided by the finops foundation 019: Publish Documentation & Support -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -1093,13 +1093,13 @@ Classic profile featuring controls provided by the finops foundation 021: Track Adoption & Manage Changes -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

022: Iterate Based on Feedback -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

diff --git a/assessments/FinOps Foundation/history/2026-04-12.json.gz b/assessments/FinOps Foundation/history/2026-04-12.json.gz new file mode 100644 index 00000000..65b61348 Binary files /dev/null and b/assessments/FinOps Foundation/history/2026-04-12.json.gz differ diff --git a/assessments/FinOps++/assessment.xlsx b/assessments/FinOps++/assessment.xlsx index 07b9f895..b05ff1b2 100644 Binary files a/assessments/FinOps++/assessment.xlsx and b/assessments/FinOps++/assessment.xlsx differ diff --git a/assessments/FinOps++/framework.md b/assessments/FinOps++/framework.md index 8a603c25..217640f8 100644 --- a/assessments/FinOps++/framework.md +++ b/assessments/FinOps++/framework.md @@ -1,6 +1,6 @@ # 002: FinOps++ Maturity Assessment Framework -**Creation Date:** 2026-04-04 +**Creation Date:** 2026-04-12 **Specification Version:** 0.0.1 @@ -31,25 +31,25 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 113: Define Assessment Scope & Goals -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

114: Collect Standardized Assessment Inputs -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

115: Establish Baselines & Identify Trends -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

116: Prioritize Actions & Publish Roadmap -

Specification Version: 0.7.1

+

Specification Version: 1.0.0

@@ -71,19 +71,19 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 097: Assess Skills & Training Needs -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

098: Develop Role-Based Learning Content -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

099: Offer Certifications & Informal Learning -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -95,7 +95,7 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 101: Track Training Impact & Refresh Content -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -117,31 +117,31 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 092: Create Implementation Roadmap -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

093: Integrate Cost Data in Workflows -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

094: Publish FinOps KPIs -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

095: Drive Stakeholder Engagement -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

096: Automate Tasks & Review Maturity -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -163,13 +163,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 125: Evaluate Build vs Buy Options -

Specification Version: 1.7.0

+

Specification Version: 2.0.0

126: Implement & Configure Tools -

Specification Version: 0.7.0

+

Specification Version: 1.0.0

@@ -197,37 +197,37 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 129: Establish Cross-Team Collab -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

130: Align Goals & Data Models -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

131: Share Reporting & Definitions -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

132: Provide Technology Metrics to Teams -

Specification Version: 0.9.0

+

Specification Version: 1.0.0

133: Coordinate Policy Changes Broadly -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

134: Drive Adoption & Resolve Conflicts -

Specification Version: 0.8.1

+

Specification Version: 1.0.0

@@ -243,13 +243,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 107: Map Invoices to Allocation Model -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

108: Reconcile Rates & Track Completion -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -261,13 +261,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 110: Automate Chargeback Data Flows -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

111: Distribute Reports & Track Accuracy -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -282,14 +282,14 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 023: Executive Strategy Alignment -

Specification Version: 0.0.1

+

Specification Version: 1.0.0

@@ -369,13 +369,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio @@ -387,19 +387,19 @@ Finops++ profile that is an extension and reorganization of the finops foundatio
136: Establish Executive Sponsorship -

Specification Version: 0.0.1

+

Specification Version: 1.0.0

118: Create Workload Selection Criteria -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

119: Define Success Criteria & Scope -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

121: Align Timelines with Budgets -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

122: Start with Lower Environments -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

123: Review Outcomes & Adjust Patterns -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -421,13 +421,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 074: Document Licensing Models -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

075: Integrate Billing with Procurement -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -439,13 +439,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 077: Influence Workload Design Choices -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

078: Publish License Spend & Utilization -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -507,7 +507,7 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 067: Define Optimization Strategy -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -525,19 +525,19 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 070: Partner on Scheduling Actions -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

071: Gamify Adoption & Celebrate Wins -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

072: Track KPIs & Document Playbooks -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -721,7 +721,7 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 016: Gather Requirements & Define KPIs -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -739,7 +739,7 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 019: Publish Documentation & Support -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -751,13 +751,13 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 021: Track Adoption & Manage Changes -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

022: Iterate Based on Feedback -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -841,25 +841,25 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 045: Increase Shared Cost Coverage -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

046: Publish Budget vs Actual Reports -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

047: Automate Budget Threshold Alerts -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

048: Review & Adapt Budget Strategy -

Specification Version: 0.7.0

+

Specification Version: 0.8.0

@@ -921,25 +921,25 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 030: Define Estimating Scope & Policy -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

031: Agree on Pricing Assumptions -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

032: Select Estimation Methods -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

033: Include Sustainability Impacts -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

@@ -957,7 +957,7 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 036: Coach Engineering on Estimates -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

@@ -979,25 +979,25 @@ Finops++ profile that is an extension and reorganization of the finops foundatio 056: Document Inputs & Formulas -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

057: Build Unit Cost Dashboards -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

058: Set Review Cadence & Targets -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

059: Use Metrics to Prioritize Investments -

Specification Version: 0.8.0

+

Specification Version: 1.0.0

diff --git a/assessments/FinOps++/history/2026-04-12.json.gz b/assessments/FinOps++/history/2026-04-12.json.gz new file mode 100644 index 00000000..6f4f90fd Binary files /dev/null and b/assessments/FinOps++/history/2026-04-12.json.gz differ diff --git a/components/actions/016.md b/components/actions/016.md index fb39490a..39ea54d0 100644 --- a/components/actions/016.md +++ b/components/actions/016.md @@ -1,6 +1,6 @@ # 16: Gather Requirements & Define Kpis -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 16-gather-requiremen @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/019.md b/components/actions/019.md index 71a02c7e..c69004e8 100644 --- a/components/actions/019.md +++ b/components/actions/019.md @@ -1,6 +1,6 @@ # 19: Publish Documentation & Support -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 19-document-support @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/021.md b/components/actions/021.md index 701e9783..93fe2ee8 100644 --- a/components/actions/021.md +++ b/components/actions/021.md @@ -1,6 +1,6 @@ # 21: Track Adoption & Manage Changes -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 21-track-adoption @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/022.md b/components/actions/022.md index 7f11be5d..46063fde 100644 --- a/components/actions/022.md +++ b/components/actions/022.md @@ -1,6 +1,6 @@ # 22: Iterate Based On Feedback -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 22-iterate-feedback @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/030.md b/components/actions/030.md index 5852b7c2..76d24674 100644 --- a/components/actions/030.md +++ b/components/actions/030.md @@ -1,6 +1,6 @@ # 30: Define Estimating Scope & Policy -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 30-define-estimating diff --git a/components/actions/031.md b/components/actions/031.md index 963fb6e0..d582bdb9 100644 --- a/components/actions/031.md +++ b/components/actions/031.md @@ -1,6 +1,6 @@ # 31: Agree On Pricing Assumptions -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 31-agree-pricing diff --git a/components/actions/032.md b/components/actions/032.md index 56ab0613..0fc67048 100644 --- a/components/actions/032.md +++ b/components/actions/032.md @@ -1,6 +1,6 @@ # 32: Select Estimation Methods -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 32-select-estimation diff --git a/components/actions/033.md b/components/actions/033.md index 5ed3f549..12fd286c 100644 --- a/components/actions/033.md +++ b/components/actions/033.md @@ -1,6 +1,6 @@ # 33: Include Sustainability Impacts -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 33-sustainability-impact diff --git a/components/actions/036.md b/components/actions/036.md index 1805de2f..4358ed0c 100644 --- a/components/actions/036.md +++ b/components/actions/036.md @@ -1,6 +1,6 @@ # 36: Coach Engineering On Estimates -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 36-coach-engineering diff --git a/components/actions/045.md b/components/actions/045.md index 5084ece3..a22f71c6 100644 --- a/components/actions/045.md +++ b/components/actions/045.md @@ -1,6 +1,6 @@ # 45: Increase Shared Cost Coverage -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 45-allocation-coverage @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/046.md b/components/actions/046.md index 0f826908..8ea089bc 100644 --- a/components/actions/046.md +++ b/components/actions/046.md @@ -1,6 +1,6 @@ # 46: Publish Budget Vs Actual Reports -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 46-publish-budget @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/047.md b/components/actions/047.md index 0db9d9fd..9e544bc5 100644 --- a/components/actions/047.md +++ b/components/actions/047.md @@ -1,6 +1,6 @@ # 47: Automate Budget Threshold Alerts -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 47-budget-alerts @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/048.md b/components/actions/048.md index 363a79ef..c70fc345 100644 --- a/components/actions/048.md +++ b/components/actions/048.md @@ -1,6 +1,6 @@ # 48: Review & Adapt Budget Strategy -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 48-review-adapt @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/056.md b/components/actions/056.md index 3bf03a7b..a470ac45 100644 --- a/components/actions/056.md +++ b/components/actions/056.md @@ -1,6 +1,6 @@ # 56: Document Inputs & Formulas -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 56-document-inputs diff --git a/components/actions/057.md b/components/actions/057.md index 9dbe3c53..c0a4028a 100644 --- a/components/actions/057.md +++ b/components/actions/057.md @@ -1,6 +1,6 @@ # 57: Build Unit Cost Dashboards -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 57-build-unitcost diff --git a/components/actions/058.md b/components/actions/058.md index 0e7a2c8c..32b8ee75 100644 --- a/components/actions/058.md +++ b/components/actions/058.md @@ -1,6 +1,6 @@ # 58: Set Review Cadence & Targets -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 58-set-cadence @@ -27,7 +27,7 @@ Reviews are important to capture changes in the business, but also mistakes in t * Review data with stakeholders when metrics exceed acceptable thresholds in either direction * Incorporate metric reviews into the decision making processes of stakeholder departments * The FinOps team should periodically publish an analysis of major changes in the metrics dashboards -as well as facilitate reviews of metrics, adoption data, modifications, and KPI refreshes + as well as facilitate reviews of metrics, adoption data, modifications, and KPI refreshes 10*Ceil(x/4) diff --git a/components/actions/059.md b/components/actions/059.md index c3190eeb..1b1d3f6f 100644 --- a/components/actions/059.md +++ b/components/actions/059.md @@ -1,6 +1,6 @@ # 59: Use Metrics To Prioritize Investments -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 59-use-metrics @@ -25,8 +25,8 @@ None * Scenario plan for which metric values will drive the next decisions, behaviors, or outcomes needed * Investigate root causes for changes in metric values to determine next steps * Add to the backlog which actions are required to get unit metrics to a desired level -* Unit economics influence the decisions in build vs buy, architecture design, - workload placement, migration, sourcing, pricing, etc. +* Unit economics influence the decisions in build vs buy, architecture design, workload placement, + migration, sourcing, pricing, etc. 10*Ceil(x/4) diff --git a/components/actions/067.md b/components/actions/067.md index a005be0e..1725b184 100644 --- a/components/actions/067.md +++ b/components/actions/067.md @@ -1,6 +1,6 @@ # 67: Define Optimization Strategy -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 67-define-strategy @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/070.md b/components/actions/070.md index d6bd4f9d..4334ed3c 100644 --- a/components/actions/070.md +++ b/components/actions/070.md @@ -1,6 +1,6 @@ # 70: Partner On Scheduling Actions -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 70-scheduling-actions @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/071.md b/components/actions/071.md index 2ddc9710..b7465bd4 100644 --- a/components/actions/071.md +++ b/components/actions/071.md @@ -1,6 +1,6 @@ # 71: Gamify Adoption & Celebrate Wins -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 71-gamify-adoption @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/072.md b/components/actions/072.md index 105c5f4e..bfb923f5 100644 --- a/components/actions/072.md +++ b/components/actions/072.md @@ -1,6 +1,6 @@ # 72: Track Kpis & Document Playbooks -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 72-track-kpis @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/074.md b/components/actions/074.md index 6f6f70a8..5b3c0681 100644 --- a/components/actions/074.md +++ b/components/actions/074.md @@ -1,6 +1,6 @@ # 74: Document Licensing Models -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 74-document-license @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/075.md b/components/actions/075.md index 7f30344f..c852e5a2 100644 --- a/components/actions/075.md +++ b/components/actions/075.md @@ -1,6 +1,6 @@ # 75: Integrate Billing With Procurement -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 75-integrate-billing @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/077.md b/components/actions/077.md index 5de21292..a1c8aedb 100644 --- a/components/actions/077.md +++ b/components/actions/077.md @@ -1,6 +1,6 @@ # 77: Influence Workload Design Choices -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 77-influence-workloa @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/078.md b/components/actions/078.md index 04863d71..db1e3d93 100644 --- a/components/actions/078.md +++ b/components/actions/078.md @@ -1,6 +1,6 @@ # 78: Publish License Spend & Utilization -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 78-publish-license @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/085.md b/components/actions/085.md index b351fc7f..8f998ae6 100644 --- a/components/actions/085.md +++ b/components/actions/085.md @@ -1,8 +1,8 @@ -# 85: None +# 85: Aggregate Emissions Data -**Specification Version:** 0.5.0 +**Specification Version:** 0.6.0 -**Slug:** None +**Slug:** 85-agg-emissions-data **Implementation Types:** @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/086.md b/components/actions/086.md index f0f54195..0af709ca 100644 --- a/components/actions/086.md +++ b/components/actions/086.md @@ -1,8 +1,8 @@ -# 86: None +# 86: Define Sustainability Metrics -**Specification Version:** 0.5.0 +**Specification Version:** 0.6.0 -**Slug:** None +**Slug:** 86-sustainability-metrics **Implementation Types:** @@ -18,7 +18,7 @@ Example kg CO2 per unit ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/087.md b/components/actions/087.md index 6f7fb112..b94bb434 100644 --- a/components/actions/087.md +++ b/components/actions/087.md @@ -1,8 +1,8 @@ -# 87: None +# 87: Carbon Impact Workload Decisions -**Specification Version:** 0.5.0 +**Specification Version:** 0.6.0 -**Slug:** None +**Slug:** 87-carbon-impact **Implementation Types:** @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/088.md b/components/actions/088.md index 3eecdc8b..2d9fee54 100644 --- a/components/actions/088.md +++ b/components/actions/088.md @@ -1,8 +1,8 @@ -# 88: None +# 88: Carbon Aware Data Guidance -**Specification Version:** 0.5.0 +**Specification Version:** 0.6.0 -**Slug:** None +**Slug:** 88-carbon-aware-guidance **Implementation Types:** @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/089.md b/components/actions/089.md index 930a83bb..cb41517d 100644 --- a/components/actions/089.md +++ b/components/actions/089.md @@ -1,8 +1,8 @@ -# 89: None +# 89: Sustainability Data Continuous Improvement -**Specification Version:** 0.5.0 +**Specification Version:** 0.6.0 -**Slug:** None +**Slug:** 89-improve-eco-data **Implementation Types:** @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/090.md b/components/actions/090.md index d1f505a1..d9c6bd2d 100644 --- a/components/actions/090.md +++ b/components/actions/090.md @@ -1,8 +1,8 @@ -# 90: None +# 90: Tie Sustainability To Cost Efficiency -**Specification Version:** 0.5.0 +**Specification Version:** 0.6.0 -**Slug:** None +**Slug:** 90-sustainability-cost **Implementation Types:** @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/092.md b/components/actions/092.md index 041df868..50653828 100644 --- a/components/actions/092.md +++ b/components/actions/092.md @@ -1,6 +1,6 @@ # 92: Create Implementation Roadmap -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 92-implement-roadmap @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/093.md b/components/actions/093.md index b7b4f42c..11c86793 100644 --- a/components/actions/093.md +++ b/components/actions/093.md @@ -1,6 +1,6 @@ # 93: Integrate Cost Data In Workflows -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 93-integrate-cost @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/094.md b/components/actions/094.md index 5b3e7001..b6e6406d 100644 --- a/components/actions/094.md +++ b/components/actions/094.md @@ -1,6 +1,6 @@ # 94: Publish Finops Kpis -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 94-publish-finops @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/095.md b/components/actions/095.md index 41718049..7f03dadb 100644 --- a/components/actions/095.md +++ b/components/actions/095.md @@ -1,6 +1,6 @@ # 95: Drive Stakeholder Engagement -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 95-drive-stakeholder @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/096.md b/components/actions/096.md index f3a67529..772fa7ea 100644 --- a/components/actions/096.md +++ b/components/actions/096.md @@ -1,6 +1,6 @@ # 96: Automate Tasks & Review Maturity -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 96-automate-tasks @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/097.md b/components/actions/097.md index 9a186fd7..815c01f1 100644 --- a/components/actions/097.md +++ b/components/actions/097.md @@ -1,6 +1,6 @@ # 97: Assess Skills & Training Needs -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 97-assess-skills @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/098.md b/components/actions/098.md index 58705f81..71244c20 100644 --- a/components/actions/098.md +++ b/components/actions/098.md @@ -1,6 +1,6 @@ # 98: Develop Role-Based Learning Content -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 98-develop-role @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/099.md b/components/actions/099.md index 4584c245..78acbef9 100644 --- a/components/actions/099.md +++ b/components/actions/099.md @@ -1,6 +1,6 @@ # 99: Offer Certifications & Informal Learning -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 99-offer-certification @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/101.md b/components/actions/101.md index be30975d..e350f783 100644 --- a/components/actions/101.md +++ b/components/actions/101.md @@ -1,6 +1,6 @@ # 101: Track Training Impact & Refresh Content -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 101-track-training @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/107.md b/components/actions/107.md index c5362e07..544d0b47 100644 --- a/components/actions/107.md +++ b/components/actions/107.md @@ -1,6 +1,6 @@ # 107: Map Invoices To Allocation Model -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 107-map-invoices @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/108.md b/components/actions/108.md index 8d4c14b0..18fb90f6 100644 --- a/components/actions/108.md +++ b/components/actions/108.md @@ -1,6 +1,6 @@ # 108: Reconcile Rates & Track Completion -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 108-reconcile-rates @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/110.md b/components/actions/110.md index 01dee545..d7bbc60b 100644 --- a/components/actions/110.md +++ b/components/actions/110.md @@ -1,6 +1,6 @@ # 110: Automate Chargeback Data Flows -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 110-automate-chargeback @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/111.md b/components/actions/111.md index 4ab196e8..36c5051d 100644 --- a/components/actions/111.md +++ b/components/actions/111.md @@ -1,6 +1,6 @@ # 111: Distribute Reports & Track Accuracy -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 111-chargeback-report @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/113.md b/components/actions/113.md index d6980bc5..08816aa6 100644 --- a/components/actions/113.md +++ b/components/actions/113.md @@ -1,6 +1,6 @@ # 113: Define Assessment Scope & Goals -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 113-define-assess diff --git a/components/actions/114.md b/components/actions/114.md index 9709abea..cef46d2d 100644 --- a/components/actions/114.md +++ b/components/actions/114.md @@ -1,6 +1,6 @@ # 114: Collect Standardized Assessment Inputs -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 114-collect-standard diff --git a/components/actions/115.md b/components/actions/115.md index e7f6c239..f06882b9 100644 --- a/components/actions/115.md +++ b/components/actions/115.md @@ -1,6 +1,6 @@ # 115: Establish Baselines & Identify Trends -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 115-establish-baseli diff --git a/components/actions/116.md b/components/actions/116.md index a524268d..a2f483af 100644 --- a/components/actions/116.md +++ b/components/actions/116.md @@ -1,6 +1,6 @@ # 116: Prioritize Actions & Publish Roadmap -**Specification Version:** 0.7.1 +**Specification Version:** 1.0.0 **Slug:** 116-actions-roadmap diff --git a/components/actions/118.md b/components/actions/118.md index be3afe2a..30e1dc45 100644 --- a/components/actions/118.md +++ b/components/actions/118.md @@ -1,6 +1,6 @@ # 118: Create Workload Selection Criteria -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 118-create-workload @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/119.md b/components/actions/119.md index 530cbb94..13a058a2 100644 --- a/components/actions/119.md +++ b/components/actions/119.md @@ -1,6 +1,6 @@ # 119: Define Success Criteria & Scope -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 119-define-success @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/121.md b/components/actions/121.md index dc18000d..793b5724 100644 --- a/components/actions/121.md +++ b/components/actions/121.md @@ -1,6 +1,6 @@ # 121: Align Timelines With Budgets -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 121-align-timelines @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/122.md b/components/actions/122.md index 2ca9b818..c5414173 100644 --- a/components/actions/122.md +++ b/components/actions/122.md @@ -1,6 +1,6 @@ # 122: Start With Lower Environments -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 122-start-lower @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/123.md b/components/actions/123.md index 9dd6e661..e23baa35 100644 --- a/components/actions/123.md +++ b/components/actions/123.md @@ -1,6 +1,6 @@ # 123: Review Outcomes & Adjust Patterns -**Specification Version:** 0.7.0 +**Specification Version:** 0.8.0 **Slug:** 123-review-outcomes @@ -18,7 +18,7 @@ None ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula @@ -26,11 +26,12 @@ None ### Scoring Details -**Score Type:** calculation +**Score Type:** binary | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Not done | +| 10 | Completed | ## References diff --git a/components/actions/125.md b/components/actions/125.md index a1a3d597..8487435f 100644 --- a/components/actions/125.md +++ b/components/actions/125.md @@ -1,6 +1,6 @@ # 125: Evaluate Build Vs Buy Options -**Specification Version:** 1.7.0 +**Specification Version:** 2.0.1 **Slug:** 125-evaluate-build @@ -14,7 +14,19 @@ Evaluate build vs buy; map requirements to vendor capabilities. ## Supplemental Guidance -None +CRAWL (0–8) No build-vs-buy process exists. TCO analysis is absent or undocumented, vendor comparisons are +informal, and decisions default to familiarity, relationships, or cost alone. + +WALK (9–17) Basic TCO covers License costs and implementation effort. Vendors are scored against a structured +scorecard, and decisions are loosely documented. Finance validation and exec sign-off are inconsistent. + +RUN (18–24) TCO is detailed, covering licensing, implementation, integration, training, support, and build +opportunity cost. A multi-stakeholder evaluation process is used, decisions are formally approved, and a guiding +policy is published and partially enforced. + +FLY (25–29) TCO is Finance-validated and accounts for multi-year trajectories, exit costs, and strategic +optionality. A full RFP with documented rationale is used, decisions are exec-approved and periodically reviewed, +and an enforced org-wide policy governs all future build-vs-buy choices. ## Scoring Guide @@ -22,18 +34,34 @@ None ### Formula -None +* Weighted sum + TCO (score ×3), + Vendor scoring (score ×3), + Decision document (score ×3), + Rules established (score ×1). + Max Raw Score (29) +* TCO_Analysis - Total Cost of Ownership analysis performed + (0=None, 1=High-level, 2=Detailed, 3=Validated with Finance) +* Vendor_Scored - Vendor options scored against criteria + (0=None, 1=Informal, 2=Structured scorecard, 3=Multi-stakeholder RFP) +* Decision_Doc - Build vs Buy decision formally documented and approved + (0=No, 1=Informal note, 2=Formal doc, 3=Exec-approved+reviewed) +* Rules_Est - Guiding rules / policy established for future decsions + (0=No rules, 1=Draft, 2=Published+Enforced) +* Output Range (0 - Max Raw) ### Scoring Details -**Score Type:** binary +**Score Type:** multi_bucket | Score | Condition | | ----- | --------- | -| 0 | No rules established | -| 10 | Published policy | +| 0 | Low Maturity, Score Range (0-8) | +| 4 | Developing, Score Range (9-17) | +| 7 | Established, Score Range (18-24) | +| 10 | Optimizing, Score Range (25-29) | ## References -* [None](None): None +* [Maturity Definition](https://www.finops.org/framework/maturity-model/): None diff --git a/components/actions/126.md b/components/actions/126.md index 5cc89e02..ef0170bb 100644 --- a/components/actions/126.md +++ b/components/actions/126.md @@ -1,6 +1,6 @@ # 126: Implement & Configure Tools -**Specification Version:** 0.7.0 +**Specification Version:** 1.0.0 **Slug:** 126-implement-config @@ -14,23 +14,45 @@ Implement/configure; tailor access/permissions and integrations. ## Supplemental Guidance -None +CRAWL Partial environment coverage, billing data only, no tag configuration. +WALK 50-79% coverage, usage and billing data, partial tagging, informal config documentation. +RUN 80-94% coverage, tags integrated, full manual tag rules, documented configuration. +FLY 100% coverage, all data sources automated, automated tag enforcement, version-controlled configuration. ## Scoring Guide -**Weight:** 0.0 +**Weight:** 1.0 ### Formula -None +* Weighted sum + Env coverage (score x4), + Data Integration (score x4), + Tag Config (score x2), + Config Doc (score x2). + Max raw (52) + +* Env_Coverage - % of cloud environments covered by deployed tools + (0=0%, 1<50%, 2=50-79%, 3=80-94%, 4=95-99%, 5=100%) +* Data_Integration - Data sources integrated (billing, usage, tags, business context) + (0=None, 1=Billing Only, 2=+Usage, 3=+Tags, 4=+Business context, 5=All+Automated) +* Tag_Config - Tag/allocation rules configured and enforced tool + (0=No, 1=Partial manual, 2=Full manual, 3=Automated enforcement) +* Config_Doc - Tool configuration documented and version-controlled + (0=No rules, 1=Informal, 2=Documented, 3=Version-controlled+Reviewed) + +* Output Range (0-52) ### Scoring Details -**Score Type:** calculation +**Score Type:** multi_bucket | Score | Condition | | ----- | --------- | -| 0 | None | +| 0 | Low Maturity, Score Range (0-15) | +| 4 | Developing, Score Range (16-31) | +| 7 | Established, Score Range (32-44) | +| 10 | Optimizing, Score Range (45-52) | ## References diff --git a/components/actions/129.md b/components/actions/129.md index 97a79415..0d4cec63 100644 --- a/components/actions/129.md +++ b/components/actions/129.md @@ -1,6 +1,6 @@ # 129: Establish Cross-Team Collab -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 129-establish-collab diff --git a/components/actions/130.md b/components/actions/130.md index b5c647d0..982c2022 100644 --- a/components/actions/130.md +++ b/components/actions/130.md @@ -1,6 +1,6 @@ # 130: Align Goals & Data Models -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 130-align-goals diff --git a/components/actions/131.md b/components/actions/131.md index bebf4e62..a903446c 100644 --- a/components/actions/131.md +++ b/components/actions/131.md @@ -1,6 +1,6 @@ # 131: Share Reporting & Definitions -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 131-share-reporting diff --git a/components/actions/132.md b/components/actions/132.md index 64a1e758..a17b3405 100644 --- a/components/actions/132.md +++ b/components/actions/132.md @@ -1,6 +1,6 @@ # 132: Provide Technology Metrics To Teams -**Specification Version:** 0.9.0 +**Specification Version:** 1.0.0 **Slug:** 132-provide-metrics diff --git a/components/actions/133.md b/components/actions/133.md index a8dc8bb5..a608e40d 100644 --- a/components/actions/133.md +++ b/components/actions/133.md @@ -1,6 +1,6 @@ # 133: Coordinate Policy Changes Broadly -**Specification Version:** 0.8.0 +**Specification Version:** 1.0.0 **Slug:** 133-coordinate-policy diff --git a/components/actions/134.md b/components/actions/134.md index c7b3563d..72881246 100644 --- a/components/actions/134.md +++ b/components/actions/134.md @@ -1,6 +1,6 @@ # 134: Drive Adoption & Resolve Conflicts -**Specification Version:** 0.8.1 +**Specification Version:** 1.0.0 **Slug:** 134-drive-adoption diff --git a/components/actions/136.md b/components/actions/136.md index 2a6db0ac..1ddff4ba 100644 --- a/components/actions/136.md +++ b/components/actions/136.md @@ -1,6 +1,6 @@ # 136: Establish Executive Sponsorship -**Specification Version:** 0.0.1 +**Specification Version:** 1.0.0 **Slug:** 136-exec-sponsor diff --git a/components/capabilities/007.md b/components/capabilities/007.md index 438bfc11..7c78ad60 100644 --- a/components/capabilities/007.md +++ b/components/capabilities/007.md @@ -8,9 +8,9 @@ None ## Actions -* [85](/components/actions/085.md): None -* [86](/components/actions/086.md): None -* [87](/components/actions/087.md): None -* [88](/components/actions/088.md): None -* [89](/components/actions/089.md): None -* [90](/components/actions/090.md): None +* [85](/components/actions/085.md): Aggregate Emissions Data +* [86](/components/actions/086.md): Define Sustainability Metrics +* [87](/components/actions/087.md): Carbon Impact Workload Decisions +* [88](/components/actions/088.md): Carbon Aware Data Guidance +* [89](/components/actions/089.md): Sustainability Data Continuous Improvement +* [90](/components/actions/090.md): Tie Sustainability to Cost Efficiency diff --git a/components/capabilities/023.md b/components/capabilities/023.md index 1bd493b9..9e1948e1 100644 --- a/components/capabilities/023.md +++ b/components/capabilities/023.md @@ -1,6 +1,6 @@ # 23: Executive Strategy Alignment -**Specification Version:** 0.0.1 +**Specification Version:** 1.0.0 ## Descriptive Statement diff --git a/specifications/README.md b/specifications/README.md index c1a913f7..eb0f1181 100644 --- a/specifications/README.md +++ b/specifications/README.md @@ -89,8 +89,7 @@ $defs: - maxLength: 25 type: string - type: 'null' - description: Machine parsable and human readable(ish) super short key label - for action + description: Machine parsable and human readable(ish) super short key label for action title: Slug Implementation Types: description: List of how the specification is implemented @@ -131,8 +130,7 @@ $defs: title: References type: array Supplemental Guidance: - description: List of notes that provide additional insights for a specific - action + description: List of notes that provide additional insights for a specific action items: anyOf: - type: string @@ -244,8 +242,7 @@ $defs: anyOf: - type: string - type: 'null' - description: Comments or longer form description of how a reference related - to a specification + description: Comments or longer form description of how a reference related to a specification title: Comment required: - Name @@ -701,8 +698,7 @@ $defs: title: Approver type: object CapabilityItem: - description: Special capability item model used for listing capabilities in other - specifications + description: Special capability item model used for listing capabilities in other specifications properties: Title: anyOf: @@ -1013,8 +1009,7 @@ $defs: title: Approver type: object CapabilityItem: - description: Special capability item model used for listing capabilities in other - specifications + description: Special capability item model used for listing capabilities in other specifications properties: Title: anyOf: diff --git a/specifications/actions/125.yaml b/specifications/actions/125.yaml index dfbe5abe..5de28153 100644 --- a/specifications/actions/125.yaml +++ b/specifications/actions/125.yaml @@ -2,7 +2,7 @@ Metadata: Proposed: '2025-09-16' Adopted: '2025-11-01' Modified: '2026-04-04' - Version: 2.0.0 + Version: 2.0.1 Status: Accepted Approvers: - Name: Victoria Levy @@ -19,12 +19,12 @@ Specification: Implementation Types: - null Weight: 1.0 - Formula: |- + Formula: | * Weighted sum - TCO (score ×3), - Vendor scoring (score ×3), - Decision document (score ×3), - Rules established (score ×1). + TCO (score ×3), + Vendor scoring (score ×3), + Decision document (score ×3), + Rules established (score ×1). Max Raw Score (29) * TCO_Analysis - Total Cost of Ownership analysis performed (0=None, 1=High-level, 2=Detailed, 3=Validated with Finance) @@ -50,7 +50,7 @@ Specification: Link: https://www.finops.org/framework/maturity-model/ Comment: null Supplemental Guidance: - - |- + - | CRAWL (0–8) No build-vs-buy process exists. TCO analysis is absent or undocumented, vendor comparisons are informal, and decisions default to familiarity, relationships, or cost alone. - | @@ -60,7 +60,7 @@ Specification: RUN (18–24) TCO is detailed, covering licensing, implementation, integration, training, support, and build opportunity cost. A multi-stakeholder evaluation process is used, decisions are formally approved, and a guiding policy is published and partially enforced. - - | + - |- FLY (25–29) TCO is Finance-validated and accounts for multi-year trajectories, exit costs, and strategic optionality. A full RFP with documented rationale is used, decisions are exec-approved and periodically reviewed, and an enforced org-wide policy governs all future build-vs-buy choices. diff --git a/specifications/capabilities/000.yaml b/specifications/capabilities/000.yaml index 211f36b5..0d53eed4 100644 --- a/specifications/capabilities/000.yaml +++ b/specifications/capabilities/000.yaml @@ -10,8 +10,8 @@ Metadata: Date: null Specification: ID: 0 - Title: null - Description: null + Title: Example Capability + Description: An example of a fully specified capability Actions: - - ID: null - Overrides: null + - ID: 0 # the example action + Overrides: null # no overrides are needed for this diff --git a/specifications/domains/000.yaml b/specifications/domains/000.yaml index b207791a..0c6d142c 100644 --- a/specifications/domains/000.yaml +++ b/specifications/domains/000.yaml @@ -10,8 +10,8 @@ Metadata: Date: null Specification: ID: 0 - Title: null - Description: null + Title: Example Domain + Description: An example of a fully specified domain Capabilities: - - ID: null - Overrides: null + - ID: 0 # the example capability + Overrides: null # no overrides are needed for this diff --git a/specifications/profiles/000.yaml b/specifications/profiles/000.yaml index f073d16a..a0b49096 100644 --- a/specifications/profiles/000.yaml +++ b/specifications/profiles/000.yaml @@ -10,16 +10,18 @@ Metadata: Date: null Specification: ID: 0 - Title: null - Description: null + Title: Example Profile + Description: an example of a fully specified profile, with additional manual configurations Domains: - - ID: null - - Title: null - Description: null + - ID: 0 # a real domain (the example domain) + - Title: Manual Domain # from this line down, this is an example of a manual specification + Description: Example of a manually created domain # A manual specification is useful of Proof-of-Concepts Capabilities: - - ID: null - - Title: null - Description: null + - Title: Manual Capability 1 + Description: An example manual capability where no actions have been set yet + Actions: null + - Title: Manual Capability 2 + Description: A more filled in manual capability with actions Actions: - - ID: null + - ID: 0 # NOTE: a real ID is used here. This action will also be used in Domain 0 Overrides: null diff --git a/tools/__main__.py b/tools/__main__.py index edbd9361..717d6304 100644 --- a/tools/__main__.py +++ b/tools/__main__.py @@ -1,20 +1,18 @@ """primary code for the finopspp CLI""" import click -from finopspp.commands import ( - generate, - specifications, - utils, - version -) +from finopspp.commands import utils +from finopspp.commands.version import group as version_group +from finopspp.commands.generate import group as generate_group +from finopspp.commands.specifications import group as specifications_group @click.group(cls=utils.ClickGroup) def cli(): """FinOps++ administration tool""" -cli.add_command(version.version) -cli.add_command(generate.generate) -cli.add_command(specifications.specifications) +cli.add_command(version_group.version) +cli.add_command(generate_group.generate) +cli.add_command(specifications_group.specifications) # include for those running script directly # as a python module. diff --git a/tools/commands/generate/__init__.py b/tools/commands/generate/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tools/commands/generate/group.py b/tools/commands/generate/group.py new file mode 100644 index 00000000..30dddad1 --- /dev/null +++ b/tools/commands/generate/group.py @@ -0,0 +1,137 @@ +"""Command file for all Generate group commands""" +import os +import sys +from importlib.resources import files + +import click +import yaml +from pydantic import TypeAdapter + +from finopspp.models import definitions +from finopspp.composers import archive, excel, markdown +from finopspp.commands import utils +from finopspp.commands.generate import helpers + +@click.group(cls=utils.ClickGroup) +def generate(): + """Generate files from YAML specifications""" + + +@generate.command() +@click.option( + '--profile', + default='FinOps++', + type=click.Choice(list(utils.profiles().keys())), + help='Which assessment profile to generate. Defaults to "FinOps++"', +) +def assessment(profile): # pylint: disable=too-many-branches,too-many-statements,too-many-locals + """Generate assessment files from their specifications""" + click.echo(f'Attempting to create assessment for profile={profile}:') + + domain_files = files('finopspp.specifications.domains') + cap_files = files('finopspp.specifications.capabilities') + action_files = files('finopspp.specifications.actions') + with open(utils.ProfilesMap[profile], 'r', encoding='utf-8') as yaml_file: + profile_yaml = yaml.safe_load( + yaml_file + ) + profile_spec = profile_yaml.get('Specification') or {} + profile_metadata = profile_yaml.get('Metadata') or {} + # edits to a specification in code should always be lowercase! + # to help show that it is a transformation from the uppercase + # version used in the actual yaml specification. + profile_spec['version'] = profile_metadata.get('Version') + + if not profile_spec.get('Domains'): + click.secho('Profile includes no domains. Exiting', err=True, fg='red') + sys.exit(1) + + if not isinstance(profile_spec['Domains'], list): + click.secho(f'Domains for profile={profile} must be a list', err=True, fg='red') + sys.exit(1) + + # pull in formatted domains data-dict + domains = helpers.domains_collector( + profile, profile_spec, domain_files, cap_files, action_files + ) + + # check if assessment directory exists for this profile + # and if it does not create it + base_path = os.path.join( + os.getcwd(), + 'assessments', + profile + ) + if not os.path.exists(base_path): + os.mkdir(base_path) + + # create assessment framework overview markdown + markdown.assessment_generate(profile, profile_spec, base_path, domains) + + # next try and create the workbook for this profile. + excel.assessment_generate(profile, base_path, domains) + + # finally, create the assessment archive file for the current version + archive.assessment_generate(profile, profile_spec, base_path, domains) + + +@generate.command() +def documents(): + """Generate schema documents markdown files from code""" + schemas = {} + for definition in [definitions.Action, definitions.Capability, definitions.Domain, definitions.Profile]: + schemas[definition.__name__.lower()] = yaml.dump( + TypeAdapter(definition).json_schema(mode='serialization'), + default_flow_style=False, + sort_keys=False, + indent=2, + width=120 # will always be longer that what is allowed by yamllint + ) + + markdown.schemas_generate(schemas) + + + +@generate.command() +@click.option( + '--specification-type', + default='profiles', + type=click.Choice(list(utils.SpecSubspecMap.keys())), + help='Which specification type to generate. Defaults to "profiles"' +) +def components(specification_type): + """Generate component markdown files from their specifications""" + spec_files = files(f'finopspp.specifications.{specification_type}') + + # get subspec to help fill in names and other important pieces of + # information from the sub specification. + subspec_type = utils.SpecSubspecMap[specification_type] + subspec_files = None + if subspec_type: + subspec_files = files(f'finopspp.specifications.{subspec_type}') + + # iterate over the specification files and generate markdown files + for spec in spec_files.iterdir(): + number, _ = os.path.splitext(spec.name) + # skip over example 0 specs + if not int(number): + continue + + # all added fields to a specification should be in lowercase! + # to help differentiate them against the uppercase fields in + # the specifications itself. + path = spec_files.joinpath(spec.name) + with open(path, 'r', encoding='utf-8') as yaml_file: + full_yaml = yaml.safe_load(yaml_file) + spec = full_yaml.get('Specification') + metadata = full_yaml.get('Metadata') or {} + spec['version'] = metadata.get('Version') + + # update all the immediate subspecs listed on the spec in places + for subspec in spec.get(subspec_type.capitalize(), []): + _, subspec_doc = helpers.sub_specification_collector(subspec, subspec_files) + subspec_id = str(subspec_doc.get('ID')) + subspec['file'] = f'/components/{subspec_type}/{"0"*(3-len(subspec_id))}{subspec_id}.md' + subspec['title'] = subspec_doc.get('Title') + + markdown.components_generate(specification_type, spec) diff --git a/tools/commands/generate.py b/tools/commands/generate/helpers.py similarity index 52% rename from tools/commands/generate.py rename to tools/commands/generate/helpers.py index d987bd27..bb56f640 100644 --- a/tools/commands/generate.py +++ b/tools/commands/generate/helpers.py @@ -1,23 +1,14 @@ -"""Command file for all Generate group commands""" -import os +"""Helpers file for generate command group""" import sys -from importlib.resources import files import click import yaml -from pydantic import TypeAdapter, ValidationError +from pydantic import ValidationError from rich.progress import track from finopspp.models import definitions -from finopspp.composers import archive, excel, markdown -from finopspp.commands import utils -@click.group(cls=utils.ClickGroup) -def generate(): - """Generate files from YAML specifications""" - - -def sub_specification_helper(spec, spec_file): +def sub_specification_collector(spec, spec_file): """Helps find and pull Specification subsection from a specification Note: metadata is only expected to be returned if it is defined. It might not @@ -30,8 +21,8 @@ def sub_specification_helper(spec, spec_file): Specification (dict) - the actual specification for a component """ spec_id = spec.get('ID') - # if no ID, or it is ID 0, assume the full sub-specification is given and return - if not spec_id: + # if ID is not an int, assume the full sub-specification is given and return + if not isinstance(spec_id, int): return {}, spec # else look up sub-specification file ID @@ -44,7 +35,7 @@ def sub_specification_helper(spec, spec_file): sub_spec = full_sub.get('Specification') or {} return sub_metadata, sub_spec -def overrides_helper(spec, profile, override_type='std'): +def overrides_collector(spec, profile, override_type='std'): """Helper for receiving the overrides for a profile if they exist Also ensure that if an override exists, it conforms to the specification of an @@ -83,41 +74,25 @@ def overrides_helper(spec, profile, override_type='std'): return validated_override.model_dump() -@generate.command() -@click.option( - '--profile', - default='FinOps++', - type=click.Choice(list(utils.profiles().keys())), - help='Which assessment profile to generate. Defaults to "FinOps++"', -) -def assessment(profile): # pylint: disable=too-many-branches,too-many-statements,too-many-locals - """Generate assessment files from their specifications""" - click.echo(f'Attempting to create assessment for profile={profile}:') - - domain_files = files('finopspp.specifications.domains') - cap_files = files('finopspp.specifications.capabilities') - action_files = files('finopspp.specifications.actions') - with open(utils.ProfilesMap[profile], 'r', encoding='utf-8') as yaml_file: - profile_yaml = yaml.safe_load( - yaml_file - ) - profile_spec = profile_yaml.get('Specification') or {} - profile_metadata = profile_yaml.get('Metadata') or {} - # edits to a specification in code should always be lowercase! - # to help show that it is a transformation from the uppercase - # version used in the actual yaml specification. - profile_spec['version'] = profile_metadata.get('Version') - - if not profile_spec.get('Domains'): - click.secho('Profile includes no domains. Exiting', err=True, fg='red') - sys.exit(1) +def domains_collector(profile, profile_spec, domain_files, cap_files, action_files): + """Helper designed to collect and return a specific format for a domains dict + + This format is required to work properly with the composers to + generate the different parts of an assessment. + NOTE: there is a UI component to this in the form of the Rich Progress Tracker. + When testing, this will most likely show in your terminal, but can be safely + ignored. + """ domains = [] - for domain in track(profile_spec.get('Domains'), 'Loading profile'): + # all profile specs should have a Domains field that is a list by this point. + # if it doesn't exist, just let it fail out on a python error + profile_domains = profile_spec['Domains'] + for domain in track(profile_domains, 'Loading profile'): capabilities = [] - metadata, spec = sub_specification_helper(domain, domain_files) - domain_override = overrides_helper(spec, profile) + metadata, spec = sub_specification_collector(domain, domain_files) + domain_override = overrides_collector(spec, profile) domain_drops = [drop['ID'] for drop in domain_override.get('DropIDs')] if domain_override.get('TitleUpdate'): @@ -129,7 +104,7 @@ def assessment(profile): # pylint: disable=too-many-branches,too-many-statements if spec.get('Capabilities') is None: spec['Capabilities'] = [] - if not isinstance(spec.get('Capabilities'), list): + if not isinstance(spec['Capabilities'], list): click.secho( f'Capabilities for domain={title} must be null or a list. Exiting', err=True, @@ -139,8 +114,8 @@ def assessment(profile): # pylint: disable=too-many-branches,too-many-statements spec_id = spec.get('ID') serial_number = None - if spec_id: - spec_id = str(spec.get('ID')) + if isinstance(spec_id, int): # will skip over None type IDs + spec_id = str(spec_id) serial_number = '0'*(3-len(spec_id)) + spec_id domains.append({ @@ -153,14 +128,14 @@ def assessment(profile): # pylint: disable=too-many-branches,too-many-statements for capability in spec.get('Capabilities'): actions = [] - metadata, spec = sub_specification_helper(capability, cap_files) + metadata, spec = sub_specification_collector(capability, cap_files) # continue early if the Capability ID is one to be dropped spec_id = spec.get('ID') if spec_id and spec_id in domain_drops: continue - cap_override = overrides_helper(spec, profile) + cap_override = overrides_collector(spec, profile) cap_drops = [drop['ID'] for drop in cap_override.get('DropIDs')] if cap_override.get('TitleUpdate'): @@ -181,8 +156,9 @@ def assessment(profile): # pylint: disable=too-many-branches,too-many-statements sys.exit(1) serial_number = None - if spec_id: - spec_id = str(spec.get('ID')) + spec_id = spec.get('ID') + if isinstance(spec_id, int): # will skip over None type IDs + spec_id = str(spec_id) serial_number = '0'*(3-len(spec_id)) + spec_id capabilities.append({ @@ -193,14 +169,14 @@ def assessment(profile): # pylint: disable=too-many-branches,too-many-statements }) spec.get('Actions').extend(cap_override.get('AddIDs')) for action in spec.get('Actions'): - metadata, spec = sub_specification_helper(action, action_files) + metadata, spec = sub_specification_collector(action, action_files) # continue early if the Action ID is one to be dropped spec_id = spec.get('ID') if spec_id and spec_id in cap_drops: continue - act_override = overrides_helper(spec, profile, 'action') + act_override = overrides_collector(spec, profile, 'action') if act_override.get('TitleUpdate'): spec['Title'] = act_override.get('TitleUpdate') @@ -227,83 +203,4 @@ def assessment(profile): # pylint: disable=too-many-branches,too-many-statements 'weighted score': None }) - # check if assessment directory exists for this profile - # and if it does not create it - base_path = os.path.join( - os.getcwd(), - 'assessments', - profile - ) - if not os.path.exists(base_path): - os.mkdir(base_path) - - # create assessment framework overview markdown - markdown.assessment_generate(profile, profile_spec, base_path, domains) - - # next try and create the workbook for this profile. - excel.assessment_generate(profile, base_path, domains) - - # finally, create the assessment archive file for the current version - archive.assessment_generate(profile, profile_spec, base_path, domains) - - -@generate.command() -def documents(): - """Generate schema documents markdown files from code""" - schemas = {} - for definition in [definitions.Action, definitions.Capability, definitions.Domain, definitions.Profile]: - schemas[definition.__name__.lower()] = yaml.dump( - TypeAdapter(definition).json_schema(mode='serialization'), - default_flow_style=False, - sort_keys=False, - indent=2, - width=120 # will always be longer that what is allowed by yamllint - ) - - markdown.schemas_generate(schemas) - - - -@generate.command() -@click.option( - '--specification-type', - default='profiles', - type=click.Choice(list(utils.SpecSubspecMap.keys())), - help='Which specification type to generate. Defaults to "profiles"' -) -def components(specification_type): - """Generate component markdown files from their specifications""" - spec_files = files(f'finopspp.specifications.{specification_type}') - - # get subspec to help fill in names and other important pieces of - # information from the sub specification. - subspec_type = utils.SpecSubspecMap[specification_type] - subspec_files = None - if subspec_type: - subspec_files = files(f'finopspp.specifications.{subspec_type}') - - # iterate over the specification files and generate markdown files - for spec in spec_files.iterdir(): - number, _ = os.path.splitext(spec.name) - # skip over example 0 specs - if not int(number): - continue - - # all added fields to a specification should be in lowercase! - # to help differentiate them against the uppercase fields in - # the specifications itself. - path = spec_files.joinpath(spec.name) - with open(path, 'r', encoding='utf-8') as yaml_file: - full_yaml = yaml.safe_load(yaml_file) - spec = full_yaml.get('Specification') - metadata = full_yaml.get('Metadata') or {} - spec['version'] = metadata.get('Version') - - # update all the immediate subspecs listed on the spec in places - for subspec in spec.get(subspec_type.capitalize(), []): - _, subspec_doc = sub_specification_helper(subspec, subspec_files) - subspec_id = str(subspec_doc.get('ID')) - subspec['file'] = f'/components/{subspec_type}/{"0"*(3-len(subspec_id))}{subspec_id}.md' - subspec['title'] = subspec_doc.get('Title') - - markdown.components_generate(specification_type, spec) + return domains diff --git a/tools/commands/specifications/__init__.py b/tools/commands/specifications/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tools/commands/approvals.tcss b/tools/commands/specifications/approvals.tcss similarity index 100% rename from tools/commands/approvals.tcss rename to tools/commands/specifications/approvals.tcss diff --git a/tools/commands/specifications.py b/tools/commands/specifications/group.py similarity index 77% rename from tools/commands/specifications.py rename to tools/commands/specifications/group.py index fcdec088..2bdfcbb6 100644 --- a/tools/commands/specifications.py +++ b/tools/commands/specifications/group.py @@ -13,15 +13,10 @@ from pydantic import TypeAdapter, ValidationError from rich.console import Console from rich.syntax import Syntax -from textual import on -from textual.app import App -from textual.containers import Horizontal, Vertical -from textual.events import Mount -from textual.widgets import Footer, Header, Label, Pretty, SelectionList, Switch -from textual.widgets.selection_list import Selection from finopspp.models import definitions, defaults from finopspp.commands import utils +from finopspp.commands.specifications import helpers # if MANPAGER is set to something odd, it will break the pager @@ -478,141 +473,6 @@ def update(selection, specification_type, major, force): sys.exit(1) -def approval_helper(approvers, number, today, specs): - """Helper to add approval to a specification by number""" - path = specs.joinpath(f'{number}.yaml') - with open(path, 'r', encoding='utf-8') as yaml_file: - specification_data = yaml.safe_load(yaml_file) - - # update basic metadata - metadata = specification_data['Metadata'] - metadata['Version'] = '1.0.0' - metadata['Adopted'] = today - metadata['Modified'] = today - metadata['Status'] = definitions.StatusEnum.accepted.value - - # now update metadata approvers list with selected approvers - approval_list = [] - for name, email in approvers: - approval_list.append( - { - 'Name': name, - 'Email': email, - 'Date': today - } - ) - metadata['Approvers'] = approval_list - - with open(path, 'w', encoding='utf-8') as yaml_file: - yaml.dump( - specification_data, - yaml_file, - default_flow_style=False, - sort_keys=False, - indent=2, - width=120 # will always be longer that what is allowed by yamllint - ) - -def approval_options_helper(specification_type): - """Helper to pull information on what should should be approved""" - spec_options = {} - specs = files(f'finopspp.specifications.{specification_type}') - for spec in specs.iterdir(): - number, _ = os.path.splitext(spec.name) - - # skip over example 0 specs - if not int(number): - continue - - path = specs.joinpath(spec.name) - - # load up previous data first - with open(path, 'r', encoding='utf-8') as yaml_file: - specification_data = yaml.safe_load(yaml_file) - - # check if status is Proposed, and if it is, add as option to approve - if specification_data['Metadata']['Status'] != definitions.StatusEnum.proposed.value: - continue - - spec = specification_data['Specification'] - - # only accept specifications with titles - title = spec['Title'] - if not title: - continue - - # new a few additional filters for actions - if specification_type == 'actions': - # skip if weight is still 0 or does not exist. - if not spec['Weight']: - continue - - # also skip if the last score is not 10 - last_score = spec['Scoring'].pop() - if last_score['Score'] != 10: - continue - - spec_options[title] = number - - return spec_options - -def approval_selector_helper(options, specification_type, approval_map): - """Helper for building the selector interface""" - selection_list = [] - for title, serial_number in options.items(): - selection_list.append(Selection(title, serial_number)) - - # now we create the terminal app that allows selections - class Approvals(App): - """Terminal application - - that will provide an selection inferface for specifications to approve""" - CSS_PATH = "approvals.tcss" - - def compose(self): - """Layout of app in terminal""" - yield Header() - with Vertical(): - yield Label( - 'Please select from this list and press [b]ctrl-q[/] to save & exit' - ) - with Horizontal(): - yield SelectionList(*selection_list) - yield Pretty(approval_map[specification_type]) - with Horizontal(id='horizontal-two'): - yield Label('(De)Select All:') - yield Switch() - yield Footer() - - def on_mount(self): - """What to do when the app is loaded""" - self.query_one( - SelectionList - ).border_title = 'Which specifications should be approved?' - self.query_one( - Pretty - ).border_title = 'Selected specifications (by ID)' - self.title = f'Approvals for "{specification_type.title()}"' - - @on(Mount) - @on(SelectionList.SelectedChanged) - def update_selected_view(self): - """How the view should update on selection changes""" - approval_map[specification_type] = self.query_one(SelectionList).selected - self.query_one( - Pretty - ).update( - approval_map[specification_type] - ) - - @on(Switch.Changed) - def update_switched_view(self): - """How the view should update on switch changes""" - self.query_one(SelectionList).toggle_all() - - - Approvals().run() - @specifications.command() def approvals(): """Command to help do mass approvals for each component type @@ -665,13 +525,13 @@ def approvals(): click.secho(f'Skipping {spec_type}...', fg='yellow') continue - spec_options = approval_options_helper(spec_type) + spec_options = helpers.approval_options(spec_type) if not spec_options: click.secho(f'No approvals needed for {spec_type}.') click.pause() else: - approval_selector_helper(spec_options, spec_type, approval_map) + helpers.approval_selector(spec_options, spec_type, approval_map) # get todays date today = str(datetime.date.today()) @@ -679,21 +539,21 @@ def approvals(): # do approvals for profiles first specs = files('finopspp.specifications.profiles') for number in approval_map['profiles']: - approval_helper(approvers, number, today, specs) + helpers.approval(approvers, number, today, specs) # next do domains specs = files('finopspp.specifications.domains') for number in approval_map['domains']: - approval_helper(approvers, number, today, specs) + helpers.approval(approvers, number, today, specs) # next do capabilities specs = files('finopspp.specifications.capabilities') for number in approval_map['capabilities']: - approval_helper(approvers, number, today, specs) + helpers.approval(approvers, number, today, specs) # finally do actions specs = files('finopspp.specifications.actions') for number in approval_map['actions']: - approval_helper(approvers, number, today, specs) + helpers.approval(approvers, number, today, specs) click.secho('Approval completed', fg='green') diff --git a/tools/commands/specifications/helpers.py b/tools/commands/specifications/helpers.py new file mode 100644 index 00000000..14e8b701 --- /dev/null +++ b/tools/commands/specifications/helpers.py @@ -0,0 +1,148 @@ +"""Helpers file for specifications command group""" +import os +from importlib.resources import files + +import yaml +from textual import on +from textual.app import App +from textual.containers import Horizontal, Vertical +from textual.events import Mount +from textual.widgets import Footer, Header, Label, Pretty, SelectionList, Switch +from textual.widgets.selection_list import Selection + +from finopspp.models import definitions + +def approval(approvers, number, today, specs): + """Helper to add approval to a specification by number""" + path = specs.joinpath(f'{number}.yaml') + with open(path, 'r', encoding='utf-8') as yaml_file: + specification_data = yaml.safe_load(yaml_file) + + # update basic metadata + metadata = specification_data['Metadata'] + metadata['Version'] = '1.0.0' + metadata['Adopted'] = today + metadata['Modified'] = today + metadata['Status'] = definitions.StatusEnum.accepted.value + + # now update metadata approvers list with selected approvers + approval_list = [] + for name, email in approvers: + approval_list.append( + { + 'Name': name, + 'Email': email, + 'Date': today + } + ) + metadata['Approvers'] = approval_list + + with open(path, 'w', encoding='utf-8') as yaml_file: + yaml.dump( + specification_data, + yaml_file, + default_flow_style=False, + sort_keys=False, + indent=2, + width=120 # will always be longer that what is allowed by yamllint + ) + +def approval_options(specification_type): + """Helper to pull information on what should should be approved""" + spec_options = {} + specs = files(f'finopspp.specifications.{specification_type}') + for spec in specs.iterdir(): + number, _ = os.path.splitext(spec.name) + + # skip over example 0 specs + if not int(number): + continue + + path = specs.joinpath(spec.name) + + # load up previous data first + with open(path, 'r', encoding='utf-8') as yaml_file: + specification_data = yaml.safe_load(yaml_file) + + # check if status is Proposed, and if it is, add as option to approve + if specification_data['Metadata']['Status'] != definitions.StatusEnum.proposed.value: + continue + + spec = specification_data['Specification'] + + # only accept specifications with titles + title = spec['Title'] + if not title: + continue + + # new a few additional filters for actions + if specification_type == 'actions': + # skip if weight is still 0 or does not exist. + if not spec['Weight']: + continue + + # also skip if the last score is not 10 + last_score = spec['Scoring'].pop() + if last_score['Score'] != 10: + continue + + spec_options[title] = number + + return spec_options + +def approval_selector(options, specification_type, approval_map): + """Helper for building the selector interface""" + selection_list = [] + for title, serial_number in options.items(): + selection_list.append(Selection(title, serial_number)) + + # now we create the terminal app that allows selections + class Approvals(App): + """Terminal application + + that will provide an selection inferface for specifications to approve""" + CSS_PATH = "approvals.tcss" + + def compose(self): + """Layout of app in terminal""" + yield Header() + with Vertical(): + yield Label( + 'Please select from this list and press [b]ctrl-q[/] to save & exit' + ) + with Horizontal(): + yield SelectionList(*selection_list) + yield Pretty(approval_map[specification_type]) + with Horizontal(id='horizontal-two'): + yield Label('(De)Select All:') + yield Switch() + yield Footer() + + def on_mount(self): + """What to do when the app is loaded""" + self.query_one( + SelectionList + ).border_title = 'Which specifications should be approved?' + self.query_one( + Pretty + ).border_title = 'Selected specifications (by ID)' + self.title = f'Approvals for "{specification_type.title()}"' + + @on(Mount) + @on(SelectionList.SelectedChanged) + def update_selected_view(self): + """How the view should update on selection changes""" + approval_map[specification_type] = self.query_one(SelectionList).selected + self.query_one( + Pretty + ).update( + approval_map[specification_type] + ) + + @on(Switch.Changed) + def update_switched_view(self): + """How the view should update on switch changes""" + self.query_one(SelectionList).toggle_all() + + + Approvals().run() diff --git a/tools/commands/utils.py b/tools/commands/utils.py index ace268e7..7eca0e96 100644 --- a/tools/commands/utils.py +++ b/tools/commands/utils.py @@ -1,4 +1,4 @@ -"""Command utility file. Include no actual commands""" +"""Common utilities used by the finopspp commands""" from importlib.resources import files import click @@ -75,6 +75,7 @@ def profiles(): for file in profile_specs.iterdir(): path = profile_specs.joinpath(file.name) with open(path, 'r', encoding='utf-8') as yaml_file: + # we only include profiles in the map that include a title title = yaml.safe_load(yaml_file).get('Specification').get('Title') if not title: continue diff --git a/tools/commands/version/__init__.py b/tools/commands/version/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tools/commands/version.py b/tools/commands/version/group.py similarity index 100% rename from tools/commands/version.py rename to tools/commands/version/group.py diff --git a/tools/tests/data/expected_example_dataframe.json b/tools/tests/data/expected_example_dataframe.json new file mode 100644 index 00000000..976ab2fe --- /dev/null +++ b/tools/tests/data/expected_example_dataframe.json @@ -0,0 +1,44 @@ +{ + "serial number": { + "('Example Domain', 'Example Capability', 'Title of specification')": "000", + "('Manual Domain', 'Manual Capability 2', 'Title of specification')": "000" + }, + "version": { + "('Example Domain', 'Example Capability', 'Title of specification')": "1.0.0", + "('Manual Domain', 'Manual Capability 2', 'Title of specification')": "1.0.0" + }, + "weights": { + "('Example Domain', 'Example Capability', 'Title of specification')": 2.4, + "('Manual Domain', 'Manual Capability 2', 'Title of specification')": 2.4 + }, + "formula": { + "('Example Domain', 'Example Capability', 'Title of specification')": "If given, this formula is used to calculate the condition for the score\n\nCan be an equation or series of equations that feed into a final value\nor range of values", + "('Manual Domain', 'Manual Capability 2', 'Title of specification')": "If given, this formula is used to calculate the condition for the score\n\nCan be an equation or series of equations that feed into a final value\nor range of values" + }, + "scoring": { + "('Example Domain', 'Example Capability', 'Title of specification')": [ + { + "Score": 0, + "Condition": "Condition required for min score value of 0" + }, + { + "Score": 10, + "Condition": "Condition required for max score value of 10" + } + ], + "('Manual Domain', 'Manual Capability 2', 'Title of specification')": [ + { + "Score": 0, + "Condition": "Condition required for min score value of 0" + }, + { + "Score": 10, + "Condition": "Condition required for max score value of 10" + } + ] + }, + "weighted score": { + "('Example Domain', 'Example Capability', 'Title of specification')": null, + "('Manual Domain', 'Manual Capability 2', 'Title of specification')": null + } +} \ No newline at end of file diff --git a/tools/tests/data/expected_example_domains.json b/tools/tests/data/expected_example_domains.json new file mode 100644 index 00000000..df50ca20 --- /dev/null +++ b/tools/tests/data/expected_example_domains.json @@ -0,0 +1,72 @@ +[ + { + "serial_number": "000", + "version": "0.0.1", + "domain": "Example Domain", + "capabilities": [ + { + "serial_number": "000", + "version": "0.0.1", + "capability": "Example Capability", + "actions": [ + { + "action": "Title of specification", + "serial_number": "000", + "version": "1.0.0", + "weights": 2.4, + "formula": "If given, this formula is used to calculate the condition for the score\n\nCan be an equation or series of equations that feed into a final value\nor range of values", + "scoring": [ + { + "Score": 0, + "Condition": "Condition required for min score value of 0" + }, + { + "Score": 10, + "Condition": "Condition required for max score value of 10" + } + ], + "weighted score": null + } + ] + } + ] + }, + { + "serial_number": null, + "version": null, + "domain": "Manual Domain", + "capabilities": [ + { + "serial_number": null, + "version": null, + "capability": "Manual Capability 1", + "actions": [] + }, + { + "serial_number": null, + "version": null, + "capability": "Manual Capability 2", + "actions": [ + { + "action": "Title of specification", + "serial_number": "000", + "version": "1.0.0", + "weights": 2.4, + "formula": "If given, this formula is used to calculate the condition for the score\n\nCan be an equation or series of equations that feed into a final value\nor range of values", + "scoring": [ + { + "Score": 0, + "Condition": "Condition required for min score value of 0" + }, + { + "Score": 10, + "Condition": "Condition required for max score value of 10" + } + ], + "weighted score": null + } + ] + } + ] + } +] \ No newline at end of file diff --git a/tools/tests/test_assessment_generation.py b/tools/tests/test_assessment_generation.py new file mode 100644 index 00000000..8dcc34ec --- /dev/null +++ b/tools/tests/test_assessment_generation.py @@ -0,0 +1,48 @@ +"""Test composers""" +import json +import os +from importlib.resources import files + +import yaml + +from finopspp.commands import utils +from finopspp.commands.generate.helpers import domains_collector +from finopspp.composers.helpers import normalize + + +def test_normalize(): + """Test for the composer util 'normalize'""" + profile = 'Example Profile' + domain_files = files('finopspp.specifications.domains') + cap_files = files('finopspp.specifications.capabilities') + action_files = files('finopspp.specifications.actions') + profile_map = utils.profiles() + with open(profile_map[profile], 'r', encoding='utf-8') as yaml_file: + profile_yaml = yaml.safe_load( + yaml_file + ) + profile_spec = profile_yaml['Specification'] + profile_spec['version'] = profile_yaml['Metadata']['Version'] + + # pull in formatted domains data-dict + domains = domains_collector( + profile, profile_spec, domain_files, cap_files, action_files + ) + assert domains + + data_path = os.path.join(os.getcwd(), 'tools/tests/data/expected_example_domains.json') + with open(data_path, mode='r', encoding='utf-8') as in_file: + expected_domains = json.load(in_file) + + assert domains == expected_domains + + # now normalize the domains + normalized_domains = normalize(domains) + assert normalized_domains is not None + + data_path = os.path.join(os.getcwd(), 'tools/tests/data/expected_example_dataframe.json') + with open(data_path, mode='r', encoding='utf-8') as in_file: + expected_dataframe = json.load(in_file) + + json_dataframe = json.loads(normalized_domains.to_json()) + assert json_dataframe == expected_dataframe