Implements Issue #11: Silo origin adapter This commit creates the SiloOrigin class that implements the FileOrigin interface introduced in Issue #9, enabling Silo to be used as a document origin in the unified file origin system. ## SiloOrigin Class (silo_origin.py) New Python module providing the FileOrigin implementation for Silo PLM: ### Identity Methods - id(): Returns 'silo' as unique identifier - name(): Returns 'Kindred Silo' for UI display - nickname(): Returns 'Silo' for compact UI elements - icon(): Returns 'silo' icon name - type(): Returns OriginType.PLM (1) ### Workflow Characteristics - tracksExternally(): True - Silo tracks documents in database - requiresAuthentication(): True - Silo requires login ### Capabilities - supportsRevisions(): True - supportsBOM(): True - supportsPartNumbers(): True - supportsAssemblies(): True ### Connection State - connectionState(): Checks auth status and API connectivity - connect(): Triggers Silo_Auth dialog if needed - disconnect(): Calls _client.logout() ### Document Identity (UUID-based tracking) - documentIdentity(): Returns SiloItemId (UUID) as primary identity - documentDisplayId(): Returns SiloPartNumber for human display - ownsDocument(): True if document has SiloItemId or SiloPartNumber ### Core Operations (delegate to existing commands) - newDocument(): Delegates to Silo_New command - openDocument(): Uses find_file_by_part_number or _sync.open_item - saveDocument(): Saves locally + uploads via _client._upload_file - saveDocumentAs(): Triggers migration workflow for local docs ### Extended Operations - commitDocument(): Delegates to Silo_Commit - pullDocument(): Delegates to Silo_Pull - pushDocument(): Delegates to Silo_Push - showInfo(): Delegates to Silo_Info - showBOM(): Delegates to Silo_BOM ### Module Functions - get_silo_origin(): Returns singleton instance - register_silo_origin(): Registers with FreeCADGui.addOrigin() - unregister_silo_origin(): Cleanup function ## UUID Tracking (silo_commands.py) Added SiloItemId property to all locations where Silo properties are set: 1. create_document_for_item() - Assembly objects (line 1115) 2. create_document_for_item() - Fallback Part objects (line 1131) 3. create_document_for_item() - Part objects (line 1145) 4. Silo_New.Activated() - Tagged existing objects (line 1471) The SiloItemId stores the database UUID (Item.ID) which is immutable, while SiloPartNumber remains the human-readable identifier that could theoretically change. Property structure on tracked objects: - SiloItemId: UUID from database (primary tracking key) - SiloPartNumber: Human-readable part number - SiloRevision: Current revision number - SiloItemType: 'part' or 'assembly' ## Workbench Integration (InitGui.py) SiloOrigin is automatically registered when the Silo workbench initializes: def Initialize(self): import silo_commands try: import silo_origin silo_origin.register_silo_origin() except Exception as e: FreeCAD.Console.PrintWarning(...) This makes Silo available as a file origin via: - FreeCADGui.listOrigins() -> includes 'silo' - FreeCADGui.getOrigin('silo') -> returns origin info dict - FreeCADGui.setActiveOrigin('silo') -> sets Silo as active ## Design Decisions 1. **Delegation Pattern**: SiloOrigin delegates to existing Silo commands rather than reimplementing logic, ensuring consistency and easier maintenance. 2. **UUID as Primary Identity**: documentIdentity() returns UUID (SiloItemId) for immutable tracking, while documentDisplayId() returns part number for user display. 3. **Graceful Fallback**: If SiloItemId is not present (legacy docs), falls back to SiloPartNumber for identity/ownership checks. 4. **Exception Handling**: All operations wrapped in try/except to prevent origin system failures from breaking FreeCAD. Refs: #11
103 lines
3.5 KiB
Python
103 lines
3.5 KiB
Python
"""Kindred Silo Workbench - Item database integration for Kindred Create."""
|
|
|
|
import os
|
|
|
|
import FreeCAD
|
|
import FreeCADGui
|
|
|
|
FreeCAD.Console.PrintMessage("Kindred Silo InitGui.py loading...\n")
|
|
|
|
|
|
class SiloWorkbench(FreeCADGui.Workbench):
|
|
"""Kindred Silo workbench for item database integration."""
|
|
|
|
MenuText = "Kindred Silo"
|
|
ToolTip = "Item database and part management for Kindred Create"
|
|
Icon = ""
|
|
|
|
def __init__(self):
|
|
# Resolve icon relative to this file so it works regardless of install location
|
|
icon_path = os.path.join(
|
|
os.path.dirname(os.path.abspath(__file__)), "resources", "icons", "silo.svg"
|
|
)
|
|
if os.path.exists(icon_path):
|
|
self.__class__.Icon = icon_path
|
|
|
|
def Initialize(self):
|
|
"""Called when workbench is first activated."""
|
|
import silo_commands
|
|
|
|
# Register Silo as a file origin in the unified origin system
|
|
try:
|
|
import silo_origin
|
|
|
|
silo_origin.register_silo_origin()
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintWarning(f"Could not register Silo origin: {e}\n")
|
|
|
|
self.toolbar_commands = [
|
|
"Silo_ToggleMode",
|
|
"Separator",
|
|
"Silo_Open",
|
|
"Silo_New",
|
|
"Silo_Save",
|
|
"Silo_Commit",
|
|
"Silo_Pull",
|
|
"Silo_Push",
|
|
"Silo_Info",
|
|
"Silo_BOM",
|
|
"Silo_Settings",
|
|
"Silo_Auth",
|
|
]
|
|
|
|
self.appendToolbar("Silo", self.toolbar_commands)
|
|
self.appendMenu("Silo", self.toolbar_commands)
|
|
|
|
def Activated(self):
|
|
"""Called when workbench is activated."""
|
|
FreeCAD.Console.PrintMessage("Kindred Silo workbench activated\n")
|
|
self._show_shortcut_recommendations()
|
|
|
|
def Deactivated(self):
|
|
pass
|
|
|
|
def GetClassName(self):
|
|
return "Gui::PythonWorkbench"
|
|
|
|
def _show_shortcut_recommendations(self):
|
|
"""Show keyboard shortcut recommendations dialog on first activation."""
|
|
try:
|
|
param_group = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/KindredSilo")
|
|
if param_group.GetBool("ShortcutsShown", False):
|
|
return
|
|
param_group.SetBool("ShortcutsShown", True)
|
|
|
|
from PySide import QtGui
|
|
|
|
msg = """<h3>Welcome to Kindred Silo!</h3>
|
|
<p>For the best experience, set up these keyboard shortcuts:</p>
|
|
<table style="margin: 10px 0;">
|
|
<tr><td><b>Ctrl+O</b></td><td> - </td><td>Silo_Open (Search & Open)</td></tr>
|
|
<tr><td><b>Ctrl+N</b></td><td> - </td><td>Silo_New (Register new item)</td></tr>
|
|
<tr><td><b>Ctrl+S</b></td><td> - </td><td>Silo_Save (Save & upload)</td></tr>
|
|
<tr><td><b>Ctrl+Shift+S</b></td><td> - </td><td>Silo_Commit (Save with comment)</td></tr>
|
|
</table>
|
|
<p><b>To set shortcuts:</b> Tools > Customize > Keyboard</p>
|
|
<p style="color: #888;">This message appears once.</p>"""
|
|
|
|
dialog = QtGui.QMessageBox()
|
|
dialog.setWindowTitle("Silo Keyboard Shortcuts")
|
|
dialog.setTextFormat(QtGui.Qt.RichText)
|
|
dialog.setText(msg)
|
|
dialog.setIcon(QtGui.QMessageBox.Information)
|
|
dialog.addButton("Set Up Now", QtGui.QMessageBox.AcceptRole)
|
|
dialog.addButton("Later", QtGui.QMessageBox.RejectRole)
|
|
if dialog.exec_() == 0:
|
|
FreeCADGui.runCommand("Std_DlgCustomize", 0)
|
|
except Exception as e:
|
|
FreeCAD.Console.PrintWarning("Silo shortcuts dialog: " + str(e) + "\n")
|
|
|
|
|
|
FreeCADGui.addWorkbench(SiloWorkbench())
|
|
FreeCAD.Console.PrintMessage("Silo workbench registered\n")
|