From 9b78af538b29b5ff61598a6487a082935ae0b090 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gerg=C5=91=20Jedlicska?= Date: Wed, 30 Aug 2023 07:41:08 +0000 Subject: [PATCH] devcontainer setup --- .devcontainer/devcontainer.json | 43 +++++++++++++ .env.example | 1 + .vscode/launch.json | 16 +++-- .vscode/settings.json | 8 ++- automate_function.py | 45 ++++++++++++++ flatten.py | 4 +- main.py | 67 +++------------------ make_comment.py | 103 -------------------------------- speckle_project_data.py | 13 ++++ 9 files changed, 130 insertions(+), 170 deletions(-) create mode 100644 .devcontainer/devcontainer.json create mode 100644 .env.example create mode 100644 automate_function.py delete mode 100644 make_comment.py create mode 100644 speckle_project_data.py diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..e0d0e2e --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,43 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/python +{ + "name": "Python 3", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye", + "features": { + "ghcr.io/devcontainers-contrib/features/poetry:2": {} + }, + + "remoteEnv": { + "SPECKLE_TOKEN": "foobar" + }, + "containerEnv": { + "SPECKLE_TOKEN": "asdfasdf" + }, + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [], + + // Use 'postCreateCommand' to run commands after the container is created. + "postCreateCommand": "cp .env.example .env && POETRY_VIRTUALENVS_IN_PROJECT=true poetry install --no-root", + + // Configure tool-specific properties. + "customizations": { + "vscode": { + // Add the IDs of extensions you want installed when the container is created. + "extensions": [ + "ms-python.vscode-pylance", + "ms-python.python", + "ms-python.black-formatter", + "streetsidesoftware.code-spell-checker", + "mikestead.dotenv" + ] + } + } + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..49ec7c6 --- /dev/null +++ b/.env.example @@ -0,0 +1 @@ +SPECKLE_TOKEN=mytoken \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index bfd404f..a4daedf 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,12 +5,18 @@ "version": "0.2.0", "configurations": [ { - "name": "Python: Current File", + "name": "Speckle Automate function", "type": "python", "request": "launch", - "program": "${file}", + "program": "main.py", "console": "integratedTerminal", - "justMyCode": false - } + "justMyCode": true, + "envFile": "${workspaceFolder}/.env", + "args": [ + "{\"projectId\": \"843d07eb10\", \"modelId\": \"base design\", \"versionId\": \"2a32ccfee1\", \"speckleServerUrl\": \"https://latest.speckle.systems\"}", + // make sure to use camelCase for variable names + "{\"speckleTypeToCount\": \"Objects.Geometry.Brep\"}" + ], + }, ] -} \ No newline at end of file +} diff --git a/.vscode/settings.json b/.vscode/settings.json index 45bd6a8..08ffa35 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,9 @@ { - "cSpell.words": ["camelcase", "pydantic", "stringcase", "typer"] + "cSpell.words": [ + "camelcase", + "pydantic", + "stringcase", + "typer" + ], + "python.defaultInterpreterPath": ".venv/bin/python" } diff --git a/automate_function.py b/automate_function.py new file mode 100644 index 0000000..80ca46f --- /dev/null +++ b/automate_function.py @@ -0,0 +1,45 @@ +from pydantic import BaseModel, ConfigDict +from stringcase import camelcase + +from specklepy.transports.memory import MemoryTransport +from specklepy.transports.server import ServerTransport +from specklepy.api.operations import receive +from specklepy.api.client import SpeckleClient +from flatten import flatten_base +from speckle_project_data import SpeckleProjectData + + +class FunctionInputs(BaseModel): + """ + These are function author defined values, automate will make sure to supply them. + """ + + speckle_type_to_count: str + model_config = ConfigDict(alias_generator=camelcase) + + +def automate_function( + project_data: SpeckleProjectData, + function_inputs: FunctionInputs, + speckle_token: str, +): + client = SpeckleClient(project_data.speckle_server_url) + client.authenticate_with_token(speckle_token) + commit = client.commit.get(project_data.project_id, project_data.version_id) + + memory_transport = MemoryTransport() + server_transport = ServerTransport(project_data.project_id, client) + if not commit.referencedObject: + raise ValueError("The commit has no root referencedObject.") + + base = receive(commit.referencedObject, server_transport, memory_transport) + + count = 0 + for b in flatten_base(base): + if b.speckle_type == function_inputs.speckle_type_to_count: + count += 1 + + print( + f"Found {count} object that match the queried speckle type: ", + "{function_inputs.speckle_type_to_count}", + ) diff --git a/flatten.py b/flatten.py index 64ebcd8..b1c2cfb 100644 --- a/flatten.py +++ b/flatten.py @@ -4,6 +4,6 @@ from specklepy.objects import Base def flatten_base(base: Base) -> Iterable[Base]: if hasattr(base, "elements"): - for element in base.elements: + for element in base["elements"]: yield from flatten_base(element) - yield base \ No newline at end of file + yield base diff --git a/main.py b/main.py index 9c2b7b0..3964999 100644 --- a/main.py +++ b/main.py @@ -1,68 +1,17 @@ import typer -from pydantic import BaseModel, ConfigDict -from stringcase import camelcase -from specklepy.transports.memory import MemoryTransport -from specklepy.transports.server import ServerTransport -from specklepy.api.operations import receive -from specklepy.api.client import SpeckleClient -import random - -from flatten import flatten_base -from make_comment import make_comment +import os +from speckle_project_data import SpeckleProjectData +from automate_function import FunctionInputs, automate_function -class SpeckleProjectData(BaseModel): - """Values of the project / model that triggered the run of this function.""" +def main(speckle_project_data: str, function_inputs: str, speckle_token: str = ""): + speckle_token = speckle_token if speckle_token else os.environ.get("SPECKLE_TOKEN") + if not speckle_token: + raise ValueError("The supplied speckle token is not valid") - project_id: str - model_id: str - version_id: str - speckle_server_url: str - - model_config = ConfigDict(alias_generator=camelcase, protected_namespaces=()) - - -class FunctionInputs(BaseModel): - """ - These are function author defined values, automate will make sure to supply them. - """ - - comment_text: str - - class Config: - alias_generator = camelcase - - -def main(speckle_project_data: str, function_inputs: str, speckle_token: str): project_data = SpeckleProjectData.model_validate_json(speckle_project_data) inputs = FunctionInputs.model_validate_json(function_inputs) - - client = SpeckleClient(project_data.speckle_server_url, use_ssl=False) - client.authenticate_with_token(speckle_token) - commit = client.commit.get(project_data.project_id, project_data.version_id) - branch = client.branch.get(project_data.project_id, project_data.model_id, 1) - - memory_transport = MemoryTransport() - server_transport = ServerTransport(project_data.project_id, client) - base = receive(commit.referencedObject, server_transport, memory_transport) - - random_beam = random.choice( - [b for b in flatten_base(base) if b.speckle_type == "IFCBEAM"] - ) - - make_comment( - client, - project_data.project_id, - branch.id, - project_data.version_id, - inputs.comment_text, - random_beam.id, - ) - - print( - "Ran function with", - f"{speckle_project_data} {function_inputs}", - ) + automate_function(project_data, inputs, speckle_token) if __name__ == "__main__": diff --git a/make_comment.py b/make_comment.py deleted file mode 100644 index 98bb0ca..0000000 --- a/make_comment.py +++ /dev/null @@ -1,103 +0,0 @@ -from specklepy.api.client import SpeckleClient -from gql import gql - - -def make_comment( - client: SpeckleClient, - project_id: str, - model_id: str, - version_id: str, - comment_text: str, - selected_object_id: str, -) -> None: - client.httpclient.execute( - gql( - """ - mutation createComment($input: CreateCommentInput!) { - commentMutations { - create(input: $input) { - id - } - } - } - """ - ), - { - "input": { - "content": { - "blobIds": [], - "doc": { - "content": [ - { - "content": [{"text": comment_text, "type": "text"}], - "type": "paragraph", - } - ], - "type": "doc", - }, - }, - "projectId": project_id, - "resourceIdString": model_id, - "screenshot": None, - "viewerState": { - "projectId": project_id, - "resources": { - "request": { - "resourceIdString": f"{model_id}@{version_id}", - "threadFilters": {}, - } - }, - "sessionId": "fooobarbaz", - "ui": { - "camera": { - "isOrthoProjection": False, - "position": [ - -13.959975903859306, - 109.21340462426888, - 19.00868018548827, - ], - "target": [ - -28.304303646087646, - 99.69336318969727, - 2.3997000455856323, - ], - "zoom": 1, - }, - "explodeFactor": 0, - "filters": { - "hiddenObjectIds": [], - "isolatedObjectIds": [selected_object_id], - "propertyFilter": {"isApplied": False, "key": None}, - "selectedObjectIds": [selected_object_id], - }, - "lightConfig": { - "azimuth": 0.75, - "castShadow": True, - "color": 16777215, - "elevation": 1.33, - "enabled": True, - "indirectLightIntensity": 1.2, - "intensity": 5, - "radius": 0, - "shadowcatcher": True, - }, - "sectionBox": None, - "selection": [ - -31.355755138199026, - 101.06821903317298, - 4.250507316347136, - ], - "spotlightUserSessionId": None, - "threads": { - "openThread": { - "isTyping": False, - "newThreadEditor": True, - "threadId": None, - } - }, - }, - "viewer": {"metadata": {"filteringState": {}}}, - }, - } - }, - ) diff --git a/speckle_project_data.py b/speckle_project_data.py new file mode 100644 index 0000000..0112d4d --- /dev/null +++ b/speckle_project_data.py @@ -0,0 +1,13 @@ +from pydantic import BaseModel, ConfigDict +from stringcase import camelcase + + +class SpeckleProjectData(BaseModel): + """Values of the project / model that triggered the run of this function.""" + + project_id: str + model_id: str + version_id: str + speckle_server_url: str + + model_config = ConfigDict(alias_generator=camelcase, protected_namespaces=())