89 lines
3.0 KiB
Python
89 lines
3.0 KiB
Python
"""Source-derived FeatureScript operation coverage inventory.
|
|
|
|
The registry records what the current source corpus actually contains. It is
|
|
deliberately independent from conversion diagnostics: an old output directory
|
|
cannot make an operation appear supported, and a planned operation cannot be
|
|
reported as observed merely because its name is listed in the roadmap.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from collections import Counter, defaultdict
|
|
from pathlib import Path
|
|
from typing import Any, Iterable
|
|
|
|
from .dataset import Sample
|
|
from .featurescript_parser import parse_featurescript
|
|
|
|
|
|
REGISTRY_SCHEMA = "cadfs_to_cdsl.operation_registry.v1"
|
|
|
|
# These are roadmap work items, not parser aliases or support declarations.
|
|
P3_OPERATION_WORK_ITEMS = (
|
|
"draft",
|
|
"thicken",
|
|
"split",
|
|
"moveFace",
|
|
"deleteFace",
|
|
"replaceFace",
|
|
"transform",
|
|
"derive",
|
|
"import",
|
|
"bend_add",
|
|
)
|
|
|
|
|
|
def build_operation_registry(samples: Iterable[Sample]) -> dict[str, Any]:
|
|
"""Parse every available FeatureScript source and inventory feature calls.
|
|
|
|
Parse failures and missing FeatureScript modalities are recorded separately
|
|
instead of being treated as a zero-count result. Sample examples remain
|
|
bounded only for report size; the count is computed from every feature.
|
|
"""
|
|
counts: Counter[str] = Counter()
|
|
sample_ids: dict[str, set[str]] = defaultdict(set)
|
|
parse_failures: list[dict[str, str]] = []
|
|
source_sample_count = 0
|
|
|
|
for sample in samples:
|
|
source_path = sample.files.get("featurescript")
|
|
if not source_path:
|
|
continue
|
|
source_sample_count += 1
|
|
try:
|
|
model = parse_featurescript(Path(source_path).read_text(encoding="utf-8"), sample.sample_id)
|
|
except Exception as error:
|
|
parse_failures.append({"sample_id": sample.sample_id, "error": f"{type(error).__name__}: {error}"})
|
|
continue
|
|
for feature in model.features:
|
|
counts[feature.operation] += 1
|
|
sample_ids[feature.operation].add(sample.sample_id)
|
|
|
|
operations: dict[str, dict[str, Any]] = {}
|
|
for operation in sorted(counts):
|
|
operations[operation] = {
|
|
"state": "observed",
|
|
"feature_count": counts[operation],
|
|
"sample_count": len(sample_ids[operation]),
|
|
"sample_ids": sorted(sample_ids[operation]),
|
|
}
|
|
for operation in P3_OPERATION_WORK_ITEMS:
|
|
if operation in operations:
|
|
operations[operation]["roadmap_work_item"] = True
|
|
else:
|
|
operations[operation] = {
|
|
"state": "not_observed_in_current_source",
|
|
"feature_count": 0,
|
|
"sample_count": 0,
|
|
"sample_ids": [],
|
|
"roadmap_work_item": True,
|
|
}
|
|
|
|
return {
|
|
"schema": REGISTRY_SCHEMA,
|
|
"source_sample_count": source_sample_count,
|
|
"parse_failure_count": len(parse_failures),
|
|
"parse_failures": parse_failures,
|
|
"operations": operations,
|
|
}
|