Skip to content

Commit 91ac8c5

Browse files
WIP: REfactor
1 parent 81f94df commit 91ac8c5

15 files changed

Lines changed: 591 additions & 440 deletions

File tree

docs/how-to/dashboards_and_quality_gates.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -380,7 +380,7 @@ Result:
380380
results == [10, 5]
381381
382382
get_just_metrics(...)
383-
~~~~~~~~~~~~~~~~~~~~
383+
~~~~~~~~~~~~~~~~~~~~~
384384

385385
Purpose
386386
^^^^^^^

docs/internals/requirements/implementation_state.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ repositories.
3030
.. needpie:: Overall Metrics with Total incooperated
3131
:labels: without any link, overall with test link, overall with code, overall fully linked
3232
:colors: red, yellow, blue, green
33-
:filter-func: src.extensions.score_metrics.traceability_dashboard.get_metrics_with_overall_total_considered(overall_metrics:with_test_link,overall_metrics:with_code_link,overall_metrics:fully_linked)
33+
:filter-func: src.extensions.score_metrics.sphinx_filter.get_metrics_with_overall_total_considered(overall_metrics:with_test_link,overall_metrics:with_code_link,overall_metrics:fully_linked)
3434

3535

3636

3737
.. needpie:: Metrics without any total incooperated
3838
:labels: tool req with test link, tool req with code, overall fully linked
3939
:colors: yellow, blue, green
40-
:filter-func: src.extensions.score_metrics.traceability_dashboard.get_just_metrics(metrics_by_type:tool_req:with_test_link,metrics_by_type:tool_req:with_code_link,overall_metrics:fully_linked)
40+
:filter-func: src.extensions.score_metrics.sphinx_filter.get_just_metrics(metrics_by_type:tool_req:with_test_link,metrics_by_type:tool_req:with_code_link,overall_metrics:fully_linked)
4141

4242

4343
.. Jump to evidence tables:

src/extensions/score_metamodel/checks/standards.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,12 @@
1616
# ╙ ╜
1717
# from sphinx.application import Sphinx
1818

19-
from sphinx_needs.need_item import NeedItem
20-
2119
from score_metrics.sphinx_filters import (
2220
generic_pie_items_by_tag,
2321
generic_pie_items_in_relationships,
2422
generic_pie_linked_items,
2523
)
24+
from sphinx_needs.need_item import NeedItem
2625

2726
# from score_metamodel import (
2827
# CheckLogger,

src/extensions/score_metrics/BUILD

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
# *******************************************************************************
2+
# Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
#
4+
# See the NOTICE file(s) distributed with this work for additional
5+
# information regarding copyright ownership.
6+
#
7+
# This program and the accompanying materials are made available under the
8+
# terms of the Apache License Version 2.0 which is available at
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# SPDX-License-Identifier: Apache-2.0
12+
# *******************************************************************************
113
load("@aspect_rules_py//py:defs.bzl", "py_library")
214
load("@docs_as_code_hub_env//:requirements.bzl", "all_requirements")
315
load("//:score_pytest.bzl", "score_pytest")
@@ -36,5 +48,8 @@ score_pytest(
3648
srcs = glob(["tests/*.py"]),
3749
pytest_config = "//:pyproject.toml",
3850
# All requirements already in the library so no need to have it double
39-
deps = [":score_metrics"],
51+
deps = [
52+
":score_metrics",
53+
"@score_docs_as_code//src/extensions/score_metamodel",
54+
],
4055
)

src/extensions/score_metrics/__init__.py

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,24 @@
1+
# *******************************************************************************
2+
# Copyright (c) 2026 Contributors to the Eclipse Foundation
3+
#
4+
# See the NOTICE file(s) distributed with this work for additional
5+
# information regarding copyright ownership.
6+
#
7+
# This program and the accompanying materials are made available under the
8+
# terms of the Apache License Version 2.0 which is available at
9+
# https://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# SPDX-License-Identifier: Apache-2.0
12+
# *******************************************************************************
113
import json
214
import os
315
from pathlib import Path
416
from typing import Any
517

