118 lines
5.0 KiB
Python
118 lines
5.0 KiB
Python
#!/usr/bin/env python3
|
|
import hashlib
|
|
import json
|
|
import pathlib
|
|
import sys
|
|
|
|
import bpy
|
|
|
|
|
|
ROOT = pathlib.Path(__file__).resolve().parents[3]
|
|
PARITY_MAP = ROOT / "tests/golden/WBV2-P0-002/parity-map.json"
|
|
|
|
|
|
def digest_file(path):
|
|
return hashlib.sha256(path.read_bytes()).hexdigest()
|
|
|
|
|
|
def risk_score(capability):
|
|
levels = {"L0": 0, "L1": 1, "L2": 2, "L3": 3}
|
|
return (capability["legacyPending"] * 100 + capability["legacyActive"] * 50 +
|
|
capability["entryCount"] * 10 + levels[capability["targetParityLevel"]] * 1000)
|
|
|
|
|
|
def build_receipt():
|
|
parity = json.loads(PARITY_MAP.read_text(encoding="utf-8"))
|
|
if parity.get("schemaVersion") != 1 or parity.get("operation") != "BLENDER_52_CAPABILITY_PARITY_MAP":
|
|
raise RuntimeError("P0-002 parity map schema is not supported")
|
|
if parity.get("summary", {}).get("featureParityClaims") != 0 or parity["summary"].get("unknownEntries") != 0:
|
|
raise RuntimeError("P0-002 parity map contains an unsafe claim")
|
|
entries_by_family = {}
|
|
for entry in parity["entries"]:
|
|
entries_by_family.setdefault(entry["family"], []).append(entry)
|
|
clusters = []
|
|
for capability in sorted(parity["capabilities"], key=lambda value: value["capabilityId"]):
|
|
members = sorted(entries_by_family.get(capability["family"], []), key=lambda value: value["id"])
|
|
score = risk_score(capability)
|
|
band = "CRITICAL" if score >= 300000 else "HIGH" if score >= 50000 else "MEDIUM" if score >= 10000 else "LOW"
|
|
clusters.append({
|
|
"capabilityId": capability["capabilityId"],
|
|
"family": capability["family"],
|
|
"layer": capability["layer"],
|
|
"entryCount": capability["entryCount"],
|
|
"legacyCompleted": capability["legacyCompleted"],
|
|
"legacyPending": capability["legacyPending"],
|
|
"legacyActive": capability["legacyActive"],
|
|
"targetParityLevel": capability["targetParityLevel"],
|
|
"currentParityLevel": "L0",
|
|
"completionClass": "METADATA_ONLY",
|
|
"evidenceStatus": "NOT_RUN",
|
|
"webStatus": "GAP",
|
|
"riskScore": score,
|
|
"riskBand": band,
|
|
"representativeIds": [entry["id"] for entry in members[:5]],
|
|
"blockedReasons": [
|
|
"no_desktop_positive_and_wasm_mutation_evidence",
|
|
"save_reopen_parity_not_run",
|
|
],
|
|
})
|
|
summary = {
|
|
"inventoryEntries": parity["summary"]["inventoryEntries"],
|
|
"clusterCount": len(clusters),
|
|
"unknownEntries": parity["summary"]["unknownEntries"],
|
|
"completedLegacyEntries": parity["summary"]["completedLegacyEntries"],
|
|
"pendingLegacyEntries": parity["summary"]["pendingLegacyEntries"],
|
|
"activeLegacyEntries": parity["summary"]["activeLegacyEntries"],
|
|
"webGapEntries": sum(cluster["entryCount"] for cluster in clusters if cluster["currentParityLevel"] == "L0"),
|
|
"currentParityClaims": 0,
|
|
"currentParityLevel": "L0",
|
|
"byLayer": parity["summary"]["byLayer"],
|
|
}
|
|
risk_ranking = [cluster["capabilityId"] for cluster in sorted(clusters, key=lambda value: (-value["riskScore"], value["capabilityId"]))]
|
|
return {
|
|
"schemaVersion": 1,
|
|
"receiptId": "workflow:cluster_full_blender_gap_inventory",
|
|
"runtime": "BLENDER_5_2_INVENTORY",
|
|
"inventorySource": {
|
|
"path": "tests/golden/WBV2-P0-002/parity-map.json",
|
|
"sha256": digest_file(PARITY_MAP),
|
|
"entryCount": parity["summary"]["inventoryEntries"],
|
|
},
|
|
"classificationPolicy": {
|
|
"featureParityRequires": ["desktopPositive", "wasmMutation", "saveReopenExact"],
|
|
"negativeOnlyClasses": ["NEGATIVE_BOUNDARY", "UNSUPPORTED_DECLARED", "BLOCKED"],
|
|
"unprovenClass": "METADATA_ONLY",
|
|
"parityLevel": "L0",
|
|
},
|
|
"clusters": clusters,
|
|
"summary": summary,
|
|
"riskRanking": risk_ranking,
|
|
"nextTask": "WBV2-P7-002",
|
|
}
|
|
|
|
|
|
def main(output):
|
|
bpy.ops.wm.read_factory_settings(use_empty=True)
|
|
scene = bpy.context.scene
|
|
scene.name = "WBV2P7001GapInventoryScene"
|
|
scene.render.engine = "BLENDER_EEVEE"
|
|
scene.render.resolution_x = 32
|
|
scene.render.resolution_y = 32
|
|
scene.render.resolution_percentage = 100
|
|
bpy.ops.mesh.primitive_cube_add(size=2)
|
|
cube = bpy.context.object
|
|
cube.name = "WBV2P7001GapInventoryCube"
|
|
cube.data.name = "WBV2P7001GapInventoryMesh"
|
|
receipt = build_receipt()
|
|
text = bpy.data.texts.new("WBV2-P7-001-ClusterFullBlenderGapInventoryReceipt")
|
|
text.write(json.dumps(receipt, sort_keys=True, separators=(",", ":")))
|
|
scene.frame_set(1)
|
|
bpy.ops.wm.save_as_mainfile(filepath=str(pathlib.Path(output).resolve()), check_existing=False, compress=True)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
arguments = sys.argv[sys.argv.index("--") + 1 :]
|
|
if len(arguments) != 1:
|
|
raise SystemExit("usage: blender -b --factory-startup --python WBV2-P7-001.py -- OUTPUT")
|
|
main(arguments[0])
|