Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 115 additions & 2 deletions tests/test_graph_kpi_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,46 @@ def test_build_graph_kpi_api_payload_is_numeric_except_snapshot_metadata(
assert payload["snapshot_date"] == "2026-06-29"
assert payload["calculated_at"] == "2026-06-29T10:00:00Z"
assert payload["snapshot_origin"] == "worai_graph_kpis"
assert payload["schema_compliance"]["urls_checked"] == 2
assert payload["schema_compliance"]["errors"] == 2
assert payload["all_total_entities"] == 5
assert payload["all_total_typed_entities"] == 5
assert payload["all_total_triples"] == 13
assert payload["all_total_properties"] == 8
assert payload["all_unique_properties_count"] == 3
assert payload["all_rdf_type_triples_count"] == 5
assert payload["all_internal_nodes_count"] == 5
assert payload["all_internal_edges_count"] == 2
assert payload["all_edge_predicate_counts"]["schema:brand"] == 2
assert payload["all_unique_urls_count"] == 2
assert payload["all_orphans_count"] == 2
assert payload["all_broken_links_count"] == 1
assert payload["all_isolated_graphs_count"] == 4
assert payload["all_largest_component_nodes_count"] == 2
assert payload["all_duplicates_count"] == 1
assert payload["all_duplicate_extra_entities_count"] == 1
assert payload["rich_snippets_candidate_count"] == 3
assert payload["rich_snippets_by_type"]["Product"] == 3
assert payload["schema_compliance_errors"] == 2
assert payload["schema_compliance_urls_checked"] == 2
assert payload["schema_compliance_urls_with_errors"] == 1
assert payload["graph_health_score"] == 43
assert payload["all_entity_types"]["Product"] == 3
assert payload["all_properties_by_predicate"]["schema:url"] == 3
assert "orphan_entity_examples" not in payload
assert "broken_internal_edge_examples" not in payload
assert "total_entities" not in payload
assert "total_triples" not in payload
assert "internal_edges" not in payload
assert "unique_urls_within_website_scope" not in payload
assert "broken_internal_edges" not in payload
assert "duplicate_url_groups" not in payload
assert "schema_compliance" not in payload
assert "entity_type_counts" not in payload
assert "property_counts" not in payload
assert "rich_snippet_candidate_entities" not in payload
assert "public_total_entities" not in payload
assert "private_total_entities" not in payload
assert "public_internal_edges_count" not in payload
assert "private_internal_edges_count" not in payload

def assert_numeric_kpis(value, path: str = "") -> None:
if path in {"snapshot_date", "calculated_at", "snapshot_origin"}:
Expand All @@ -144,6 +180,83 @@ def assert_numeric_kpis(value, path: str = "") -> None:
assert_numeric_kpis(payload)


def test_graph_health_score_is_normalized_by_metric_denominators() -> None:
small_payload = build_graph_kpi_api_payload(
{
"totals": {
"total_entity_count": 100,
"total_property_count": 300,
"total_triples": 1_000,
"unique_urls_within_website_scope": 10,
},
"edges": {"total_internal_edges": 20},
"integrity": {
"broken_internal_edge_count": 2,
"duplicate_url_group_count": 1,
},
"schema_compliance": {
"urls_checked": 10,
"errors": 1,
"warnings": 2,
"google_merchant_eligible": 8,
"google_merchant_not_eligible": 2,
},
},
snapshot_date="2026-06-29",
)
large_payload = build_graph_kpi_api_payload(
{
"totals": {
"total_entity_count": 1_000,
"total_property_count": 3_000,
"total_triples": 10_000,
"unique_urls_within_website_scope": 100,
},
"edges": {"total_internal_edges": 200},
"integrity": {
"broken_internal_edge_count": 20,
"duplicate_url_group_count": 10,
},
"schema_compliance": {
"urls_checked": 100,
"errors": 10,
"warnings": 20,
"google_merchant_eligible": 80,
"google_merchant_not_eligible": 20,
},
},
snapshot_date="2026-06-29",
)

assert small_payload["graph_health_score"] == large_payload["graph_health_score"]
assert small_payload["graph_health_score"] == 94


def test_graph_health_score_uses_safe_defaults_for_missing_denominators() -> None:
payload = build_graph_kpi_api_payload(
{
"totals": {
"total_entity_count": 10,
"total_property_count": 20,
"total_triples": 30,
},
"edges": {"total_internal_edges": 0},
"integrity": {
"broken_internal_edge_count": 5,
"duplicate_url_group_count": 5,
},
"schema_compliance": {
"urls_checked": 0,
"errors": 5,
"warnings": 5,
},
},
snapshot_date="2026-06-29",
)

assert payload["graph_health_score"] == 100


def test_build_graph_kpi_api_payload_rejects_non_numeric_kpi_values() -> None:
with pytest.raises(ValueError):
build_graph_kpi_api_payload(
Expand Down
144 changes: 109 additions & 35 deletions wordlift_sdk/graph/audit/_kpi_snapshot.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,50 +111,124 @@ def build_graph_kpi_api_payload(
topology = snapshot.get("topology") or {}
compliance = snapshot.get("schema_compliance") or {}

all_total_entities = totals.get("total_entity_count", 0)
all_total_typed_entities = totals.get("total_typed_entity_count", 0)
all_total_properties = totals.get("total_property_count", 0)
all_total_triples = totals.get("total_triples", 0)
all_unique_properties_count = totals.get("unique_property_count", 0)
all_rdf_type_triples_count = totals.get("rdf_type_triples", 0)
all_internal_edges_count = edges.get("total_internal_edges", 0)
all_unique_urls_count = totals.get("unique_urls_within_website_scope", 0)
all_broken_links_count = integrity.get("broken_internal_edge_count", 0)
all_duplicates_count = integrity.get("duplicate_url_group_count", 0)
all_duplicate_extra_entities_count = integrity.get(
"duplicate_extra_entity_count", 0
)
schema_compliance_errors = compliance.get("errors", 0)
schema_compliance_warnings = compliance.get("warnings", 0)
schema_compliance_urls_checked = compliance.get("urls_checked", 0)
rich_snippets_valid_count = compliance.get("google_merchant_eligible", 0)
rich_snippets_invalid_count = compliance.get("google_merchant_not_eligible", 0)
rich_snippet_candidate_entities = (
snapshot.get("rich_snippet_candidate_entities") or {}
)

payload: dict[str, Any] = {
"snapshot_date": snapshot_date_value,
"calculated_at": calculated_at_value,
"snapshot_origin": snapshot_origin,
"total_triples": totals.get("total_triples", 0),
"total_entities": totals.get("total_entity_count", 0),
"total_typed_entities": totals.get("total_typed_entity_count", 0),
"total_properties": totals.get("total_property_count", 0),
"unique_properties": totals.get("unique_property_count", 0),
"rdf_type_triples": totals.get("rdf_type_triples", 0),
"unique_urls_within_website_scope": totals.get(
"unique_urls_within_website_scope", 0
"all_total_entities": all_total_entities,
"all_total_typed_entities": all_total_typed_entities,
"all_total_properties": all_total_properties,
"all_total_triples": all_total_triples,
"all_unique_properties_count": all_unique_properties_count,
"all_rdf_type_triples_count": all_rdf_type_triples_count,
"all_internal_nodes_count": connectivity.get("internal_node_count", 0),
"all_internal_edges_count": all_internal_edges_count,
"all_external_edges_count": 0,
"all_edges_count": all_internal_edges_count,
"all_edge_predicate_counts": edges.get("edge_predicate_counts") or {},
"all_edge_node_ratio": edges.get("edge_to_node_ratio", 0),
"all_unique_urls_count": all_unique_urls_count,
"all_orphans_count": connectivity.get("orphan_entity_count", 0),
"all_broken_links_count": all_broken_links_count,
"all_isolated_graphs_count": topology.get("isolated_graph_count", 0),
"all_largest_component_nodes_count": topology.get(
"largest_component_node_count", 0
),
"entity_type_counts": snapshot.get("entity_type_counts") or {},
"property_counts": snapshot.get("property_counts") or {},
"rich_snippet_candidate_entities": snapshot.get(
"rich_snippet_candidate_entities"
)
or {},
"internal_edges": edges.get("total_internal_edges", 0),
"edge_predicate_counts": edges.get("edge_predicate_counts") or {},
"edge_to_node_ratio": edges.get("edge_to_node_ratio", 0),
"internal_nodes": connectivity.get("internal_node_count", 0),
"orphan_entities": connectivity.get("orphan_entity_count", 0),
"broken_internal_edges": integrity.get("broken_internal_edge_count", 0),
"duplicate_url_groups": integrity.get("duplicate_url_group_count", 0),
"duplicate_extra_entities": integrity.get("duplicate_extra_entity_count", 0),
"isolated_graph_components": topology.get("isolated_graph_count", 0),
"largest_component_nodes": topology.get("largest_component_node_count", 0),
"schema_compliance": {
"urls_checked": compliance.get("urls_checked", 0),
"urls_with_errors": compliance.get("urls_with_errors", 0),
"urls_with_warnings": compliance.get("urls_with_warnings", 0),
"errors": compliance.get("errors", 0),
"warnings": compliance.get("warnings", 0),
"google_merchant_eligible": compliance.get("google_merchant_eligible", 0),
"google_merchant_not_eligible": compliance.get(
"google_merchant_not_eligible", 0
),
},
"all_duplicates_count": all_duplicates_count,
"all_duplicate_extra_entities_count": all_duplicate_extra_entities_count,
"rich_snippets_candidate_count": rich_snippet_candidate_entities.get(
"total", 0
),
"rich_snippets_valid_count": rich_snippets_valid_count,
"rich_snippets_invalid_count": rich_snippets_invalid_count,
"rich_snippets_by_type": rich_snippet_candidate_entities.get("by_type", {}),
"schema_compliance_errors": schema_compliance_errors,
"schema_compliance_warnings": schema_compliance_warnings,
"schema_compliance_urls_checked": schema_compliance_urls_checked,
"schema_compliance_urls_with_errors": compliance.get("urls_with_errors", 0),
"schema_compliance_urls_with_warnings": compliance.get("urls_with_warnings", 0),
"all_entity_types": snapshot.get("entity_type_counts") or {},
"all_properties_by_predicate": snapshot.get("property_counts") or {},
}
payload["graph_health_score"] = _calculate_graph_health_score(payload)
return _numeric_only(payload)


def _calculate_graph_health_score(metrics: dict[str, Any]) -> int:
score_value = (
100
- _safe_rate(
metrics.get("schema_compliance_errors"),
metrics.get("schema_compliance_urls_checked"),
)
* 35
- _safe_rate(
metrics.get("schema_compliance_warnings"),
metrics.get("schema_compliance_urls_checked"),
)
* 10
- _safe_rate(
metrics.get("all_broken_links_count"),
metrics.get("all_internal_edges_count"),
)
* 25
- _safe_rate(
metrics.get("all_duplicates_count"),
metrics.get("all_unique_urls_count"),
)
* 20
+ _rich_snippet_valid_rate(metrics) * 5
)
score = int(score_value + 0.5)
return max(0, min(100, score))


def _rich_snippet_valid_rate(metrics: dict[str, Any]) -> float:
valid = _to_float(metrics.get("rich_snippets_valid_count"))
invalid = _to_float(metrics.get("rich_snippets_invalid_count"))
total = valid + invalid
if total <= 0:
return 0
return valid / total


def _safe_rate(numerator: Any, denominator: Any) -> float:
denominator_value = _to_float(denominator)
if denominator_value <= 0:
return 0
return _to_float(numerator) / denominator_value


def _to_float(value: Any) -> float:
if isinstance(value, bool):
return 0
if isinstance(value, (int, float)):
return float(value)
return 0


def schema_term(uri: Any) -> str | None:
value = str(uri)
if value.startswith(SCHEMA):
Expand Down
Loading