fix: pull assembly dependencies recursively before opening
When pulling an assembly from Silo, the linked component files were not downloaded, causing FreeCAD to report 'Link not restored' errors for every external reference. Add _pull_dependencies() that queries the BOM API to discover child part numbers, then downloads the latest file revision for each child that doesn't already exist locally. Recurses into sub-assemblies. Silo_Pull.Activated() now calls _pull_dependencies() after downloading the assembly file and before opening it, so all PropertyXLink paths resolve correctly.
This commit is contained in:
@@ -1062,6 +1062,65 @@ class SiloPullDialog:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def _pull_dependencies(part_number, progress_callback=None):
|
||||||
|
"""Recursively pull all BOM children that have files on the server.
|
||||||
|
|
||||||
|
Returns list of (part_number, dest_path) tuples for successfully pulled files.
|
||||||
|
Skips children that already exist locally.
|
||||||
|
"""
|
||||||
|
pulled = []
|
||||||
|
try:
|
||||||
|
bom = _client.get_bom(part_number)
|
||||||
|
except Exception as e:
|
||||||
|
FreeCAD.Console.PrintWarning(f"Could not fetch BOM for {part_number}: {e}\n")
|
||||||
|
return pulled
|
||||||
|
|
||||||
|
for entry in bom:
|
||||||
|
child_pn = entry.get("child_part_number")
|
||||||
|
if not child_pn:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Skip if already exists locally
|
||||||
|
existing = find_file_by_part_number(child_pn)
|
||||||
|
if existing and existing.exists():
|
||||||
|
FreeCAD.Console.PrintMessage(f" {child_pn}: already exists at {existing}\n")
|
||||||
|
# Still recurse — this child may itself be an assembly with missing deps
|
||||||
|
_pull_dependencies(child_pn, progress_callback)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Check if this child has a file on the server
|
||||||
|
try:
|
||||||
|
latest = _client.latest_file_revision(child_pn)
|
||||||
|
except Exception:
|
||||||
|
latest = None
|
||||||
|
|
||||||
|
if not latest or not latest.get("file_key"):
|
||||||
|
FreeCAD.Console.PrintMessage(f" {child_pn}: no file on server, skipping\n")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Determine destination path
|
||||||
|
child_desc = entry.get("child_description", "")
|
||||||
|
dest_path = get_cad_file_path(child_pn, child_desc)
|
||||||
|
dest_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
rev_num = latest["revision_number"]
|
||||||
|
FreeCAD.Console.PrintMessage(f" Pulling {child_pn} rev {rev_num}...\n")
|
||||||
|
|
||||||
|
try:
|
||||||
|
ok = _client._download_file(
|
||||||
|
child_pn, rev_num, str(dest_path), progress_callback=progress_callback
|
||||||
|
)
|
||||||
|
if ok:
|
||||||
|
pulled.append((child_pn, dest_path))
|
||||||
|
except Exception as e:
|
||||||
|
FreeCAD.Console.PrintWarning(f" Failed to pull {child_pn}: {e}\n")
|
||||||
|
|
||||||
|
# Recurse into child (it may be a sub-assembly)
|
||||||
|
_pull_dependencies(child_pn, progress_callback)
|
||||||
|
|
||||||
|
return pulled
|
||||||
|
|
||||||
|
|
||||||
class Silo_Pull:
|
class Silo_Pull:
|
||||||
"""Download from MinIO / sync from database."""
|
"""Download from MinIO / sync from database."""
|
||||||
|
|
||||||
@@ -1189,6 +1248,17 @@ class Silo_Pull:
|
|||||||
|
|
||||||
FreeCAD.Console.PrintMessage(f"Pulled revision {rev_num} of {part_number}\n")
|
FreeCAD.Console.PrintMessage(f"Pulled revision {rev_num} of {part_number}\n")
|
||||||
|
|
||||||
|
# Pull assembly dependencies before opening so links resolve
|
||||||
|
if item.get("item_type") == "assembly":
|
||||||
|
progress.setLabelText(f"Pulling dependencies for {part_number}...")
|
||||||
|
progress.setValue(0)
|
||||||
|
progress.show()
|
||||||
|
dep_pulled = _pull_dependencies(part_number, progress_callback=on_progress)
|
||||||
|
progress.setValue(100)
|
||||||
|
progress.close()
|
||||||
|
if dep_pulled:
|
||||||
|
FreeCAD.Console.PrintMessage(f"Pulled {len(dep_pulled)} dependency file(s)\n")
|
||||||
|
|
||||||
# Close existing document if open, then reopen
|
# Close existing document if open, then reopen
|
||||||
if doc and doc.FileName == str(dest_path):
|
if doc and doc.FileName == str(dest_path):
|
||||||
FreeCAD.closeDocument(doc.Name)
|
FreeCAD.closeDocument(doc.Name)
|
||||||
|
|||||||
Reference in New Issue
Block a user