6-
from sphinx.application import Sphinx
7-
from sphinx_needs import logging
818
from score_metrics.traceability_metrics import calculate_full_need_metrics
19+
from sphinx.application import Sphinx
920
from sphinx.environment import BuildEnvironment
21+
from sphinx_needs import logging
1022

1123
from src.extensions.score_metrics.traceability_metrics import CALCULATED_METRICS
1224

@@ -24,7 +36,7 @@
2436
# )
2537
# set_default_include_external(include_external)
2638
#
27-
def calculate_need_metrics(app: Sphinx, env:BuildEnvironment) -> None:
39+
def calculate_need_metrics(app: Sphinx, env: BuildEnvironment) -> None:
2840
"""
2941
This is the single source of truth for traceability metrics. It runs
3042
inside the Sphinx build so it has access to all needs (local + external)
@@ -37,7 +49,7 @@ def calculate_need_metrics(app: Sphinx, env:BuildEnvironment) -> None:
3749
calculate_full_need_metrics(app=app, include_external=include_external)
3850

3951

40-
def _write_metrics_json(app: Sphinx, exception: Any|None) -> None:
52+
def _write_metrics_json(app: Sphinx, exception: Any | None) -> None:
4153
"""
4254
Write a schema-v1 metrics.json alongside needs.json in the build output.
4355
"""
@@ -71,8 +83,8 @@ def setup(app: Sphinx) -> dict[str, str | bool]:
7183
)
7284

7385
# _ = app.connect("config-inited", _configure_traceability_dashboard, priority=498)
74-
_ = app.connect("env-updated", calculate_need_metrics, priority=550)
75-
_ = app.connect("build-finished", _write_metrics_json, priority=501)
86+
_ = app.connect("env-updated", calculate_need_metrics, priority=600)
87+
_ = app.connect("build-finished", _write_metrics_json, priority=550)
7688

7789
return {
7890
"version": "0.1",

src/extensions/score_metrics/sphinx_filters.py

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,10 @@ def func(needs: list[NeedItem], results: list[int], **kwargs) -> None: ...
3838
"""
3939

4040
from __future__ import annotations
41+
from typing import Any
4142

4243
from sphinx_needs.need_item import NeedItem
44+
from score_metrics.traceability_metrics import CALCULATED_METRICS
4345

4446

4547
def _matches_source_selector(need: NeedItem, selector: str) -> bool:
@@ -55,15 +57,15 @@ def _matches_source_selector(need: NeedItem, selector: str) -> bool:
5557

5658

