diff --git a/Exercises/exercise-0/function.py b/Exercises/exercise-0/function.py new file mode 100644 index 0000000..e6df6cf --- /dev/null +++ b/Exercises/exercise-0/function.py @@ -0,0 +1,50 @@ +def automate_function( + automate_context: AutomationContext, + function_inputs: FunctionInputs, +) -> None: + """This is an example Speckle Automate function. + + Args: + automate_context: A context helper object, that carries relevant information + about the runtime context of this function. + It gives access to the Speckle project data, that triggered this run. + It also has convenience methods attach result data to the Speckle model. + function_inputs: An instance object matching the defined schema. + """ + + # the context provides a convenient way, to receive the triggering version + version_root_object = automate_context.receive_version() + + flat_list_of_objects = flatten_base(version_root_object) + + # filter the list to only include objects that are displayable. + # this is a simple example, that checks if the object has a displayValue + displayable_objects = [ + speckle_object + for speckle_object in flat_list_of_objects + if ( + getattr(speckle_object, "displayValue", None) + or getattr(speckle_object, "@displayValue", None) + ) + and getattr(speckle_object, "id", None) is not None + ] + + if len(displayable_objects) == 0: + automate_context.mark_run_failed( + "Automation failed: No displayable objects found." + ) + + else: + # select a random object from the list + random_object = random.choice(displayable_objects) + + automate_context.attach_info_to_objects( + category="Selected Object", + object_ids=[random_object.id], + message=function_inputs.comment_phrase, + ) + + automate_context.mark_run_success("Added a comment to a random object.") + + # set the automation context view, to the original model / version view + automate_context.set_context_view() diff --git a/Exercises/exercise-0/inputs.py b/Exercises/exercise-0/inputs.py new file mode 100644 index 0000000..4ed70bf --- /dev/null +++ b/Exercises/exercise-0/inputs.py @@ -0,0 +1,16 @@ +from pydantic import Field +from speckle_automate import AutomateBase + + +class FunctionInputs(AutomateBase): + """These are function author defined values. + + Automate will make sure to supply them matching the types specified here. + Please use the pydantic model schema to define your inputs: + https://docs.pydantic.dev/latest/usage/models/ + """ + + comment_phrase: str = Field( + title="Comment Phrase", + description="This phrase will be added to a random model element.", + ) diff --git a/main.py b/main.py index 1d22f84..51432a8 100644 --- a/main.py +++ b/main.py @@ -1,103 +1,17 @@ -"""This module contains the function's business logic. - -Use the automation_context module to wrap your function in an Automate context helper. +""" +This main entry point is the command line interface for the Speckle Automate function. """ -from pydantic import Field, SecretStr from speckle_automate import ( - AutomateBase, - AutomationContext, execute_automate_function, ) -from flatten import flatten_base - - -class FunctionInputs(AutomateBase): - """These are function author-defined values. - - Automate will make sure to supply them matching the types specified here. - Please use the pydantic model schema to define your inputs: - https://docs.pydantic.dev/latest/usage/models/ - """ - - # An example of how to use secret values. - whisper_message: SecretStr = Field(title="This is a secret message") - forbidden_speckle_type: str = Field( - title="Forbidden speckle type", - description=( - "If a object has the following speckle_type," - " it will be marked with an error." - ), - ) - - -def automate_function( - automate_context: AutomationContext, - function_inputs: FunctionInputs, -) -> None: - """This is an example Speckle Automate function. - - Args: - automate_context: A context-helper object that carries relevant information - about the runtime context of this function. - It gives access to the Speckle project data that triggered this run. - It also has convenient methods for attaching result data to the Speckle model. - function_inputs: An instance object matching the defined schema. - """ - # The context provides a convenient way to receive the triggering version. - version_root_object = automate_context.receive_version() - - objects_with_forbidden_speckle_type = [ - b - for b in flatten_base(version_root_object) - if b.speckle_type == function_inputs.forbidden_speckle_type - ] - count = len(objects_with_forbidden_speckle_type) - - if count > 0: - # This is how a run is marked with a failure cause. - automate_context.attach_error_to_objects( - category="Forbidden speckle_type" - f" ({function_inputs.forbidden_speckle_type})", - object_ids=[o.id for o in objects_with_forbidden_speckle_type if o.id], - message="This project should not contain the type: " - f"{function_inputs.forbidden_speckle_type}", - ) - automate_context.mark_run_failed( - "Automation failed: " - f"Found {count} object that have one of the forbidden speckle types: " - f"{function_inputs.forbidden_speckle_type}" - ) - - # Set the automation context view to the original model/version view - # to show the offending objects. - automate_context.set_context_view() - - else: - automate_context.mark_run_success("No forbidden types found.") - - # If the function generates file results, this is how it can be - # attached to the Speckle project/model - # automate_context.store_file_result("./report.pdf") - - -def automate_function_without_inputs(automate_context: AutomationContext) -> None: - """A function example without inputs. - - If your function does not need any input variables, - besides what the automation context provides, - the inputs argument can be omitted. - """ - pass - +import Exercises.exercise_0.inputs.FunctionInputs as FunctionInputs +import Exercises.exercise_0.function.automate_function as automate_function # make sure to call the function with the executor +# Pass in the function reference with the inputs schema to the executor. +# If the function has no arguments, the executor can handle it like so +# execute_automate_function(automate_function_without_inputs) if __name__ == "__main__": - # NOTE: always pass in the automate function by its reference; do not invoke it! - - # Pass in the function reference with the inputs schema to the executor. execute_automate_function(automate_function, FunctionInputs) - - # If the function has no arguments, the executor can handle it like so - # execute_automate_function(automate_function_without_inputs)