feat: BOM auto-extraction and manifest field population (#276, #277)
All checks were successful
Build and Test / build (pull_request) Successful in 29m22s
All checks were successful
Build and Test / build (pull_request) Successful in 29m22s
Documentation updates: - KNOWN_ISSUES.md: correct #6 (datum handling), resolve #10 (delete_bom_entry) and #11 (silo icons), fix stale formatDate reference, mark completed next steps, add new next steps. - INTEGRATION_PLAN.md: correct ztools SDK migration claim. kc_format.py (#277): - New _manifest_enrich_hook: populates part_uuid from SiloItemId and silo_instance from Silo API URL on every .kc save. - New update_manifest_fields(): public API to update manifest fields in an already-saved .kc ZIP (used for post-upload revision_hash). mods/silo submodule (#276): - New bom_sync.py extraction engine. - Post-commit hooks for BOM sync and manifest revision update. - SSE bom_merged signal + Activity pane handler. - merge_bom_json client method (forward-looking). Refs: #276, #277
This commit is contained in:
@@ -45,6 +45,51 @@ def _metadata_save_hook(doc, filename, entries):
|
||||
register_pre_reinject(_metadata_save_hook)
|
||||
|
||||
|
||||
def _manifest_enrich_hook(doc, filename, entries):
|
||||
"""Populate silo_instance and part_uuid from the tracked Silo object."""
|
||||
raw = entries.get("silo/manifest.json")
|
||||
if raw is None:
|
||||
return
|
||||
try:
|
||||
manifest = json.loads(raw)
|
||||
except (json.JSONDecodeError, ValueError):
|
||||
return
|
||||
|
||||
changed = False
|
||||
|
||||
# Populate part_uuid from SiloItemId if available.
|
||||
for obj in doc.Objects:
|
||||
if hasattr(obj, "SiloItemId") and obj.SiloItemId:
|
||||
if manifest.get("part_uuid") != obj.SiloItemId:
|
||||
manifest["part_uuid"] = obj.SiloItemId
|
||||
changed = True
|
||||
break
|
||||
|
||||
# Populate silo_instance from Silo settings.
|
||||
if not manifest.get("silo_instance"):
|
||||
try:
|
||||
import silo_commands
|
||||
|
||||
api_url = silo_commands._get_api_url()
|
||||
if api_url:
|
||||
# Strip /api suffix to get base instance URL.
|
||||
instance = api_url.rstrip("/")
|
||||
if instance.endswith("/api"):
|
||||
instance = instance[:-4]
|
||||
manifest["silo_instance"] = instance
|
||||
changed = True
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if changed:
|
||||
entries["silo/manifest.json"] = (json.dumps(manifest, indent=2) + "\n").encode(
|
||||
"utf-8"
|
||||
)
|
||||
|
||||
|
||||
register_pre_reinject(_manifest_enrich_hook)
|
||||
|
||||
|
||||
KC_VERSION = "1.0"
|
||||
|
||||
|
||||
@@ -133,6 +178,34 @@ class _KcFormatObserver:
|
||||
)
|
||||
|
||||
|
||||
def update_manifest_fields(filename, updates):
|
||||
"""Update fields in an existing .kc manifest after save.
|
||||
|
||||
*filename*: path to the .kc file.
|
||||
*updates*: dict of field_name -> value to merge into the manifest.
|
||||
|
||||
Used by silo_commands to write ``revision_hash`` after a successful
|
||||
upload (which happens after the ZIP has already been written by save).
|
||||
"""
|
||||
if not filename or not filename.lower().endswith(".kc"):
|
||||
return
|
||||
if not os.path.isfile(filename):
|
||||
return
|
||||
try:
|
||||
with zipfile.ZipFile(filename, "a") as zf:
|
||||
if "silo/manifest.json" not in zf.namelist():
|
||||
return
|
||||
raw = zf.read("silo/manifest.json")
|
||||
manifest = json.loads(raw)
|
||||
manifest.update(updates)
|
||||
zf.writestr(
|
||||
"silo/manifest.json",
|
||||
json.dumps(manifest, indent=2) + "\n",
|
||||
)
|
||||
except Exception as e:
|
||||
FreeCAD.Console.PrintWarning(f"kc_format: failed to update manifest: {e}\n")
|
||||
|
||||
|
||||
def register():
|
||||
"""Connect to application-level save signals."""
|
||||
FreeCAD.addDocumentObserver(_KcFormatObserver())
|
||||
|
||||
Reference in New Issue
Block a user