5759
def generic_pie_linked_items(
58-
needs: list[NeedItem], results: list[int], **kwargs: dict[str, str | int | float]
60+
needs: list[NeedItem], results: list[int], **kwargs: Any
5961
) -> None:
6062
"""Count target IDs by whether they are linked by selected source needs.
6163
6264
Arguments are passed via ``arg1`` (target ID prefix), ``arg2`` (source
6365
selector prefix, matched against source ``type`` and ``id``), and ``arg3``
6466
(link field name, default ``complies``).
6567
"""
66-
results = []
68+
results.clear()
6769
id_prefix = str(kwargs.get("arg1", ""))
6870
source_selector = str(kwargs.get("arg2", ""))
6971
link_field = str(kwargs.get("arg3", "complies"))
@@ -90,14 +92,15 @@ def generic_pie_linked_items(
9092

9193

9294
def generic_pie_items_by_tag(
93-
needs: list[NeedItem], results: list[int], **kwargs: dict[str, str | int | float]
95+
needs: list[NeedItem], results: list[int], **kwargs: Any
9496
) -> None:
9597
"""Count tagged items split by whether selected source needs link them.
9698
9799
Arguments are passed via ``arg1`` (tag), ``arg2`` (source selector prefix,
98100
matched against source ``type`` and ``id``), and ``arg3`` (link field
99101
name, default ``complies``).
100102
"""
103+
results.clear()
101104
tag = str(kwargs.get("arg1", ""))
102105
source_selector = str(kwargs.get("arg2", ""))
103106
link_field = str(kwargs.get("arg3", "complies"))
@@ -124,7 +127,7 @@ def generic_pie_items_by_tag(
124127

125128

126129
def generic_pie_items_in_relationships(
127-
needs: list[NeedItem], results: list[int], **kwargs: dict[str, str | int | float]
130+
needs: list[NeedItem], results: list[int], **kwargs: Any
128131
) -> None:
129132
"""Count items of a given type by how many container items reference them.
130133
@@ -143,6 +146,7 @@ def generic_pie_items_in_relationships(
143146
Appends to *results*:
144147
``[not_referenced_count, referenced_once_count, referenced_multiple_count]``
145148
"""
149+
results.clear()
146150
container_type = str(kwargs.get("arg1", ""))
147151
field = str(kwargs.get("arg2", ""))
148152
item_type = str(kwargs.get("arg3", ""))
@@ -167,9 +171,71 @@ def generic_pie_items_in_relationships(
167171
results.append(referenced_multiple)
168172

169173

170-
def get_metrics_from_generated_json(
171-
needs: list[NeedItem], results: list[int], **kwargs: str | int | float
174+
def _get_key_values(results: list[int], argument_paths: list[str]):
175+
"""Append integer metric values selected by colon-separated paths.
176+
177+
Each path is resolved against ``CALCULATED_METRICS`` (for example:
178+
``"overall_metrics:total"``), converted to ``int``, and appended to
179+
``results``.
180+
"""
181+
metrics_json = CALCULATED_METRICS
182+
for raw_path in argument_paths:
183+
path = raw_path.strip()
184+
if not path:
185+
continue
186+
187+
current: Any = metrics_json
188+
for key in path.split(":"):
189+
current = current[key]
190+
191+
results.append(int(current))
192+
193+
194+
def get_metrics_with_overall_total_considered(
195+
needs: list[Any], results: list[int], **kwargs: Any
196+
) -> None:
197+
"""Append selected metrics and compute remainder from overall total.
198+
199+
This function appends ``overall_metrics:total`` first, then appends all
200+
metrics referenced by ``kwargs`` values. Finally, it replaces the first
201+
appended value with the remainder after subtracting all other appended
202+
values.
203+
"""
204+
results.clear()
205+
metrics_json = CALCULATED_METRICS
206+
results.append(int(metrics_json["overall_metrics"]["total"]))
207+
_get_key_values(results, [str(value) for value in kwargs.values()])
208+
results[0] -= sum(results[1:])
209+
print(results)
210+
211+
212+
def get_metrics_with_custom_type_total_considered(
213+
needs: list[Any], results: list[int], **kwargs: Any
172214
) -> None:
173-
metrics_json_path = str(kwargs.get("arg1", "_build/metrics.json"))
174-
# TODO: Read the Metrics.json and give back the data
175-
results
215+
"""Append selected metrics, optionally using a custom total path.
216+
217+
If the last kwarg value ends with ``":total"``, that path is used as
218+
baseline total and all preceding paths are treated as components; the first
219+
result becomes ``total - sum(components)``. Otherwise, all paths are simply
220+
appended as-is.
221+
"""
222+
# Get the 'total' that was specified as the first value
223+
224+
results.clear()
225+
values = [str(value) for value in kwargs.values()]
226+
if values[-1].endswith(":total"):
227+
_get_key_values(results, [values[-1]]) # baseline total
228+
_get_key_values(results, values[:-1]) # components
229+
results[0] -= sum(results[1:])
230+
else:
231+
_get_key_values(results, values)
232+
233+
234+
def get_just_metrics(needs: list[Any], results: list[int], **kwargs: Any) -> None:
235+
"""Append selected metric values without any total/remainder calculation.
236+
237+
All kwarg values are interpreted as colon-separated metric paths and
238+
appended to ``results`` in insertion order.
239+
"""
240+
results.clear()
241+
_get_key_values(results, [str(value) for value in kwargs.values()])

0 commit comments

Comments
 (0)