diff --git a/.vscode/launch.json b/.vscode/launch.json index bbae7fb..e6bca58 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,7 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ + { "name": "Speckle Automate function", "type": "python", diff --git a/.vscode/settings.json b/.vscode/settings.json index 08ffa35..2bd83c1 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,10 @@ "stringcase", "typer" ], - "python.defaultInterpreterPath": ".venv/bin/python" + "python.defaultInterpreterPath": ".venv/bin/python", + "python.testing.pytestArgs": [ + "tests" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true } diff --git a/Dockerfile b/Dockerfile index a1dedb2..1ad00d1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,14 +2,12 @@ FROM ubuntu:22.04 ENV DEBIAN_FRONTEND=noninteractive -RUN apt update -RUN apt install -y tar wget blender python3-pip - -RUN pip3 install poetry +RUN apt update && \ + apt install --no-install-recommends -y tar wget blender python3-pip && \ + pip3 install poetry COPY . . -RUN poetry export -f requirements.txt --output requirements.txt && pip install -r requirements.txt -# RUN mkdir -p $HOME/.config/blender/3.0/scripts/addons -RUN wget -O blender-connector.zip https://releases.speckle.dev/installers/blender/bpy_speckle-2.17.0-alpha2.zip -RUN blender --background --python installation/connector.py +RUN poetry export -f requirements.txt --output requirements.txt && pip install -r requirements.txt && \ + wget -O blender-connector.zip https://releases.speckle.dev/installers/blender/bpy_speckle-2.17.0-alpha2.zip && \ + blender --background --python installation/connector.py diff --git a/environment.blend b/environment.blend index 39d2d67..aaa41aa 100644 Binary files a/environment.blend and b/environment.blend differ diff --git a/main.py b/main.py index abe4b79..e3da74a 100644 --- a/main.py +++ b/main.py @@ -3,6 +3,7 @@ use the automation_context module to wrap your function in an Autamate context helper """ +import json from speckle_automate import ( AutomationContext, execute_automate_function, @@ -21,30 +22,36 @@ def automate_function( VERSION_ID = automate_context.automation_run_data.version_id OBJECT_ID = client.commit.get(PROJECT_ID, VERSION_ID).referencedObject - DATA = f""" -TOKEN = '{client.account.token}' -SERVER_URL = '{client.account.serverInfo.url}' -PROJECT_ID = '{PROJECT_ID}' -OBJECT_ID = '{OBJECT_ID}' -""" - - Path.write_text(Path("./automate_data.py"), DATA) + data = { + "TOKEN": client.account.token, + "SERVER_URL": client.account.serverInfo.url, + "PROJECT_ID": PROJECT_ID, + "OBJECT_ID": OBJECT_ID + } + + Path("./automate_data.json").write_text(json.dumps(data)) - run( + process = run( [ 'blender', - '"environment.blend"', + 'environment.blend', '--background', - '--python speckle_import.py', + '--python', + 'speckle_import.py' ], capture_output=True, text=True, ) + if (returncode := process.returncode) != 0: + automate_context.mark_run_failed(f"The blender process exited with code {returncode}\n{process.stdout}") + else: + print(process.stdout) + print(process.stderr) - for file_path in Path("./screenshots").glob("**/*.png") : - automate_context.store_file_result(file_path) + for file_path in Path("./Screenshots").glob("**/*.png") : + automate_context.store_file_result(file_path) - automate_context.mark_run_success("YAYAY!!! we did it!") + automate_context.mark_run_success("YAYAY!!! we did it!") diff --git a/speckle_import.py b/speckle_import.py index 815052f..280d001 100644 --- a/speckle_import.py +++ b/speckle_import.py @@ -1,27 +1,74 @@ -from bpy_speckle.convert.to_native import _deep_conversion +import math +import bpy +import json +from pathlib import Path + +from mathutils import Vector +from bpy_speckle.convert.to_native import _deep_conversion, can_convert_to_native, convert_to_native, display_value_to_native, get_scale_factor from specklepy.api import operations from specklepy.transports.server import ServerTransport -from automate_data import * +from bpy_speckle.functions import get_default_traversal_func + +filepath = bpy.data.filepath +directory = Path(filepath).parent def set_filename(fileName): """Sets the output directory for rendered images""" - filepath = bpy.data.filepath - directory = os.path.dirname(filepath) + if not filepath: raise Exception("Blend file must be saved!") - - bpy.context.scene.render.filepath = os.path.join(directory, "Screenshots", fileName) + + path = Path(directory, "Screenshots", fileName) + print(path) + bpy.context.scene.render.filepath = str(path) print(f"Starting Receive...") -remote_transport = ServerTransport(STREAMID, token=TOKEN, url=SERVER_URL) -commit_object = operations.receive(OBJECTID, remote_transport) +text = Path(directory, "automate_data.json").read_text() +data = json.loads(text) + +PROJECT_ID = data["PROJECT_ID"] +TOKEN = data["TOKEN"] +SERVER_URL = data["SERVER_URL"] +OBJECT_ID = data["OBJECT_ID"] + +remote_transport = ServerTransport(PROJECT_ID, token=TOKEN, url=SERVER_URL) +root_object = operations.receive(OBJECT_ID, remote_transport) converted_objects = {} -_deep_conversion(commit_object, converted_objects, True) +_deep_conversion(root_object, converted_objects, True) + +#Make all materials blank +for mat in bpy.data.materials: + mat.diffuse_color = (1.0, 1.0, 1.0, 1.0) + +# Convert all rooms as lights +traversal_func = get_default_traversal_func(can_convert_to_native) +rooms = [x.current for x in traversal_func.traverse(root_object) if x.current.speckle_type == "Objects.BuiltElements.Room"] + +light = bpy.data.lights.new("myLight", "PointLight") + +for room in rooms: + (converted, _) = display_value_to_native(room, f"FakeRoom{room.id}", get_scale_factor(room)) + + #calculate bounds + minimum = Vector((-math.inf, -math.inf, -math.inf)) + maximum = Vector((math.inf, math.inf, math.inf)) + + for vert in converted.vertices: + minimum.x = min(minimum.x, vert.co.x) + minimum.y = min(minimum.y, vert.co.y) + minimum.z = min(minimum.z, vert.co.z) + maximum.x = max(maximum.x, vert.co.x) + maximum.y = max(maximum.y, vert.co.y) + maximum.z = max(maximum.z, vert.co.z) + + bpy.data.objects.new(f"FakeLight{room.id}", light) + + all_cameras = [o for o in bpy.context.scene.objects if o.type == "CAMERA"] diff --git a/tests/test_function.py b/tests/test_function.py index 574f1b4..037f7e8 100644 --- a/tests/test_function.py +++ b/tests/test_function.py @@ -6,6 +6,7 @@ import pytest from gql import gql from speckle_automate import ( + AutomationContext, AutomationRunData, AutomationStatus, run_function, @@ -15,8 +16,7 @@ from specklepy.objects.base import Base from specklepy.transports.server import ServerTransport -from main import FunctionInputs, automate_function - +from main import automate_function def crypto_random_string(length: int) -> str: """Generate a semi crypto random string of a given length.""" @@ -78,8 +78,8 @@ def speckle_token() -> str: @pytest.fixture() def speckle_server_url() -> str: - """Provide a speckle server url for the test suite, default to localhost.""" - return os.getenv("SPECKLE_SERVER_URL", "http://127.0.0.1:3000") + """Provide a speckle server url for the test suite, default to latests.""" + return os.getenv("SPECKLE_SERVER_URL", "https://latest.speckle.systems") @pytest.fixture() @@ -105,29 +105,31 @@ def automation_run_data( test_object: Base, test_client: SpeckleClient, speckle_server_url: str ) -> AutomationRunData: """Set up an automation context for testing.""" - project_id = test_client.stream.create("Automate function e2e test") + project_id = "704984e22d" # test_client.stream.create("Automate function e2e test") branch_name = "main" model = test_client.branch.get(project_id, branch_name, commits_limit=1) model_id: str = model.id - root_obj_id = operations.send( - test_object, [ServerTransport(project_id, test_client)] - ) - version_id = test_client.commit.create(project_id, root_obj_id) - - automation_name = crypto_random_string(10) - automation_id = crypto_random_string(10) - automation_revision_id = crypto_random_string(10) - - register_new_automation( - project_id, - model_id, - test_client, - automation_id, - automation_name, - automation_revision_id, - ) + # root_obj_id = operations.send( + # test_object, [ServerTransport(project_id, test_client)] + # ) + version_id = "704984e22d" #test_client.commit.create(project_id, root_obj_id) + if(isinstance(version_id, Exception)): + raise version_id + + automation_name = "myFunctionName" #crypto_random_string(10) + automation_id = "myFunctionId" #crypto_random_string(10) + automation_revision_id = "myFunctionVer" # crypto_random_string(10) + + # register_new_automation( + # project_id, + # model_id, + # test_client, + # automation_id, + # automation_name, + # automation_revision_id, + # ) automation_run_id = crypto_random_string(10) function_id = crypto_random_string(10) @@ -142,17 +144,17 @@ def automation_run_data( automation_revision_id=automation_revision_id, automation_run_id=automation_run_id, function_id=function_id, - function_revision=function_revision, + function_name="", + function_release= function_revision ) def test_function_run(automation_run_data: AutomationRunData, speckle_token: str): """Run an integration test for the automate function.""" + context = AutomationContext.initialize(automation_run_data, speckle_token) automate_sdk = run_function( + context, automate_function, - automation_run_data, - speckle_token, - FunctionInputs(forbidden_speckle_type="Base"), ) - - assert automate_sdk.run_status == AutomationStatus.FAILED + assert len(context._automation_result.blobs) > 0 + assert automate_sdk.run_status == AutomationStatus.SUCCEEDED