Skip to content

Commit

Permalink
Merge pull request #217 from specklesystems/bilal/cnx-890-replace-pla…
Browse files Browse the repository at this point in the history
…ceholder-model-info

Bilal/cnx 890 replace placeholder model info
  • Loading branch information
bimgeek authored Jan 15, 2025
2 parents 0c68eb1 + fd46280 commit d862ace
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 30 deletions.
96 changes: 71 additions & 25 deletions bpy_speckle/ui/model_selection_dialog.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import bpy
from bpy.types import UILayout, Context, UIList, PropertyGroup, Operator, Event
from typing import List, Tuple
from bpy.types import UILayout, Context, UIList, PropertyGroup, Operator, Event, WindowManager
from .mouse_position_mixin import MousePositionMixin
from ..utils.model_manager import get_models_for_project

class speckle_model(bpy.types.PropertyGroup):
"""
PropertyGroup for storing models.
This PropertyGroup is used to store information about a model,
such as its name, source application, and update time.
such as its name, ID and update time.
These are then used in the model selection dialog.
"""
name: bpy.props.StringProperty()
source_app: bpy.props.StringProperty(name="Source")
id: bpy.props.StringProperty(name="ID")
updated: bpy.props.StringProperty(name="Updated")

class SPECKLE_UL_models_list(bpy.types.UIList):
Expand All @@ -30,7 +30,7 @@ def draw_item(self, context: Context, layout: UILayout, data: PropertyGroup, ite
split.label(text=item.name)

right_split = split.split(factor=0.25)
right_split.label(text=item.source_app)
right_split.label(text=item.id)
right_split.label(text=item.updated)
# This handles when the list is in a grid layout
elif self.layout_type == 'GRID':
Expand All @@ -44,10 +44,29 @@ class SPECKLE_OT_model_selection_dialog(MousePositionMixin, bpy.types.Operator):
bl_idname = "speckle.model_selection_dialog"
bl_label = "Select Model"

def update_models_list(self, context):
wm = context.window_manager
# Clear existing models
wm.speckle_models.clear()

# Get models for the selected project, using search if provided
search = self.search_query if self.search_query.strip() else None
models = get_models_for_project(wm.selected_account_id, self.project_id, search=search)

# Populate models list
for name, id, updated in models:
model = wm.speckle_models.add()
model.name = name
model.updated = updated
model.id = id

return None

search_query: bpy.props.StringProperty(
name="Search",
description="Search a project",
default=""
description="Search a model",
default="",
update=update_models_list
)

project_name: bpy.props.StringProperty(
Expand All @@ -56,32 +75,44 @@ class SPECKLE_OT_model_selection_dialog(MousePositionMixin, bpy.types.Operator):
default=""
)

models: List[Tuple[str, str, str]] = [
("94-workset name", "RVT", "1 day ago"),
("296/skp2skp3", "SKP", "16 days ago"),
("49/rhn2viewer", "RHN", "21 days ago"),
]
project_id: bpy.props.StringProperty(
name="Project ID",
description="The ID of the project to select",
default=""
)

model_index: bpy.props.IntProperty(name="Model Index", default=0)

def execute(self, context: Context) -> set[str]:
selected_model = context.scene.speckle_state.models[self.model_index]
selected_model = context.window_manager.speckle_models[self.model_index]
if context.scene.speckle_state.ui_mode == "PUBLISH":
bpy.ops.speckle.selection_filter_dialog("INVOKE_DEFAULT", project_name=self.project_name, model_name=selected_model.name)
bpy.ops.speckle.selection_filter_dialog("INVOKE_DEFAULT",
project_name=self.project_name,
project_id=self.project_id,
model_name=selected_model.name,
model_id=selected_model.id)
elif context.scene.speckle_state.ui_mode == "LOAD":
bpy.ops.speckle.version_selection_dialog("INVOKE_DEFAULT", project_name=self.project_name, model_name=selected_model.name)
bpy.ops.speckle.version_selection_dialog("INVOKE_DEFAULT",
project_name=self.project_name,
project_id=self.project_id,
model_name=selected_model.name,
model_id=selected_model.id)
return {'FINISHED'}

def invoke(self, context: Context, event: Event) -> set[str]:
# Clear existing models
context.scene.speckle_state.models.clear()
# Populate with new projects
for name, source_app, updated in self.models:
model = context.scene.speckle_state.models.add()
model.name = name
model.source_app = source_app
model.updated = updated
wm = context.window_manager

# Ensure WindowManager has the projects collection
if not hasattr(WindowManager, "speckle_models"):
# Register the collection property
WindowManager.speckle_models = bpy.props.CollectionProperty(type=speckle_model)

# Clear existing models
wm.speckle_models.clear()

# Update models list
self.update_models_list(context)

# Store the original mouse position
self.init_mouse_position(context, event)

Expand All @@ -90,14 +121,29 @@ def invoke(self, context: Context, event: Event) -> set[str]:
def draw(self, context: Context) -> None:
layout : UILayout = self.layout
layout.label(text=f"Project: {self.project_name}")

# Search field
row = layout.row(align=True)
row.prop(self, "search_query", icon='VIEWZOOM', text="")

# Models UIList
layout.template_list("SPECKLE_UL_models_list", "", context.scene.speckle_state, "models", self, "model_index")
layout.template_list("SPECKLE_UL_models_list", "", context.window_manager, "speckle_models", self, "model_index")

layout.separator()

# Move cursor to original position
self.restore_mouse_position(context)
self.restore_mouse_position(context)

def register():
bpy.utils.register_class(speckle_model)
bpy.utils.register_class(SPECKLE_UL_models_list)
bpy.utils.register_class(SPECKLE_OT_model_selection_dialog)

def unregister():
# Clean up WindowManager properties
if hasattr(WindowManager, "speckle_models"):
del WindowManager.speckle_models

bpy.utils.unregister_class(SPECKLE_OT_model_selection_dialog)
bpy.utils.unregister_class(SPECKLE_UL_models_list)
bpy.utils.unregister_class(speckle_model)
14 changes: 11 additions & 3 deletions bpy_speckle/ui/project_selection_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class speckle_project(bpy.types.PropertyGroup):
name: bpy.props.StringProperty()
role: bpy.props.StringProperty(name="Role")
updated: bpy.props.StringProperty(name="Updated")
id: bpy.props.StringProperty(name="ID")

class SPECKLE_UL_projects_list(bpy.types.UIList):
"""
Expand Down Expand Up @@ -55,11 +56,12 @@ def update_projects_list(self, context):
projects = get_projects_for_account(self.accounts, search=search)

# Populate projects list in WindowManager
for name, role, updated in projects:
for name, role, updated, id in projects:
project = wm.speckle_projects.add()
project.name = name
project.role = role
project.updated = updated
project.id = id

return None

Expand All @@ -84,7 +86,7 @@ def execute(self, context: Context) -> set[str]:
wm = context.window_manager
if 0 <= self.project_index < len(wm.speckle_projects):
selected_project = wm.speckle_projects[self.project_index]
bpy.ops.speckle.model_selection_dialog("INVOKE_DEFAULT", project_name=selected_project.name)
bpy.ops.speckle.model_selection_dialog("INVOKE_DEFAULT", project_name=selected_project.name, project_id=selected_project.id)
return {'FINISHED'}

def invoke(self, context: Context, event: Event) -> set[str]:
Expand All @@ -100,16 +102,22 @@ def invoke(self, context: Context, event: Event) -> set[str]:

# Get the selected account
selected_account_id = self.accounts

if not hasattr(WindowManager, "selected_account_id"):
# Register the collection property
WindowManager.selected_account_id = bpy.props.StringProperty()
wm.selected_account_id = selected_account_id

# Fetch projects from server
projects = get_projects_for_account(selected_account_id)

# Populate projects list in WindowManager
for name, role, updated in projects:
for name, role, updated, id in projects:
project = wm.speckle_projects.add()
project.name = name
project.role = role
project.updated = updated
project.id = id

return context.window_manager.invoke_props_dialog(self)

Expand Down
53 changes: 53 additions & 0 deletions bpy_speckle/utils/model_manager.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
from specklepy.api.client import SpeckleClient
from specklepy.api.credentials import get_local_accounts
from typing import List, Tuple, Optional
from .misc import format_relative_time
from specklepy.core.api.inputs.project_inputs import ProjectModelsFilter

def get_models_for_project(account_id: str, project_id: str, search: Optional[str] = None) -> List[Tuple[str, str, str]]:
"""
Fetch models for a given project from the Speckle server.
Args:
account_id: The ID of the Speckle account to fetch models for
project_id: The ID of the project to fetch models from
search: Optional search string to filter models
Returns:
List of tuples containing (model_name, model_id, last_updated)
Returns empty list if any error occurs
"""
try:
# Validate inputs
if not account_id or not project_id:
print(f"Error: Invalid inputs - account_id: {account_id}, project_id: {project_id}")
return []

# Get the account info
account = next((acc for acc in get_local_accounts() if acc.id == account_id), None)
if not account:
print(f"Error: Could not find account with ID: {account_id}")
return []

# Initialize the client
client = SpeckleClient(host=account.serverInfo.url)
# Authenticate
client.authenticate_with_account(account)

# Validate project exists
try:
client.project.get(project_id)
except Exception as e:
print(f"Error: Project with ID {project_id} not found: {str(e)}")
return []

filter = ProjectModelsFilter(search=search) if search else None

# Get models
models = client.model.get_models(project_id=project_id, models_limit=10, models_filter=filter).items

return [(model.name, model.id, format_relative_time(model.createdAt)) for model in models]

except Exception as e:
print(f"Error fetching models: {str(e)}")
return []
4 changes: 2 additions & 2 deletions bpy_speckle/utils/project_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def get_projects_for_account(account_id: str, search: Optional[str] = None) -> L
search: Optional search string to filter projects
Returns:
List of tuples containing (project_name, role, last_updated)
List of tuples containing (project_name, role, last_updated, project_id)
"""
try:
# Get the account info
Expand All @@ -32,7 +32,7 @@ def get_projects_for_account(account_id: str, search: Optional[str] = None) -> L
# Fetch projects
projects = client.active_user.get_projects(limit=10, filter=filter).items

return [(project.name, format_role(project.role), format_relative_time(project.updatedAt)) for project in projects]
return [(project.name, format_role(project.role), format_relative_time(project.updatedAt), project.id) for project in projects]

except Exception as e:
import traceback
Expand Down

0 comments on commit d862ace

Please sign in to comment.