Compare commits
5 Commits
be8783bf0a
...
fed72676bc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fed72676bc | ||
| 91f539a18a | |||
| 2ddfea083a | |||
| 069bb7a552 | |||
| 8a6e5cdffa |
@@ -206,41 +206,45 @@ def get_projects_dir() -> Path:
|
||||
def get_cad_file_path(part_number: str, description: str = "") -> Path:
|
||||
"""Generate canonical file path for a CAD file.
|
||||
|
||||
Path format: ~/projects/cad/{category_code}_{category_name}/{part_number}_{description}.FCStd
|
||||
Path format: ~/projects/cad/{category_code}_{category_name}/{part_number}_{description}.kc
|
||||
"""
|
||||
category, _ = parse_part_number(part_number)
|
||||
folder_name = get_category_folder_name(category)
|
||||
|
||||
if description:
|
||||
filename = f"{part_number}_{sanitize_filename(description)}.FCStd"
|
||||
filename = f"{part_number}_{sanitize_filename(description)}.kc"
|
||||
else:
|
||||
filename = f"{part_number}.FCStd"
|
||||
filename = f"{part_number}.kc"
|
||||
|
||||
return get_projects_dir() / "cad" / folder_name / filename
|
||||
|
||||
|
||||
def find_file_by_part_number(part_number: str) -> Optional[Path]:
|
||||
"""Find existing CAD file for a part number."""
|
||||
"""Find existing CAD file for a part number. Prefers .kc over .FCStd."""
|
||||
category, _ = parse_part_number(part_number)
|
||||
folder_name = get_category_folder_name(category)
|
||||
cad_dir = get_projects_dir() / "cad" / folder_name
|
||||
|
||||
if cad_dir.exists():
|
||||
matches = list(cad_dir.glob(f"{part_number}*.FCStd"))
|
||||
if matches:
|
||||
return matches[0]
|
||||
|
||||
base_cad_dir = get_projects_dir() / "cad"
|
||||
if base_cad_dir.exists():
|
||||
for subdir in base_cad_dir.iterdir():
|
||||
if subdir.is_dir():
|
||||
matches = list(subdir.glob(f"{part_number}*.FCStd"))
|
||||
if matches:
|
||||
return matches[0]
|
||||
for search_dir in _search_dirs(cad_dir):
|
||||
for ext in ("*.kc", "*.FCStd"):
|
||||
matches = list(search_dir.glob(f"{part_number}{ext[1:]}"))
|
||||
if matches:
|
||||
return matches[0]
|
||||
|
||||
return None
|
||||
|
||||
|
||||
def _search_dirs(category_dir: Path):
|
||||
"""Yield the category dir, then all sibling dirs under cad/."""
|
||||
if category_dir.exists():
|
||||
yield category_dir
|
||||
base_cad_dir = category_dir.parent
|
||||
if base_cad_dir.exists():
|
||||
for subdir in base_cad_dir.iterdir():
|
||||
if subdir.is_dir() and subdir != category_dir:
|
||||
yield subdir
|
||||
|
||||
|
||||
def search_local_files(search_term: str = "", category_filter: str = "") -> list:
|
||||
"""Search for CAD files in local cad directory."""
|
||||
results = []
|
||||
@@ -260,7 +264,9 @@ def search_local_files(search_term: str = "", category_filter: str = "") -> list
|
||||
if category_filter and category_code.upper() != category_filter.upper():
|
||||
continue
|
||||
|
||||
for fcstd_file in category_dir.glob("*.FCStd"):
|
||||
for fcstd_file in sorted(
|
||||
list(category_dir.glob("*.kc")) + list(category_dir.glob("*.FCStd"))
|
||||
):
|
||||
filename = fcstd_file.stem
|
||||
parts = filename.split("_", 1)
|
||||
part_number = parts[0]
|
||||
@@ -965,6 +971,65 @@ class SiloPullDialog:
|
||||
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:
|
||||
"""Download from MinIO / sync from database."""
|
||||
|
||||
@@ -1092,6 +1157,17 @@ class Silo_Pull:
|
||||
|
||||
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
|
||||
if doc and doc.FileName == str(dest_path):
|
||||
FreeCAD.closeDocument(doc.Name)
|
||||
|
||||
Reference in New Issue
Block a user