From 5ff59d805a9e3307d2e188ffe6e9557aa53f0257 Mon Sep 17 00:00:00 2001 From: Jekannadar Date: Wed, 22 Nov 2023 15:50:09 +0100 Subject: [PATCH] Add source code documentation to entirety of the add-in --- .pre-commit-config.yaml | 5 + .../cls_cad_backend/database/commands.py | 14 +- .../cls_cad_backend/repository_builder.py | 70 +++--- .../cls_cad_backend/responses.py | 4 +- .../cls-cad-backend/cls_cad_backend/server.py | 65 ++++-- .../cls_cad_backend/util/hrid.py | 7 +- .../cls_cad_backend/util/json_operations.py | 36 +-- .../cls_cad_backend/util/motion.py | 8 +- applications/cls-cad-backend/main.py | 2 +- .../.pre-commit-config.yaml | 20 +- applications/cls-cad-fusion-plugin/CLS-CAD.py | 6 + .../commands/__init__.py | 11 + .../commands/assembleresult/entry.py | 217 ++++++++++++++---- .../attributestaxonomyediting/entry.py | 62 +++-- .../commands/checkandsubmit/entry.py | 76 +++--- .../commands/downloadtaxonomy/entry.py | 57 +++-- .../commands/exportproject/entry.py | 81 ++++--- .../commands/formatstaxonomyediting/entry.py | 61 +++-- .../commands/jointtyping/entry.py | 152 ++++++------ .../commands/partmanagement/entry.py | 42 +++- .../commands/partstaxonomyediting/entry.py | 62 +++-- .../commands/parttyping/entry.py | 79 +++++-- .../commands/requestsynthesis/entry.py | 85 +++++-- .../commands/togglecustomgraphics/entry.py | 57 +++-- .../commands/typecrawlingproject/entry.py | 74 +++--- .../commands/uploadtaxonomy/entry.py | 57 +++-- .../uuidupdatecrawlingproject/entry.py | 75 +++--- .../lib/cls_python_compat/types.py | 15 +- .../lib/fusion360utils/event_utils.py | 78 +++++-- .../lib/fusion360utils/general_utils.py | 24 +- .../lib/general_utils/hrid.py | 28 ++- .../lib/general_utils/util.py | 43 +++- 32 files changed, 1093 insertions(+), 580 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 256972c..86add3a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -43,6 +43,11 @@ repos: - id: isort args: ["--profile", "black"] exclude: ^static/ + - repo: https://github.com/PyCQA/docformatter + rev: v1.7.5 + hooks: + - id: docformatter + args: [--in-place, --pre-summary-newline, --black] - repo: https://github.com/psf/black rev: 22.3.0 hooks: diff --git a/applications/cls-cad-backend/cls_cad_backend/database/commands.py b/applications/cls-cad-backend/cls_cad_backend/database/commands.py index 0092f61..1900790 100644 --- a/applications/cls-cad-backend/cls_cad_backend/database/commands.py +++ b/applications/cls-cad-backend/cls_cad_backend/database/commands.py @@ -19,11 +19,12 @@ def init_database(): """ - Initialize the database for the backend. This can either be a remote MongoDB instance, or a local MontyDB instance. - When possible, the local instance uses LMDB as storage engine, else a normal flatfile. - The configuration is done via the config.ini in this folder. - If no configuration exists, the user is prompted graphically to create one. - If the backend runs in a container, a LMDB MontyDB instance is always created. + Initialize the database for the backend. This can either be a remote MongoDB + instance, or a local MontyDB instance. When possible, the local instance uses LMDB + as storage engine, else a normal flatfile. The configuration is done via the + config.ini in this folder. If no configuration exists, the user is prompted + graphically to create one. If the backend runs in a container, a LMDB MontyDB + instance is always created. :return: """ @@ -109,7 +110,8 @@ def init_database(): def switch_to_test_database() -> None: """ - For running test cases, a separate MontyDB instance is gets created by this method and attached to. + For running test cases, a separate MontyDB instance is gets created by this method + and attached to. :return: """ diff --git a/applications/cls-cad-backend/cls_cad_backend/repository_builder.py b/applications/cls-cad-backend/cls_cad_backend/repository_builder.py index 48b4a46..309b2ae 100644 --- a/applications/cls-cad-backend/cls_cad_backend/repository_builder.py +++ b/applications/cls-cad-backend/cls_cad_backend/repository_builder.py @@ -14,8 +14,9 @@ class Part: def __call__(self, *required_parts): """ - Collects all arguments that the term gives to a specific application. - Then, recursively interprets these into a dict that matches the later Fusion 360 assembly tree. + Collects all arguments that the term gives to a specific application. Then, + recursively interprets these into a dict that matches the later Fusion 360 + assembly tree. :param required_parts: The set of all parts connected to this part. :return: The completed dictionary of this part. @@ -48,7 +49,8 @@ def __hash__(self): def __init__(self, info) -> None: """ - Parts get created with info from their JSON representation. This is aggregated in the call method. + Parts get created with info from their JSON representation. This is aggregated + in the call method. :param info: A dict containing information about the part. """ @@ -56,7 +58,8 @@ def __init__(self, info) -> None: def __eq__(self, other): """ - Required for PiCLS. Computes equality based on if two parts have exactly the same info dict. + Required for PiCLS. Computes equality based on if two parts have exactly the + same info dict. :param other: The object to compare against. :return: true if the objects are equal, else false. @@ -71,13 +74,15 @@ class Role(str, Enum): def generate_leaf(provides: list[Constructor], part_counts, taxonomy) -> Type: """ - Generates a leaf type, i.e., the type of a part that only provides something and doesn't require anything. - Such a part binds either a part count of 0 or 1 of a counted Literal, depending on if it is a subtype of the - counted metric or not. + Generates a leaf type, i.e., the type of a part that only provides something and + doesn't require anything. Such a part binds either a part count of 0 or 1 of a + counted Literal, depending on if it is a subtype of the counted metric or not. - :param provides: A list of constructors that when intersected represent the provided type. + :param provides: A list of constructors that when intersected represent the provided + type. :param part_counts: The set of constraints. - :param taxonomy: The taxonomy that decides if the part provides 1 or 0 of a constraint. + :param taxonomy: The taxonomy that decides if the part provides 1 or 0 of a + constraint. :return: The complete type of the leaf part. """ arguments = [ @@ -100,11 +105,13 @@ def collect_and_increment_part_count( counted_vars: dict[str, Any], count_name: str, multiplicities: Mapping[str, int] ): """ - Collect the part counts of all positions in the multi-arrow type and compute their sum, incremented by one. + Collect the part counts of all positions in the multi-arrow type and compute their + sum, incremented by one. :param counted_vars: The set of Literals that are bound by PiCLS. :param count_name: The name of the Literal that will be bound to the result. - :param multiplicities: An optional set of multiplicities, signalling that a type corresponds to multiple physical. + :param multiplicities: An optional set of multiplicities, signalling that a type + corresponds to multiple physical. :return: The sum of the weighted individual part counts. """ return ( @@ -123,11 +130,13 @@ def collect_part_count( counted_vars: dict, count_name: str, multiplicities: Mapping[str, int] ): """ - Collect the part counts of all positions in the multi-arrow type and compute their sum. + Collect the part counts of all positions in the multi-arrow type and compute their + sum. :param counted_vars: The set of Literals that are bound by PiCLS. :param count_name: The name of the Literal that will be bound to the result. - :param multiplicities: An optional set of multiplicities, signalling that a type corresponds to multiple physical. + :param multiplicities: An optional set of multiplicities, signalling that a type + corresponds to multiple physical. :return: The sum of the weighted individual part counts. """ return sum( @@ -143,7 +152,8 @@ def get_joint_origin_type( uuid: str, part: dict, role: Role, prefix: str = "" ) -> list[Constructor]: """ - Convert the type information for a specific JointOrigin in a part JSON to actual type constructors. + Convert the type information for a specific JointOrigin in a part JSON to actual + type constructors. :param uuid: The uuid of the JointOrigin to create constructors for. :param part: The part JSON containing information on the JointOrigin. @@ -156,12 +166,14 @@ def get_joint_origin_type( def fetch_required_joint_origins_info(part, configuration): """ - Retrieve the subsets of the part JSON relevant to the JointOrigin uuids of a configuration of the part. - A configuration is one joint being selected as provided and all others as required. + Retrieve the subsets of the part JSON relevant to the JointOrigin uuids of a + configuration of the part. A configuration is one joint being selected as provided + and all others as required. :param part: The complete part JSON. :param configuration: The configuration to retrieve information for. - :return: A list of the JSON subsets pertaining to the JointOrigins in the configuration. + :return: A list of the JSON subsets pertaining to the JointOrigins in the + configuration. """ return { joint_origin_uuid: fetch_joint_origin_info(part, joint_origin_uuid) @@ -172,6 +184,7 @@ def fetch_required_joint_origins_info(part, configuration): def fetch_joint_origin_info(part, joint_origin_uuid: str): """ Retrieve the subset of the part JSON relevant to a specific JointOrigin uuid. + :param part: The complete part JSON. :param joint_origin_uuid: The uuid to retrieve a subset for. :return: The subset of the JSON for given uuid. @@ -195,8 +208,8 @@ def types_from_uuids( uuids: list, part: dict, prefix: str = "" ) -> OrderedDict[str, list[Constructor]]: """ - Given a JSON of a part and a list of JointOrigin uuids, return a list of tuples of uuid and corresponding type - information. + Given a JSON of a part and a list of JointOrigin uuids, return a list of tuples of + uuid and corresponding type information. :param uuids: A list of uuids to retrieve type information for. :param part: The part JSON that contains all the uuids. @@ -227,14 +240,17 @@ def add_part_to_repository( taxonomy: Subtypes = None, ) -> None: """ - Adds a part to a repository to be used for synthesis. The type is dependent on the constraints in part_counts. - If no part_counts are provided, the generated types are multi-arrows where each position is the type of the - respective JointOrigin, terminating in the provided type of the provided JointOrigin. + Adds a part to a repository to be used for synthesis. The type is dependent on + the constraints in part_counts. If no part_counts are provided, the generated + types are multi-arrows where each position is the type of the respective + JointOrigin, terminating in the provided type of the provided JointOrigin. - :param part_counts: The constraints the type needs to account for, i.e. add Literals that get incremented. + :param part_counts: The constraints the type needs to account for, i.e. add + Literals that get incremented. :param part: The JSON representation of the part to add to the repository. :param repository: The repository dict for the part to be added to. - :param taxonomy: The taxonomy to check against if a type needs to increment a Literal or not. + :param taxonomy: The taxonomy to check against if a type needs to increment a + Literal or not. :return: """ for configuration in part["configurations"]: @@ -352,8 +368,10 @@ def add_all_to_repository( :param project_id: The id of the project to get parts from. :param taxonomy: The taxonomy describing the subtype relationships. - :param part_counts: The constraints for the synthesis request (the types in the repository depend on this). - :return: The repository containing all part combinators with their respective types. + :param part_counts: The constraints for the synthesis request (the types in the + repository depend on this). + :return: The repository containing all part combinators with their respective + types. """ repository: dict = {} for part in get_all_parts_for_project(project_id): diff --git a/applications/cls-cad-backend/cls_cad_backend/responses.py b/applications/cls-cad-backend/cls_cad_backend/responses.py index cf1d70f..bbf88cf 100644 --- a/applications/cls-cad-backend/cls_cad_backend/responses.py +++ b/applications/cls-cad-backend/cls_cad_backend/responses.py @@ -18,8 +18,8 @@ class FastResponse(Response): def render(self, content: typing.Any) -> bytes: """ - Encodes the response of an endpoint into a JSON. When not running on PyPy, uses faster JSON encoders in a - cascading fashion based on maximum JSON depth. + Encodes the response of an endpoint into a JSON. When not running on PyPy, uses + faster JSON encoders in a cascading fashion based on maximum JSON depth. :param content: The content to encode. :return: The encoded content. diff --git a/applications/cls-cad-backend/cls_cad_backend/server.py b/applications/cls-cad-backend/cls_cad_backend/server.py index ef86211..c95b7dd 100644 --- a/applications/cls-cad-backend/cls_cad_backend/server.py +++ b/applications/cls-cad-backend/cls_cad_backend/server.py @@ -72,11 +72,13 @@ async def save_part( payload: PartInf, ) -> str: """ - Takes a part payload in JSON form and inserts it into the database as is. It is indexed by a unique ID, usually the - Fusion 360 file identifier. + Takes a part payload in JSON form and inserts it into the database as is. It is + indexed by a unique ID, usually the Fusion 360 file identifier. - :param payload: The payload containing project, folder and part ids, as well as type information. - :return: Returns "OK" when successful, else returns a 422 response code if payload didn't pass validation. + :param payload: The payload containing project, folder and part ids, as well as type + information. + :return: Returns "OK" when successful, else returns a 422 response code if payload + didn't pass validation. """ upsert_part(payload.model_dump(by_alias=True)) print(payload.model_dump(by_alias=True)) @@ -88,11 +90,13 @@ async def save_taxonomy( payload: TaxonomyInf, ) -> str: """ - Takes a taxonomy payload in JSON form and inserts it into the database as is. It is indexed by a unique ID, usually - the Fusion 360 project identifier. + Takes a taxonomy payload in JSON form and inserts it into the database as is. It is + indexed by a unique ID, usually the Fusion 360 project identifier. - :param payload: The payload containing the taxonomy, split into three distinct taxonomies. - :return: Returns "OK" when successful, else returns a 422 response code if payload didn't pass validation. + :param payload: The payload containing the taxonomy, split into three distinct + taxonomies. + :return: Returns "OK" when successful, else returns a 422 response code if payload + didn't pass validation. """ upsert_taxonomy(payload.model_dump(by_alias=True)) return "OK" @@ -103,14 +107,18 @@ async def synthesize_assembly( payload: SynthesisRequestInf, background_tasks: BackgroundTasks ): """ - Takes a payload describing a synthesis request as JSON. Builds a repository and a query and then executes PiCLS. - Results (if present) get enumerated (up to 100) and then post-processed into assembly instructions for the - Fusion 360 Add-In to execute. A background task inserts the results bundled in a single JSON Object into the + Takes a payload describing a synthesis request as JSON. Builds a repository and a + query and then executes PiCLS. Results (if present) get enumerated (up to 100) and + then post-processed into assembly instructions for the Fusion 360 Add-In to execute. + A background task inserts the results bundled in a single JSON Object into the database. - :param payload: The payload containing target types and constraints for the synthesis request. - :param background_tasks: The background tasks to asynchronously insert into the database. - :return: A JSON containing a result id and metadata, or FAIL if there are no results. + :param payload: The payload containing target types and constraints for the + synthesis request. + :param background_tasks: The background tasks to asynchronously insert into the + database. + :return: A JSON containing a result id and metadata, or FAIL if there are no + results. """ take_time = timer() literals = {} @@ -178,11 +186,12 @@ async def synthesize_assembly( @app.get("/data/taxonomy/{project_id}", response_class=FastResponse) async def get_taxonomy(project_id: str): """ - Retrieves the taxonomy and inverts subtype and supertype (Add-In uses Keys as Supertypes, CLS uses Keys as Subtype, - for multiple inheritance). + Retrieves the taxonomy and inverts subtype and supertype (Add-In uses Keys as + Supertypes, CLS uses Keys as Subtype, for multiple inheritance). :param project_id: The project id for which a taxonomy should be retrieved. - :return: The inverted taxonomy for the project id if present. If not present, an empty default taxonomy. + :return: The inverted taxonomy for the project id if present. If not present, an + empty default taxonomy. """ return invert_taxonomy(get_taxonomy_for_project(project_id)) @@ -203,15 +212,16 @@ async def list_project_ids(project_id: str): Lists all result metadata for a specific project id. :param project_id: The project id for which to list metadata. - :return: A list of JSON objects describing the individual results. Each object has an "id" key. + :return: A list of JSON objects describing the individual results. Each object has + an "id" key. """ return [dict(x, id=x["_id"]) for x in get_all_result_ids_for_project(project_id)] async def cache_request(request_id, project_id: str): """ - Caches a specific synthesis result. Since these can be several Mb of JSON data, this avoids unnecessary database - accesses. + Caches a specific synthesis result. Since these can be several Mb of JSON data, this + avoids unnecessary database accesses. :param project_id: The id of the project of the result to be cached. :param request_id: The id of the result to be cached. @@ -231,12 +241,14 @@ async def maximum_counts_for_id( request_id: str, ): """ - Computes the maximum amount of a part across all assemblies contained in a synthesis result. - This is used to create a template file that contains enough parts to assemble any assembly from the results. + Computes the maximum amount of a part across all assemblies contained in a synthesis + result. This is used to create a template file that contains enough parts to + assemble any assembly from the results. :param project_id: The project id of the project the result is from. :param request_id: The id of the result. - :return: A JSON object containing all the maximum counts. "Invalid" if the request or project ids were invalid. + :return: A JSON object containing all the maximum counts. "Invalid" if the request + or project ids were invalid. """ try: results = await cache_request(request_id, project_id) @@ -267,7 +279,8 @@ async def results_for_id( :param request_id: The id of the result. :param skip: How many assemblies to skip from the start. :param limit: How many assemblies to return. - :return: A list of assemblies of size up to limit. "Invalid" if the request or project ids were invalid. + :return: A list of assemblies of size up to limit. "Invalid" if the request or + project ids were invalid. """ if limit == 0: return [] @@ -294,10 +307,12 @@ async def results_for_id( async def results_for_result_id(project_id: str, request_id: str, result_id: int): """ Returns a single assembly from a synthesis result. + :param project_id: The project id of the project the result is from. :param request_id: The id of the result. :param result_id: The index of the assembly in the result. - :return: The assembly, or "" if the index did not exist. "Invalid" if the request or project ids were invalid. + :return: The assembly, or "" if the index did not exist. "Invalid" if the request or + project ids were invalid. """ try: results = await cache_request(request_id, project_id) diff --git a/applications/cls-cad-backend/cls_cad_backend/util/hrid.py b/applications/cls-cad-backend/cls_cad_backend/util/hrid.py index 476b13f..4cdee9a 100644 --- a/applications/cls-cad-backend/cls_cad_backend/util/hrid.py +++ b/applications/cls-cad-backend/cls_cad_backend/util/hrid.py @@ -10,9 +10,10 @@ def generate_id(separator: str = "-", word_count: int = 6) -> str: """ - Generate a human readable ID - :param separator: The string to use to separate words - :param seed: The seed to use. The same seed will produce the same ID + Generate a human readable ID :param separator: The string to use to separate words + :param seed: The seed to use. + + The same seed will produce the same ID :param word_count: The number of words to use. Minimum of 3. :return: A human readable ID """ diff --git a/applications/cls-cad-backend/cls_cad_backend/util/json_operations.py b/applications/cls-cad-backend/cls_cad_backend/util/json_operations.py index 818e3ac..ffef305 100644 --- a/applications/cls-cad-backend/cls_cad_backend/util/json_operations.py +++ b/applications/cls-cad-backend/cls_cad_backend/util/json_operations.py @@ -16,8 +16,8 @@ def postprocess(data: dict | Part): """ - Used to post-process a tree-like dictionary from interpreting a term into a flat list of part counts and assembly - instructions. + Used to post-process a tree-like dictionary from interpreting a term into a flat + list of part counts and assembly instructions. :param data: The tree-like dictionary. :return: The post-processed flat dictionary. @@ -43,8 +43,9 @@ def postprocess(data: dict | Part): def resolve_multiplicity(data: dict): """ - Propagates all multiplicities of joints (multiple identical physical joints being mapped to the same type) through - the dictionary by multiplying their subtree multiplicities accordingly. + Propagates all multiplicities of joints (multiple identical physical joints being + mapped to the same type) through the dictionary by multiplying their subtree + multiplicities accordingly. :param data: The tree-like dictionary with multiplicities not yet resolved. :return: A tree-like dictionary with resolved multiplicities. @@ -68,7 +69,8 @@ def resolve_multiplicity(data: dict): def compute_insertions_and_totals(data: dict) -> tuple: """ - Aggregates all parts present in the tree-like assembly dictionary, computing their total counts and costs. + Aggregates all parts present in the tree-like assembly dictionary, computing their + total counts and costs. :param data: The tree-like dictionary. :return: A dictionary of part counts and costs. @@ -89,8 +91,9 @@ def compute_insertions_and_totals(data: dict) -> tuple: def compute_instructions(data): """ - Traverse the tree-like assembly dict in the order that it must be assembled in. Creates assembly instructions in - the traversed order, thus flattening the tree-like structure. + Traverse the tree-like assembly dict in the order that it must be assembled in. + Creates assembly instructions in the traversed order, thus flattening the tree-like + structure. :param data: The tree-like dictionary. :return: A set of assembly instructions, and the total amount of encountered links. @@ -118,7 +121,8 @@ def compute_instructions(data): def remove_unused_keys_from_part_json(data: dict): """ - Removes metadata keys that are not relevant to the assembly instructions, reducing JSON size. + Removes metadata keys that are not relevant to the assembly instructions, reducing + JSON size. :param data: The dictionary with unused keys. :return: The dictionary without unused keys. @@ -131,10 +135,12 @@ def remove_unused_keys_from_part_json(data: dict): def invert_taxonomy(taxonomy): """ - Converts a taxonomy where the keys denote supertypes to one where the keys denote subtypes, and vice versa. + Converts a taxonomy where the keys denote supertypes to one where the keys denote + subtypes, and vice versa. :param taxonomy: The taxonomy before inversion. - :return: The inverted taxonomy, or a default taxonomy if the passed taxonomy was empty. + :return: The inverted taxonomy, or a default taxonomy if the passed taxonomy was + empty. """ if not taxonomy: return { @@ -164,7 +170,8 @@ def invert_taxonomy(taxonomy): def invert_sub_taxonomy(sub_taxonomy): """ - Converts a sub-taxonomy where the keys denote supertypes to one where the keys denote subtypes, and vice versa. + Converts a sub-taxonomy where the keys denote supertypes to one where the keys + denote subtypes, and vice versa. :param sub_taxonomy: The taxonomy before inversion. :return: The inverted taxonomy. @@ -179,10 +186,11 @@ def invert_sub_taxonomy(sub_taxonomy): def suffix_and_merge_taxonomy(taxonomy: dict): """ - Merges individual taxonomies into a single one for usage with PiCLS. This is done by suffixing the types in each - taxonomy by the individual taxonomies names. + Merges individual taxonomies into a single one for usage with PiCLS. This is done by + suffixing the types in each taxonomy by the individual taxonomies names. - :param taxonomy: The taxonomy dictionary with the individual taxonomies separated (as stored in database). + :param taxonomy: The taxonomy dictionary with the individual taxonomies separated + (as stored in database). :return: The merged taxonomy. """ suffixed_taxonomy = {} diff --git a/applications/cls-cad-backend/cls_cad_backend/util/motion.py b/applications/cls-cad-backend/cls_cad_backend/util/motion.py index 2173937..7508315 100644 --- a/applications/cls-cad-backend/cls_cad_backend/util/motion.py +++ b/applications/cls-cad-backend/cls_cad_backend/util/motion.py @@ -1,9 +1,9 @@ def combine_motions(motion1: str, motion2: str): """ - Composes two motions. If one JointOrigin is Rigid, it follow the motion the other JointOrigin permits. - If two motions are the same, their composition is their identity. - If both motions are different and not-rigid, this is undefined behaviour, so the least restrictive Joint type is - chosen, a ball joint, constraining no axes. + Composes two motions. If one JointOrigin is Rigid, it follow the motion the other + JointOrigin permits. If two motions are the same, their composition is their + identity. If both motions are different and not-rigid, this is undefined behaviour, + so the least restrictive Joint type is chosen, a ball joint, constraining no axes. :param motion1: The first motion. :param motion2: The second motion. diff --git a/applications/cls-cad-backend/main.py b/applications/cls-cad-backend/main.py index cc12d53..bd71f0c 100644 --- a/applications/cls-cad-backend/main.py +++ b/applications/cls-cad-backend/main.py @@ -2,7 +2,7 @@ def start(): - """Launched with `poetry run start` at root level""" + """Launched with `poetry run start` at root level.""" uvicorn.run("cls_cad_backend.server:app", reload=True) diff --git a/applications/cls-cad-fusion-plugin/.pre-commit-config.yaml b/applications/cls-cad-fusion-plugin/.pre-commit-config.yaml index fc4ac22..24b9beb 100644 --- a/applications/cls-cad-fusion-plugin/.pre-commit-config.yaml +++ b/applications/cls-cad-fusion-plugin/.pre-commit-config.yaml @@ -1,16 +1,6 @@ # See https://pre-commit.com for more information # See https://pre-commit.com/hooks.html for more hooks repos: - - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml - - id: check-json - - id: check-added-large-files - - id: mixed-line-ending - args: ["--fix=lf"] - repo: https://github.com/pre-commit/mirrors-prettier rev: v2.5.1 hooks: @@ -49,3 +39,13 @@ repos: hooks: - id: black exclude: ^resources/ + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-yaml + - id: check-json + - id: check-added-large-files + - id: mixed-line-ending + args: ["--fix=lf"] diff --git a/applications/cls-cad-fusion-plugin/CLS-CAD.py b/applications/cls-cad-fusion-plugin/CLS-CAD.py index 1fe11c7..7c4133b 100644 --- a/applications/cls-cad-fusion-plugin/CLS-CAD.py +++ b/applications/cls-cad-fusion-plugin/CLS-CAD.py @@ -4,6 +4,9 @@ def run(context): + """ + :param context: + """ try: # This will run the start function in each of your commands as defined in commands/__init__.py commands.start() @@ -13,6 +16,9 @@ def run(context): def stop(context): + """ + :param context: + """ try: # Remove all of the event handlers your app has created futil.clear_handlers() diff --git a/applications/cls-cad-fusion-plugin/commands/__init__.py b/applications/cls-cad-fusion-plugin/commands/__init__.py index fe05ac4..45780e3 100644 --- a/applications/cls-cad-fusion-plugin/commands/__init__.py +++ b/applications/cls-cad-fusion-plugin/commands/__init__.py @@ -49,6 +49,7 @@ def add_tab() -> adsk.core.ToolbarTab: + """""" design_workspace = ui.workspaces.itemById("FusionSolidEnvironment") if design_workspace: @@ -65,6 +66,11 @@ def add_tab() -> adsk.core.ToolbarTab: def add_panel( parent_tab: adsk.core.ToolbarTab, panel_id: str, panel_name: str ) -> adsk.core.ToolbarPanel: + """ + :param parent_tab: adsk.core.ToolbarTab: + :param panel_id: str: + :param panel_name: str: + """ new_panel = parent_tab.toolbarPanels.itemById(panel_id) if new_panel is None: @@ -79,6 +85,7 @@ def add_panel( # Assumes you defined a "start" function in each of your modules. # The start function will be run when the add-in is started. def start(): + """""" main_tab = add_tab() if not main_tab.isActive: main_tab.activate @@ -97,6 +104,9 @@ def start(): def application_document_opened(args: adsk.core.DocumentEventArgs): + """ + :param args: adsk.core.DocumentEventArgs: + """ # Restore custom graphics if assemble_result.NO_GRAPHICS: return @@ -111,5 +121,6 @@ def application_document_opened(args: adsk.core.DocumentEventArgs): # Assumes you defined a "stop" function in each of your modules. # The stop function will be run when the add-in is stopped. def stop(): + """""" for command in commands: command.stop() diff --git a/applications/cls-cad-fusion-plugin/commands/assembleresult/entry.py b/applications/cls-cad-fusion-plugin/commands/assembleresult/entry.py index 79e12e7..e19ac64 100644 --- a/applications/cls-cad-fusion-plugin/commands/assembleresult/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/assembleresult/entry.py @@ -41,10 +41,9 @@ def start(): """ Creates the promoted "Assemble Result" command in the CLS-CAD tab. - Registers the commandCreated handler. - - Returns: + Registers the commandCreated handler. + :return: """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_Description, ICON_FOLDER @@ -60,10 +59,10 @@ def start(): def stop(): """ - Removes this command from the CLS-CAD tab. Also removes the associated palette. - - Returns: + Removes this command from the CLS-CAD tab. + Also removes the associated palette. + :return: """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) @@ -83,14 +82,11 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): """ - Called when the user clicks the command in CLS-CAD tab. - Registers execute and destroy handlers. - - Args: - args: A CommandCreatedEventArgs that allows access to the commands properties and inputs. - - Returns: + Called when the user clicks the command in CLS-CAD tab. Registers execute and + destroy handlers. + :param args: adsk.core.CommandCreatedEventArgs: and inputs. + :return: """ futil.log(f"{CMD_NAME}: Command created event.") @@ -105,15 +101,12 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): def command_execute(args: adsk.core.CommandEventArgs): """ - Executes immediately when the command is clicked in the CLS-CAD tab, since there are no inputs. - If the palette does not already exist, it is created. - The palette is sets to be visible and docked. - - Args: - args: A CommandEventArgs that allows access to the commands properties and inputs. - - Returns: + Executes immediately when the command is clicked in the CLS-CAD tab, since there are + no inputs. If the palette does not already exist, it is created. The palette is set + to be visible and docked. + :param args: adsk.core.CommandEventArgs: inputs. + :return: """ futil.log(f"{CMD_NAME}: Command execute event.") @@ -147,6 +140,12 @@ def command_execute(args: adsk.core.CommandEventArgs): def center_in_window(): + """ + Selects the entire assembly and then executes "FindInWindow" Fusion 360 command. + + Used to graphically show the assembly during the assembly process. + :return: + """ if NO_GRAPHICS: return global progress_dialog @@ -170,6 +169,13 @@ def center_in_window(): def do_events_for_duration(duration: float): + """ + Allows the main Fusion 360 thread to process ui events for the specified duration. + Prevents the interface lagging during the assembly process. + + :param duration: float: The duration for which to process ui events. + :return: + """ start_time = timer() while timer() - start_time < duration: adsk.doEvents() @@ -182,13 +188,10 @@ def create_offset_joint_origin_in_occurence( Creates a JointOrigin that is offset from its parent geometry by specified vector. The JointOrigin is created in specified occurrence. - Args: - source_joint_origin: The JointOrigin the new JointOrigin is an offset copy off. - offset_vector: The offset Vector3D {x, y, z} - occurrence: The occurrence to create the JointOrigin in - - Returns: - + :param source_joint_origin: The JointOrigin the new JointOrigin is an offset copy + :param offset_vector: The offset Vector3D {x, y, z} + :param occurrence: The occurrence to create the JointOrigin in + :return: off. """ joint_origin_input = occurrence.component.jointOrigins.createInput( source_joint_origin.geometry @@ -199,12 +202,20 @@ def create_offset_joint_origin_in_occurence( return occurrence.component.jointOrigins.add(joint_origin_input) -def create_ground_joint(ground_joint): +def create_ground_joint(ground_joint_origin): + """ + Creates a joint between the passed JointOrigin and the origin of the coordinate + system. This is used to force the assembly to be upright and consistently oriented. + + :param ground_joint_origin: The JointOrigin which identifies the root of the + assembly. + :return: The newly created joint. + """ global design root = design.rootComponent joints = root.joints joint_input = joints.createInput( - ground_joint, + ground_joint_origin, adsk.fusion.JointGeometry.createByPoint( design.rootComponent.originConstructionPoint ), @@ -217,18 +228,17 @@ def create_ground_joint(ground_joint): def palette_incoming(html_args: adsk.core.HTMLEventArgs): """ - Handles incoming messages from the JavaScript portion of the palette. - Receives an "assembleMessage" that contains the JSON representation of the assembly as data. - If not present, the necessary folders in the project are created. - The initial part of the assembly is inserted and then assemble_recursively is called to start the assembly process. - - Sets the document to direct modeling mode during assembly to improve performance and avoid congested history. + Handles incoming messages from the JavaScript portion of the palette. Receives an + "assembleMessage" that contains the JSON representation of the assembly as data. If + not present, the necessary folders in the project are created. The initial part of + the assembly is inserted and then assemble_recursively is called to start the + assembly process. - Args: - html_args: - - Returns: + Sets the document to direct modeling mode during assembly to improve performance and + avoid congested history. + :param html_args: adsk.core.HTMLEventArgs: The received HTML event containing data + :return: """ futil.log(f"{CMD_NAME}: Palette incoming event.") @@ -297,6 +307,16 @@ def palette_incoming(html_args: adsk.core.HTMLEventArgs): def buckets_saved_handler(args: adsk.core.DataEventArgs): + """ + Waits for the initial template file created to batch assemble result to be + successfully saved. This allows the user interface to completely free up before the + lengthy batch assembly process begins and ensures the bucket datafile is completely + done processing. + + :param args: adsk.core.DataEventArgs: The event data, containing info about the + saved file. + :return: + """ global bucket_primed if ( ("Parts Template" in args.file.name) @@ -306,6 +326,20 @@ def buckets_saved_handler(args: adsk.core.DataEventArgs): def create_assembly_documents(data, maxcounts, name): + """ + Batch assembles all assemblies contained in data. To achieve this, creates part + buckets containing the amount of part specified in maxcounts, respectively. The part + bucket template gets copied again and again, extraneous parts deleted w.r.t. to the + specific assembly and then an assembly gets made. This is a lot faster than creating + individual assemblies, because the bulk of the per-assembly runtime is inserting + parts. + + :param data: The data containing the instructions for all assemblies to be built. + :param name: The name of the folder in which results will be stored (created if not + present) + :param maxcounts: The computed maximum counts for the part buckets. + :return: + """ global progress_dialog, NO_GRAPHICS, design, bucket_primed, bucket_attributes bucket_primed = True bucket_attributes = defaultdict(list) @@ -394,6 +428,16 @@ def create_assembly_documents(data, maxcounts, name): def create_assembly_document(data, name): + """ + Creates a single assembly document based on the instructions in data. Inserts all + parts contained in data, and then creates joints in the order that the instructions + specify. + + :param data: The instruction data for creating the assembly. + :param name: The name of the result to be placed in the "Synthesized Assemblies" + folder. + :return: + """ global progress_dialog, design, bucket_attributes, NO_GRAPHICS bucket_attributes = defaultdict(list) total_time = timer() @@ -435,6 +479,16 @@ def create_assembly_document(data, name): def insert_part_into_bucket_from_quantity_information(forge_document_id, count): + """ + Based on a given document id and a count, create a bucket that contains that part + count times. + + Uses the patter command to speed up creation. The bucket gets an attribute added to + it to be easily retrievable during assembly. + :param forge_document_id: The document id of the part to insert + :param count: The amount of copies of the part to place in the bucket + :return: + """ global progress_dialog, bucket_attributes root = adsk.fusion.Design.cast(app.activeProduct).rootComponent document = app.data.findFileById(forge_document_id) @@ -471,6 +525,15 @@ def insert_part_into_bucket_from_quantity_information(forge_document_id, count): def create_copies_of_part_in_container(count, inserted_occurrence, parts_container): + """ + Creates count copies of a part inside a wrapping component. Uses the rectangular + pattern command internally. + + :param count: The amount of copies to create. + :param parts_container: The wrapping component to place the copies in. + :param inserted_occurrence: The original occurrence of the part to copy. + :return: + """ global progress_dialog root = adsk.fusion.Design.cast(app.activeProduct).rootComponent object_collection_wrapper_for_part = adsk.core.ObjectCollection.create() @@ -498,6 +561,12 @@ def create_copies_of_part_in_container(count, inserted_occurrence, parts_contain def create_links(count): + """ + Create a set of empty wrapping components with names link_0 to link_count. + + :param count: The amount of wrapping components to create. + :return: + """ app.activeDocument.dataFile root = adsk.fusion.Design.cast(app.activeProduct).rootComponent link_occurrences = {} @@ -515,6 +584,18 @@ def move_part_to_links_from_instructions( group: str = "BUCKET", tag: str = "DOCUMENT", ): + """ + Move parts from their wrapping bucket to the corresponding assembly link in which + they belong, based on the assembly instructions. + + :param instructions: The instructions containing information about which parts to + move where. + :param group: str: (Default value = "BUCKET") The attribute group of the buckets. + :param tag: str: (Default value = "DOCUMENT") The tag of the bucket to find parts to + move in. + :param link_occurrences: The list of wrapping components serving as kinematic links. + :return: + """ global design, bucket_attributes for joint_info in instructions: link, move, count = ( @@ -544,12 +625,32 @@ def move_part_to_links_from_instructions( def remove_tagged_occurrences(group: str, tag: str): + """ + Remove all occurrences from the design that are in attribute group "group" and are + tagged with "tag". + + Primarily used to delete wrapping components that are no longer needed after + assembly, i.e., the buckets. + :param group: str: The attribute group to remove from. + :param tag: str: The tag to remove. + :return: + """ global design for bucket_occurrence in [x.parent for x in design.findAttributes(group, tag)]: bucket_occurrence.deleteMe() def create_joints_from_instructions(instructions): + """ + Exexcute the set of instructions that detail which joints to create between which + JointOrigins and in which order. + + The JointOrigin's uuid attributes are utilized to find them in the assembly tree. + Previous move operation into wrapping components must match the order of the + instructions. + :param instructions: The instructions for assembly to execute. + :return: + """ global progress_dialog global design progress_dialog.message = "Creating all Joints..." @@ -583,6 +684,13 @@ def create_joints_from_instructions(instructions): def create_result_class_folder(progress_dialog, name="User Picked Name"): + """ + Create a folder to house batch assembled results. + + :param progress_dialog: A progress dialog reference to display feedback in. + :param name: Default value = "User Picked Name") The name of the folder to create. + :return: + """ results_folder = create_results_folder(progress_dialog) request_folder = wrapped_forge_call( partial( @@ -600,6 +708,13 @@ def create_result_class_folder(progress_dialog, name="User Picked Name"): def create_results_folder(progress_dialog, folder_name: str): + """ + Create a folder to house assembled results. + + :param progress_dialog: A progress dialog reference to display feedback in. + :param folder_name: str: The name of the folder to create. + :return: + """ root_folder_children = ( app.activeDocument.dataFile.parentProject.rootFolder.dataFolders if app.activeDocument.dataFile is not None @@ -623,6 +738,18 @@ def create_joint_from_typed_joint_origins( motion, link: adsk.fusion.Occurrence = None, ): + """ + Create a joint of specified motion type between two joint origins. + + The joint is either created with a wrapping component as parent or in the global + context, depending on if a link was passed or not. + :param target_joint_origin: The JointOrigin to attach to. + :param motion: The joint motion (Rigid or Revolute are supported) + :param source_joint_origin: The JointOrigin to be attached. + :param link: adsk.fusion.Occurrence: An optional link that serves as the parent of + the joint. + :return: + """ global design root = design.rootComponent target_joint_origin.attributes.itemByName("CLS-INFO", "UUID").deleteMe() @@ -641,13 +768,11 @@ def create_joint_from_typed_joint_origins( def command_destroy(args: adsk.core.CommandEventArgs): """ - Logs the command terminating. This is instantly the case upon clicking, as the command only opens the palette. - - Args: - args: Unused - - Returns: + Logs the command terminating. This is instantly the case upon clicking, as the + command only opens the palette. + :param args: adsk.core.CommandEventArgs: + :return: """ # General logging for debug. futil.log(f"{CMD_NAME}: Command destroy event.") diff --git a/applications/cls-cad-fusion-plugin/commands/attributestaxonomyediting/entry.py b/applications/cls-cad-fusion-plugin/commands/attributestaxonomyediting/entry.py index d7c9c4b..b775ba1 100644 --- a/applications/cls-cad-fusion-plugin/commands/attributestaxonomyediting/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/attributestaxonomyediting/entry.py @@ -34,6 +34,13 @@ def start(): + """ + Creates the promoted "Edit Taxonomy" command in the CLS-CAD tab, for the attribute + taxonomy. + + Registers the commandCreated handler. + :return: + """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER ) @@ -48,6 +55,13 @@ def start(): def stop(): + """ + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + + This does not fail, even if the panel is emptied by multiple commands. + :return: + """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) command_control = panel.controls.itemById(CMD_ID) @@ -64,6 +78,13 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): + """ + Called when the user clicks the command in CLS-CAD tab. Registers execute and + destroy handlers. + + :param args: adsk.core.CommandCreatedEventArgs: + :return: + """ futil.log(f"{CMD_NAME}: Command created event.") load_project_taxonomy_to_config() futil.add_handler( @@ -75,6 +96,14 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): def command_execute(args: adsk.core.CommandEventArgs): + """ + Executes immediately when the command is clicked in the CLS-CAD tab, since there are + no inputs. If the palette does not already exist, it is created. The palette is set + to be visible and docked. + + :param args: adsk.core.CommandEventArgs: + :return: + """ palettes = ui.palettes palette = palettes.itemById(PALETTE_ID) if palette is not None: @@ -93,8 +122,6 @@ def command_execute(args: adsk.core.CommandEventArgs): height=800, useNewWebBrowser=True, ) - futil.add_handler(palette.closed, palette_closed) - futil.add_handler(palette.navigatingURL, palette_navigating) futil.add_handler(palette.incomingFromHTML, palette_incoming) args.command.setDialogMinimumSize(1200, 800) @@ -106,23 +133,15 @@ def command_execute(args: adsk.core.CommandEventArgs): futil.log(f"{CMD_NAME}: Command execute event.") -def palette_closed(args: adsk.core.UserInterfaceGeneralEventArgs): - futil.log(f"{CMD_NAME}: Palette was closed.") - - -def palette_navigating(args: adsk.core.NavigationEventArgs): - futil.log(f"{CMD_NAME}: Palette navigating event.") - - url = args.navigationURL - - log_msg = f"User is attempting to navigate to {url}\n" - futil.log(log_msg, adsk.core.LogLevels.InfoLogLevel) - - if url.startswith("http"): - args.launchExternally = True - - def palette_incoming(html_args: adsk.core.HTMLEventArgs): + """ + Handles incoming messages from the JavaScript portion of the palette. Receives an + "taxonomyDataMessage" that contains the JSON representation of the attribute + taxonomy. + + :param html_args: adsk.core.HTMLEventArgs: The received HTML event containing data. + :return: + """ futil.log(f"{CMD_NAME}: Palette incoming event.") message_data: dict = json.loads(html_args.data) @@ -161,6 +180,13 @@ def palette_incoming(html_args: adsk.core.HTMLEventArgs): def command_destroy(args: adsk.core.CommandEventArgs): + """ + Logs the command terminating. This is instantly the case upon clicking, as the + command only opens the palette. + + :param args: adsk.core.CommandEventArgs: + :return: + """ futil.log(f"{CMD_NAME}: Command destroy event.") global local_handlers diff --git a/applications/cls-cad-fusion-plugin/commands/checkandsubmit/entry.py b/applications/cls-cad-fusion-plugin/commands/checkandsubmit/entry.py index 85de000..7f6bc04 100644 --- a/applications/cls-cad-fusion-plugin/commands/checkandsubmit/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/checkandsubmit/entry.py @@ -28,10 +28,9 @@ def start(): """ Creates the promoted "Check and Submit" command in the CLS-CAD tab. - Registers the commandCreated handler. - - Returns: + Registers the commandCreated handler. + :return: """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_Description, ICON_FOLDER @@ -46,11 +45,11 @@ def start(): def stop(): """ - Removes this command from the CLS-CAD tab along with all others it shares a panel with. - This does not fail, even if the panel is emptied by multiple commands. - - Returns: + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + This does not fail, even if the panel is emptied by multiple commands. + :return: """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) @@ -69,14 +68,12 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): """ - Called when the user clicks the command in CLS-CAD tab. - Registers execute, destroy and most importantly validate handlers. - - Args: - args: A CommandCreatedEventArgs that allows access to the commands properties and inputs. - - Returns: + Called when the user clicks the command in CLS-CAD tab. Registers execute, destroy + and most importantly validate handlers. + :param args: A CommandCreatedEventArgs that allows access to the commands properties + and inputs. + :return: """ futil.log(f"{CMD_NAME} Command Created Event") global type_text_box_input @@ -104,22 +101,17 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): def command_execute(args: adsk.core.CommandEventArgs): """ - This method is called when the user clicks the "OK" button. - It concatenates the singular taxonomies into a combined one. For details see ~create_backend_taxonomy. + This method is called when the user clicks the "OK" button. It concatenates the + singular taxonomies into a combined one. For details see ~create_backend_taxonomy. It assembles the JSON representing the part. For details see ~create_backend_json. - These types are then intersected. A constructor with the ordered UUIDs as string acts as guard per configuration. + These types are then intersected. A constructor with the ordered UUIDs as string + acts as guard per configuration. - The method connects to the backend at /submit/part and submit/taxonomy and posts the created JSON files. - - Attributes: - Reads: (CLS-INFO, UUID),(CLS-JOINT,*) - Sets: None - - Args: - args: A CommandEventArgs that allows access to the commands properties and inputs. - - Returns: + The method connects to the backend at /submit/part and submit/taxonomy and posts the + created JSON files. + :param args: adsk.core.CommandEventArgs: inputs. + :return: """ futil.log(f"{CMD_NAME} Command Execute Event") @@ -150,11 +142,11 @@ def command_execute(args: adsk.core.CommandEventArgs): def create_backend_json(): """ - Assembles the JSON representing the part by creating a type for every "provides" joint. - Duplicate UUIDs are removed and instead added as "count" to the jointOrderInfo objects. - - Returns: The created JSON/dict. + Assembles the JSON representing the part by creating a type for every "provides" + joint. Duplicate UUIDs are removed and instead added as "count" to the + jointOrderInfo objects. + :return: The created JSON/dict. """ app = adsk.core.Application.get() design = adsk.fusion.Design.cast(app.activeProduct) @@ -284,14 +276,12 @@ def create_backend_json(): def command_validate(args: adsk.core.ValidateInputsEventArgs): """ - The method checks if there is at least one JointOrigin present that has received a ProvidesFormat. - This implies the part can be connected at that JointOrigin, making it usable. - - Args: - args: A ValidateInputsEventArgs that allows setting the current validity state. - - Returns: + The method checks if there is at least one JointOrigin present that has received a + ProvidesFormat. This implies the part can be connected at that JointOrigin, making + it usable. + :param args: adsk.core.ValidateInputsEventArgs: state. + :return: """ app = adsk.core.Application.get() design = adsk.fusion.Design.cast(app.activeProduct) @@ -307,13 +297,11 @@ def command_validate(args: adsk.core.ValidateInputsEventArgs): def command_destroy(args: adsk.core.CommandEventArgs): """ - Logs that the command was destroyed (window closed). Currently, does not clean up anything. - - Args: - args: A CommandEventArgs that allows access to the commands properties and inputs. - - Returns: + Logs that the command was destroyed (window closed). Currently, does not clean up + anything. + :param args: adsk.core.CommandEventArgs: inputs. + :return: """ global local_handlers local_handlers = [] diff --git a/applications/cls-cad-fusion-plugin/commands/downloadtaxonomy/entry.py b/applications/cls-cad-fusion-plugin/commands/downloadtaxonomy/entry.py index f202e0b..bfaea19 100644 --- a/applications/cls-cad-fusion-plugin/commands/downloadtaxonomy/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/downloadtaxonomy/entry.py @@ -24,6 +24,12 @@ def start(): + """ + Creates the promoted "Download Taxonomy" command in the CLS-CAD tab. + + Registers the commandCreated handler. + :return: + """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER ) @@ -36,6 +42,13 @@ def start(): def stop(): + """ + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + + This does not fail, even if the panel is emptied by multiple commands. + :return: + """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) command_definition = ui.commandDefinitions.itemById(CMD_ID) @@ -49,27 +62,33 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): + """ + Called when the user clicks the command in CLS-CAD tab. Registers execute and + destroy handlers. + + :param args: A CommandCreatedEventArgs that allows access to the commands properties + and inputs. + :return: + """ futil.log(f"{CMD_NAME} Command Created Event") futil.add_handler( args.command.execute, command_execute, local_handlers=local_handlers ) - futil.add_handler( - args.command.inputChanged, command_input_changed, local_handlers=local_handlers - ) - futil.add_handler( - args.command.executePreview, command_preview, local_handlers=local_handlers - ) futil.add_handler( args.command.destroy, command_destroy, local_handlers=local_handlers ) -def command_execute_preview(args: adsk.core.CommandEventHandler): - return - - def command_execute(args: adsk.core.CommandEventArgs): + """ + Executes immediately when the command is clicked in the CLS-CAD tab, since there are + no inputs. + + Displays a file dialog so the user can select where to store the project taxonomy. + :param args: adsk.core.CommandEventArgs: + :return: + """ futil.log(f"{CMD_NAME} Command Execute Event") file_dlg = ui.createFileDialog() file_dlg.isMultiSelectEnabled = True @@ -87,18 +106,14 @@ def command_execute(args: adsk.core.CommandEventArgs): return -def command_preview(args: adsk.core.CommandEventArgs): - futil.log(f"{CMD_NAME} Command Preview Event") - - -def command_input_changed(args: adsk.core.InputChangedEventArgs): - changed_input = args.input - futil.log( - f"{CMD_NAME} Input Changed Event fired from a change to {changed_input.id}" - ) - - def command_destroy(args: adsk.core.CommandEventArgs): + """ + Logs that the command was destroyed (window closed). Currently, does not clean up + anything. + + :param args: adsk.core.CommandEventArgs: inputs. + :return: + """ global local_handlers local_handlers = [] futil.log(f"{CMD_NAME} Command Destroy Event") diff --git a/applications/cls-cad-fusion-plugin/commands/exportproject/entry.py b/applications/cls-cad-fusion-plugin/commands/exportproject/entry.py index 1a37bd5..988302f 100644 --- a/applications/cls-cad-fusion-plugin/commands/exportproject/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/exportproject/entry.py @@ -30,10 +30,9 @@ def start(): """ Creates the promoted "Export Project" command in the CLS-CAD tab. - Registers the commandCreated handler. - - Returns: + Registers the commandCreated handler. + :return: """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER @@ -48,11 +47,11 @@ def start(): def stop(): """ - Removes this command from the CLS-CAD tab along with all others it shares a panel with. - This does not fail, even if the panel is emptied by multiple commands. - - Returns: + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + This does not fail, even if the panel is emptied by multiple commands. + :return: """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) @@ -68,14 +67,11 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): """ - Called when the user clicks the command in CLS-CAD tab. - Registers execute and destroy handlers. - - Args: - args: A CommandCreatedEventArgs that allows access to the commands properties and inputs. - - Returns: + Called when the user clicks the command in CLS-CAD tab. Registers execute and + destroy handlers. + :param args: adsk.core.CommandCreatedEventArgs: and inputs. + :return: """ futil.log(f"{CMD_NAME} Command Created Event") @@ -89,16 +85,14 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): def recursively_submit(folders: adsk.core.DataFolders): """ - For every passed DataFolder, all contained Files are opened that are f3d files. - The corresponding JSON for the backend is created and submitted if the file is valid. (Provides at least one format) - If the file is not valid, it is skipped. This may be changed to provide an error report in the future. - The file traversal is DFS, after a single folder is done, its children are processed immediately. - - Args: - folders: A list of DataFolders to process. - - Returns: - + For every passed DataFolder, all contained Files are opened that are f3d files. The + corresponding JSON for the backend is created and submitted if the file is valid. + (Provides at least one format) If the file is not valid, it is skipped. This may be + changed to provide an error report in the future. The file traversal is DFS, after a + single folder is done, its children are processed immediately. + + :param folders: adsk.core.DataFolders: + :return: """ global progress_dialog for folder in wrapped_forge_call(folders.asArray, progress_dialog): @@ -112,6 +106,13 @@ def recursively_submit(folders: adsk.core.DataFolders): def submit_files_in_folder(folder): + """ + Opens all files in a DataFolder in sequence and calls the CheckAndSubmit command on + them, submitting them to the backend database as long as they pass validation. + + :param folder: The DataFolder to process. + :return: + """ global progress_dialog folder_data_files = wrapped_forge_call(folder.dataFiles.asArray, progress_dialog) for file in folder_data_files: @@ -143,12 +144,15 @@ def submit_files_in_folder(folder): def slugify(value, allow_unicode=False): - """ - Taken from https://github.com/django/django/blob/master/django/utils/text.py + """Taken from https://github.com/django/django/blob/master/django/utils/text.py Convert to ASCII if 'allow_unicode' is False. Convert spaces or repeated dashes to single dashes. Remove characters that aren't alphanumerics, underscores, or hyphens. Convert to lowercase. Also strip leading and trailing whitespace, dashes, and underscores. + + :param value: param allow_unicode: (Default value = False) + :param allow_unicode: Default value = False) + :return: """ value = str(value) if allow_unicode: @@ -165,16 +169,13 @@ def slugify(value, allow_unicode=False): def command_execute(args: adsk.core.CommandEventArgs): """ - Executes immediately when user clicks the button in CLS-CAD tab as there are no command inputs. - Prompts the user to confirm beginning the lengthy crawling process. - Fetches the activeDocuments (priority, else active side browser position) projects root folder. - Recursively traverses this initial set of folders. - - Args: - args: - - Returns: + Executes immediately when user clicks the button in CLS-CAD tab as there are no + command inputs. Prompts the user to confirm beginning the lengthy crawling process. + Fetches the activeDocuments (priority, else active side browser position) projects + root folder. Recursively traverses this initial set of folders. + :param args: adsk.core.CommandEventArgs: + :return: """ futil.log(f"{CMD_NAME} Command Execute Event") @@ -236,13 +237,11 @@ def command_execute(args: adsk.core.CommandEventArgs): def command_destroy(args: adsk.core.CommandEventArgs): """ - Logs that the command was destroyed (window closed). Currently, does not clean up anything. - - Args: - args: A CommandEventArgs that allows access to the commands properties and inputs. - - Returns: + Logs that the command was destroyed (window closed). Currently, does not clean up + anything. + :param args: adsk.core.CommandEventArgs: inputs. + :return: """ global local_handlers local_handlers = [] diff --git a/applications/cls-cad-fusion-plugin/commands/formatstaxonomyediting/entry.py b/applications/cls-cad-fusion-plugin/commands/formatstaxonomyediting/entry.py index 43b333f..acb79fe 100644 --- a/applications/cls-cad-fusion-plugin/commands/formatstaxonomyediting/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/formatstaxonomyediting/entry.py @@ -34,6 +34,13 @@ def start(): + """ + Creates the promoted "Edit Taxonomy" command for the format taxonomy in the CLS-CAD + tab. + + Registers the commandCreated handler. + :return: + """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER ) @@ -48,6 +55,13 @@ def start(): def stop(): + """ + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + + This does not fail, even if the panel is emptied by multiple commands. + :return: + """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) command_control = panel.controls.itemById(CMD_ID) @@ -64,6 +78,13 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): + """ + Called when the user clicks the command in CLS-CAD tab. Registers execute and + destroy handlers. + + :param args: adsk.core.CommandCreatedEventArgs: + :return: + """ futil.log(f"{CMD_NAME}: Command created event.") load_project_taxonomy_to_config() futil.add_handler( @@ -75,6 +96,14 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): def command_execute(args: adsk.core.CommandEventArgs): + """ + Executes immediately when the command is clicked in the CLS-CAD tab, since there are + no inputs. If the palette does not already exist, it is created. The palette is set + to be visible and docked. + + :param args: adsk.core.CommandEventArgs: + :return: + """ palettes = ui.palettes palette = palettes.itemById(PALETTE_ID) if palette is not None: @@ -93,8 +122,6 @@ def command_execute(args: adsk.core.CommandEventArgs): height=800, useNewWebBrowser=True, ) - futil.add_handler(palette.closed, palette_closed) - futil.add_handler(palette.navigatingURL, palette_navigating) futil.add_handler(palette.incomingFromHTML, palette_incoming) args.command.setDialogMinimumSize(1200, 800) @@ -106,23 +133,14 @@ def command_execute(args: adsk.core.CommandEventArgs): futil.log(f"{CMD_NAME}: Command execute event.") -def palette_closed(args: adsk.core.UserInterfaceGeneralEventArgs): - futil.log(f"{CMD_NAME}: Palette was closed.") - - -def palette_navigating(args: adsk.core.NavigationEventArgs): - futil.log(f"{CMD_NAME}: Palette navigating event.") - - url = args.navigationURL - - log_msg = f"User is attempting to navigate to {url}\n" - futil.log(log_msg, adsk.core.LogLevels.InfoLogLevel) - - if url.startswith("http"): - args.launchExternally = True - - def palette_incoming(html_args: adsk.core.HTMLEventArgs): + """ + Handles incoming messages from the JavaScript portion of the palette. Receives an + "taxonomyDataMessage" that contains the JSON representation of the format taxonomy. + + :param html_args: adsk.core.HTMLEventArgs: The received HTML event containing data. + :return: + """ futil.log(f"{CMD_NAME}: Palette incoming event.") message_data: dict = json.loads(html_args.data) @@ -160,6 +178,13 @@ def palette_incoming(html_args: adsk.core.HTMLEventArgs): def command_destroy(args: adsk.core.CommandEventArgs): + """ + Logs the command terminating. This is instantly the case upon clicking, as the + command only opens the palette. + + :param args: adsk.core.CommandEventArgs: + :return: + """ futil.log(f"{CMD_NAME}: Command destroy event.") global local_handlers diff --git a/applications/cls-cad-fusion-plugin/commands/jointtyping/entry.py b/applications/cls-cad-fusion-plugin/commands/jointtyping/entry.py index d6a59ed..3f98104 100644 --- a/applications/cls-cad-fusion-plugin/commands/jointtyping/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/jointtyping/entry.py @@ -44,6 +44,12 @@ def start(): + """ + Creates the promoted "Typed Joint" command in the CLS-CAD tab. + + Registers the commandCreated handler. + :return: + """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER ) @@ -60,6 +66,13 @@ def start(): def stop(): + """ + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + + This does not fail, even if the panel is emptied by multiple commands. + :return: + """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) command_definition = ui.commandDefinitions.itemById(CMD_ID) @@ -87,6 +100,13 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): + """ + Called when the user clicks the command in CLS-CAD tab. Registers all important + handlers for the command. + + :param args: adsk.core.CommandCreatedEventArgs: + :return: + """ futil.log(f"{CMD_NAME} Command Created Event") load_project_taxonomy_to_config() @@ -97,44 +117,18 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): futil.add_handler( args.command.inputChanged, command_input_changed, local_handlers=local_handlers ) - futil.add_handler( - args.command.executePreview, command_preview, local_handlers=local_handlers - ) futil.add_handler( args.command.destroy, command_destroy, local_handlers=local_handlers ) - futil.add_handler( - args.command.preSelectMouseMove, - command_preselect_mousemove, - local_handlers=local_handlers, - ) - futil.add_handler( - args.command.preSelect, command_preselect, local_handlers=local_handlers - ) - futil.add_handler( - args.command.preSelectEnd, command_preselect_end, local_handlers=local_handlers - ) - futil.add_handler( - args.command.executePreview, - command_execute_preview, - local_handlers=local_handlers, - ) futil.add_handler( args.command.select, command_select, local_handlers=local_handlers ) futil.add_handler( args.command.unselect, command_unselect, local_handlers=local_handlers ) - futil.add_handler( - args.command.navigatingURL, palette_navigating, local_handlers=local_handlers - ) futil.add_handler( args.command.incomingFromHTML, palette_incoming, local_handlers=local_handlers ) - futil.add_handler( - args.command.activate, command_activate, local_handlers=local_handlers - ) - inputs = args.command.commandInputs args.command.setDialogMinimumSize(600, 800) args.command.setDialogInitialSize(600, 800) @@ -205,19 +199,16 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): ) -def command_execute_preview(args: adsk.core.CommandEventHandler): - app = adsk.core.Application.get() - design = adsk.fusion.Design.cast(app.activeProduct) - if design: - pass - - -def command_activate(args: adsk.core.CommandEventArgs): - app = adsk.core.Application.get() - app.log("In command_activate event handler.") - - def command_select(args: adsk.core.SelectionEventArgs): + """ + Fires when the user selects a JointOrigin. + + Adds the selected JointOrigin to a global selection list and updates the command's + ui accordingly. + :param args: adsk.core.SelectionEventArgs: The event data of the selection, + containing the entity. + :return: + """ app = adsk.core.Application.get() design = adsk.fusion.Design.cast(app.activeProduct) selected_joint_origin = adsk.fusion.JointOrigin.cast(args.selection.entity) @@ -276,6 +267,15 @@ def command_select(args: adsk.core.SelectionEventArgs): def command_unselect(args: adsk.core.SelectionEventArgs): + """ + Fires when the user selects a JointOrigin. + + Removes the selected JointOrigin from a global selection list and updates the + command's ui accordingly. + :param args: adsk.core.SelectionEventArgs: The event data of the selection, + containing the entity. + :return: + """ app = adsk.core.Application.get() design = adsk.fusion.Design.cast(app.activeProduct) selected_joint_origin = adsk.fusion.JointOrigin.cast(args.selection.entity) @@ -287,27 +287,16 @@ def command_unselect(args: adsk.core.SelectionEventArgs): ] -def command_preselect_mousemove(args: adsk.core.SelectionEventArgs): - app = adsk.core.Application.get() - design = adsk.fusion.Design.cast(app.activeProduct) - selected_joint_origin = adsk.fusion.JointOrigin.cast(args.selection.entity) - if design and selected_joint_origin: - pass - - -def command_preselect(args: adsk.core.SelectionEventArgs): - app = adsk.core.Application.get() - design = adsk.fusion.Design.cast(app.activeProduct) - selected_joint_origin = adsk.fusion.JointOrigin.cast(args.selection.entity) - if design and selected_joint_origin: - pass - - -def command_preselect_end(args: adsk.core.SelectionEventArgs): - pass - - def command_input_changed(args: adsk.core.InputChangedEventArgs): + """ + Fires when any of the command inputs changes. Specifically checks if the user has + selected the reset attributes checkbox, and cleans all pre-existing type information + in that case. + + :param args: adsk.core.InputChangedEventArgs: The event data containing info on + which input has changed. + :return: + """ if args.input.id == "resetAttribsBoolInput": global reset_bool_value_input, req_formats, req_attributes, req_parts, provides_formats, provides_parts, provides_attributes if reset_bool_value_input.value: @@ -321,19 +310,17 @@ def command_input_changed(args: adsk.core.InputChangedEventArgs): ) = ([], [], [], [], [], []) -def palette_navigating(args: adsk.core.NavigationEventArgs): - futil.log(f"{CMD_NAME}: Palette navigating event.") - - url = args.navigationURL - - log_msg = f"User is attempting to navigate to {url}\n" - futil.log(log_msg, adsk.core.LogLevels.InfoLogLevel) - - if url.startswith("http"): - args.launchExternally = True - - def palette_incoming(html_args: adsk.core.HTMLEventArgs): + """ + Handles incoming messages from the JavaScript portion of the palette. Receives an + "updateDataNotification", "renameDataNotification", or "selectionNotification", that + contain the JSON representation of data instructing the add-in to rename a type + internally, update a taxonomy with new data, or add an element from the palette to + the currently selected types. + + :param html_args: adsk.core.HTMLEventArgs: The received HTML event containing data. + :return: + """ futil.log(f"{CMD_NAME}: Palette incoming event.") message_data = json.loads(html_args.data) message_action = html_args.action @@ -414,6 +401,15 @@ def palette_incoming(html_args: adsk.core.HTMLEventArgs): def command_execute(args: adsk.core.CommandEventArgs): + """ + This method is called when the user clicks the "OK" button. It applies the selected + types from the palettes to all selected JointOrigins. If multiple JointOrigins are + selected, this marks them as being identical, meaning their subassemblies during + synthesis will be identical. + + :param args: adsk.core.CommandEventArgs: inputs. + :return: + """ futil.log(f"{CMD_NAME} Command Execute Event") app = adsk.core.Application.get() @@ -476,6 +472,12 @@ def command_execute(args: adsk.core.CommandEventArgs): def update_type_information_graphics(): + """ + Executes the display of custom graphics twice, effectively redrawing all custom + graphics. + + :return: + """ config.custom_graphics_displaying = True cmd = ui.commandDefinitions.itemById( f"{config.COMPANY_NAME}_{config.ADDIN_NAME}_toggle_display" @@ -484,11 +486,13 @@ def update_type_information_graphics(): cmd.execute() -def command_preview(args: adsk.core.CommandEventArgs): - futil.log(f"{CMD_NAME} Command Preview Event") - - def command_destroy(args: adsk.core.CommandEventArgs): + """ + Cleans up all collected data after the user has pressed the "Okay" button. + + :param args: adsk.core.CommandEventArgs: + :return: + """ global local_handlers, req_attributes, req_formats, req_parts, provides_attributes, provides_formats, provides_parts local_handlers = [] ( diff --git a/applications/cls-cad-fusion-plugin/commands/partmanagement/entry.py b/applications/cls-cad-fusion-plugin/commands/partmanagement/entry.py index 4dff90f..2c67024 100644 --- a/applications/cls-cad-fusion-plugin/commands/partmanagement/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/partmanagement/entry.py @@ -20,6 +20,12 @@ def start(): + """ + Creates the promoted "Manage Part" command in the CLS-CAD tab. + + Registers the commandCreated handler. + :return: + """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER ) @@ -33,6 +39,13 @@ def start(): def stop(): + """ + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + + This does not fail, even if the panel is emptied by multiple commands. + :return: + """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) command_definition = ui.commandDefinitions.itemById(CMD_ID) @@ -51,6 +64,13 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): + """ + Called when the user clicks the command in CLS-CAD tab. Registers all important + handlers for the command. + + :param args: adsk.core.CommandCreatedEventArgs: + :return: + """ global cost_value_input, availability_value_input futil.log(f"{CMD_NAME} Command Created Event") @@ -63,9 +83,6 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): futil.add_handler( args.command.destroy, command_destroy, local_handlers=local_handlers ) - futil.add_handler( - args.command.activate, command_activate, local_handlers=local_handlers - ) app = adsk.core.Application.get() design = adsk.fusion.Design.cast(app.activeProduct) @@ -97,12 +114,14 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): ) -def command_activate(args: adsk.core.CommandEventArgs): - app = adsk.core.Application.get() - app.log("In command_activate event handler.") - - def command_execute(args: adsk.core.CommandEventArgs): + """ + This method is called when the user clicks the "OK" button. It persists the + configured cost and availability in the rootComponents attributes. + + :param args: adsk.core.CommandEventArgs: inputs. + :return: + """ futil.log(f"{CMD_NAME} Command Execute Event") global cost_value_input, availability_value_input @@ -124,6 +143,13 @@ def command_execute(args: adsk.core.CommandEventArgs): def command_destroy(args: adsk.core.CommandEventArgs): + """ + Logs that the command was destroyed (window closed). Currently, does not clean up + anything. + + :param args: adsk.core.CommandEventArgs: inputs. + :return: + """ global local_handlers, provides_attributes, provides_parts provides_parts = [] provides_attributes = [] diff --git a/applications/cls-cad-fusion-plugin/commands/partstaxonomyediting/entry.py b/applications/cls-cad-fusion-plugin/commands/partstaxonomyediting/entry.py index 090de6e..da9ae19 100644 --- a/applications/cls-cad-fusion-plugin/commands/partstaxonomyediting/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/partstaxonomyediting/entry.py @@ -34,6 +34,13 @@ def start(): + """ + Creates the promoted "Edit Taxonomy" command for the part taxonomy in the CLS-CAD + tab. + + Registers the commandCreated handler. + :return: + """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER ) @@ -48,6 +55,13 @@ def start(): def stop(): + """ + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + + This does not fail, even if the panel is emptied by multiple commands. + :return: + """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) command_control = panel.controls.itemById(CMD_ID) @@ -64,6 +78,14 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): + """ + Called when the user clicks the command in CLS-CAD tab. Registers execute and + destroy handlers. + + :param args: A CommandCreatedEventArgs that allows access to the commands properties + and inputs. + :return: + """ futil.log(f"{CMD_NAME}: Command created event.") load_project_taxonomy_to_config() futil.add_handler( @@ -75,6 +97,14 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): def command_execute(args: adsk.core.CommandEventArgs): + """ + Executes immediately when the command is clicked in the CLS-CAD tab, since there are + no inputs. If the palette does not already exist, it is created. The palette is set + to be visible and docked. + + :param args: adsk.core.CommandEventArgs: + :return: + """ palettes = ui.palettes palette = palettes.itemById(PALETTE_ID) if palette is not None: @@ -93,8 +123,6 @@ def command_execute(args: adsk.core.CommandEventArgs): height=800, useNewWebBrowser=True, ) - futil.add_handler(palette.closed, palette_closed) - futil.add_handler(palette.navigatingURL, palette_navigating) futil.add_handler(palette.incomingFromHTML, palette_incoming) args.command.setDialogMinimumSize(1200, 800) @@ -106,23 +134,14 @@ def command_execute(args: adsk.core.CommandEventArgs): futil.log(f"{CMD_NAME}: Command execute event.") -def palette_closed(args: adsk.core.UserInterfaceGeneralEventArgs): - futil.log(f"{CMD_NAME}: Palette was closed.") - - -def palette_navigating(args: adsk.core.NavigationEventArgs): - futil.log(f"{CMD_NAME}: Palette navigating event.") - - url = args.navigationURL - - log_msg = f"User is attempting to navigate to {url}\n" - futil.log(log_msg, adsk.core.LogLevels.InfoLogLevel) - - if url.startswith("http"): - args.launchExternally = True - - def palette_incoming(html_args: adsk.core.HTMLEventArgs): + """ + Handles incoming messages from the JavaScript portion of the palette. Receives an + "taxonomyDataMessage" that contains the JSON representation of the part taxonomy. + + :param html_args: adsk.core.HTMLEventArgs: The received HTML event containing data. + :return: + """ futil.log(f"{CMD_NAME}: Palette incoming event.") message_data: dict = json.loads(html_args.data) @@ -161,6 +180,13 @@ def palette_incoming(html_args: adsk.core.HTMLEventArgs): def command_destroy(args: adsk.core.CommandEventArgs): + """ + Logs the command terminating. This is instantly the case upon clicking, as the + command only opens the palette. + + :param args: adsk.core.CommandEventArgs: + :return: + """ futil.log(f"{CMD_NAME}: Command destroy event.") global local_handlers diff --git a/applications/cls-cad-fusion-plugin/commands/parttyping/entry.py b/applications/cls-cad-fusion-plugin/commands/parttyping/entry.py index 38d88aa..5830533 100644 --- a/applications/cls-cad-fusion-plugin/commands/parttyping/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/parttyping/entry.py @@ -35,6 +35,12 @@ def start(): + """ + Creates the promoted "Typed Part" command in the CLS-CAD tab. + + Registers the commandCreated handler. + :return: + """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER ) @@ -48,6 +54,13 @@ def start(): def stop(): + """ + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + + This does not fail, even if the panel is emptied by multiple commands. + :return: + """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) command_definition = ui.commandDefinitions.itemById(CMD_ID) @@ -66,6 +79,13 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): + """ + Called when the user clicks the command in CLS-CAD tab. Registers all important + handlers for the command. + + :param args: adsk.core.CommandCreatedEventArgs: + :return: + """ global type_text_box_input, parts_type_selection_browser_input, provides_parts, provides_attributes futil.log(f"{CMD_NAME} Command Created Event") @@ -75,18 +95,12 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): futil.add_handler( args.command.execute, command_execute, local_handlers=local_handlers ) - futil.add_handler( - args.command.executePreview, command_preview, local_handlers=local_handlers - ) futil.add_handler( args.command.destroy, command_destroy, local_handlers=local_handlers ) futil.add_handler( args.command.incomingFromHTML, palette_incoming, local_handlers=local_handlers ) - futil.add_handler( - args.command.activate, command_activate, local_handlers=local_handlers - ) app = adsk.core.Application.get() design = adsk.fusion.Design.cast(app.activeProduct) provides_attributes = [ @@ -134,17 +148,18 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): minimumHeight=300, ) - -def command_execute_preview(args: adsk.core.CommandEventHandler): - type_text_box_input.text = generate_type_text() - - -def command_activate(args: adsk.core.CommandEventArgs): - app = adsk.core.Application.get() - app.log("In command_activate event handler.") + try: + type_text_box_input.formattedText = generate_type_text() + except Exception: + pass def generate_type_text(): + """ + Generates a html string representation of the resulting logic type of the part. + + :return: The generated string. + """ app = adsk.core.Application.get() design = adsk.fusion.Design.cast(app.activeProduct) typed_jos = [] @@ -211,6 +226,16 @@ def generate_type_text(): def palette_incoming(html_args: adsk.core.HTMLEventArgs): + """ + Handles incoming messages from the JavaScript portion of the palette. Receives an + "updateDataNotification", "renameDataNotification", or "selectionNotification", that + contain the JSON representation of data instructing the add-in to rename a type + internally, update a taxonomy with new data, or add an element from the palette to + the currently selected types for the part. + + :param html_args: adsk.core.HTMLEventArgs: The received HTML event containing data. + :return: + """ futil.log(f"{CMD_NAME}: Palette incoming event.") print("Incoming") message_data: dict = json.loads(html_args.data) @@ -268,6 +293,14 @@ def palette_incoming(html_args: adsk.core.HTMLEventArgs): def winapi_path(dos_path, encoding=None): + """ + Encodes a path into a winapi compatible path, this is important for longer paths + that exceed the standard char limit. + + :param dos_path: The path to encode. + :param encoding: Default value = None) The encoding to use. + :return: + """ if not isinstance(dos_path, str) and encoding is not None: dos_path = dos_path.decode(encoding) path = os.path.abspath(dos_path) @@ -277,6 +310,13 @@ def winapi_path(dos_path, encoding=None): def command_execute(args: adsk.core.CommandEventArgs): + """ + This method is called when the user clicks the "OK" button. It applies the selected + types from the palettes to the part itself. + + :param args: adsk.core.CommandEventArgs: inputs. + :return: + """ futil.log(f"{CMD_NAME} Command Execute Event") global provides_attributes, provides_parts @@ -295,12 +335,13 @@ def command_execute(args: adsk.core.CommandEventArgs): ) -def command_preview(args: adsk.core.CommandEventArgs): - type_text_box_input.formattedText = generate_type_text() - futil.log(f"{CMD_NAME} Command Preview Event") - - def command_destroy(args: adsk.core.CommandEventArgs): + """ + Cleans up all collected data after the user has pressed the "Okay" button. + + :param args: adsk.core.CommandEventArgs: + :return: + """ global local_handlers, provides_attributes, provides_parts provides_parts = [] provides_attributes = [] diff --git a/applications/cls-cad-fusion-plugin/commands/requestsynthesis/entry.py b/applications/cls-cad-fusion-plugin/commands/requestsynthesis/entry.py index 53ebd75..2c810df 100644 --- a/applications/cls-cad-fusion-plugin/commands/requestsynthesis/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/requestsynthesis/entry.py @@ -41,6 +41,12 @@ def add_row_to_table(table_input): + """ + Adds a row to the table for specifying the synthesis constraints. + + :param table_input: + :return: + """ global _rowNumber, counted_parts, counted_formats, counted_attributes, counted_types, selected_text cmd_inputs = adsk.core.CommandInputs.cast(table_input.commandInputs) @@ -72,6 +78,12 @@ def add_row_to_table(table_input): def start(): + """ + Creates the promoted "Request Synthesis" command in the CLS-CAD tab. + + Registers the commandCreated handler. + :return: + """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER ) @@ -84,6 +96,13 @@ def start(): def stop(): + """ + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + + This does not fail, even if the panel is emptied by multiple commands. + :return: + """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) command_definition = ui.commandDefinitions.itemById(CMD_ID) @@ -103,6 +122,13 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): + """ + Called when the user clicks the command in CLS-CAD tab. Registers all important + handlers for the command. + + :param args: adsk.core.CommandCreatedEventArgs: + :return: + """ global type_text_box_input, parts_type_selection_browser_input, request_parts, request_attributes, counted_parts, counted_attributes, counted_formats, selected_text futil.log(f"{CMD_NAME} Command Created Event") @@ -112,9 +138,6 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): futil.add_handler( args.command.execute, command_execute, local_handlers=local_handlers ) - futil.add_handler( - args.command.executePreview, command_preview, local_handlers=local_handlers - ) futil.add_handler( args.command.inputChanged, input_changed, local_handlers=local_handlers ) @@ -124,9 +147,6 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): futil.add_handler( args.command.incomingFromHTML, palette_incoming, local_handlers=local_handlers ) - futil.add_handler( - args.command.activate, command_activate, local_handlers=local_handlers - ) request_attributes = [] request_parts = [] @@ -191,6 +211,14 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): def input_changed(args: adsk.core.InputChangedEventArgs): + """ + Fires when any of the command inputs changes. Used to respond to any changes or + button clicks with regard to the synthesis constraints table. + + :param args: adsk.core.InputChangedEventArgs: The event data containing info on + which input has changed. + :return: + """ global counted_types table_input: adsk.core.TableCommandInput = args.inputs.itemById("amount_table") if args.input.id == "table_add": @@ -203,16 +231,17 @@ def input_changed(args: adsk.core.InputChangedEventArgs): table_input.deleteRow(table_input.selectedRow) -def command_execute_preview(args: adsk.core.CommandEventHandler): - pass - - -def command_activate(args: adsk.core.CommandEventArgs): - app = adsk.core.Application.get() - app.log("In command_activate event handler.") - - def palette_incoming(html_args: adsk.core.HTMLEventArgs): + """ + Handles incoming messages from the JavaScript portion of the palette. Receives an + "updateDataNotification", "renameDataNotification", or "selectionNotification", that + contain the JSON representation of data instructing the add-in to rename a type + internally, update a taxonomy with new data, or add an element from the palette to + the currently selected types for the synthesis request. + + :param html_args: adsk.core.HTMLEventArgs: The received HTML event containing data. + :return: + """ futil.log(f"{CMD_NAME}: Palette incoming event.") print("Incoming") message_data: dict = json.loads(html_args.data) @@ -301,6 +330,14 @@ def palette_incoming(html_args: adsk.core.HTMLEventArgs): def winapi_path(dos_path, encoding=None): + """ + Encodes a path into a winapi compatible path, this is important for longer paths + that exceed the standard char limit. + + :param dos_path: The path to encode. + :param encoding: Default value = None) The encoding to use. + :return: + """ if not isinstance(dos_path, str) and encoding is not None: dos_path = dos_path.decode(encoding) path = os.path.abspath(dos_path) @@ -310,6 +347,14 @@ def winapi_path(dos_path, encoding=None): def command_execute(args: adsk.core.CommandEventArgs): + """ + This method is called when the user clicks the "OK" button. It generates a JSON from + the collected information (selected types in palettes and table),describing a + synthesis request, and sends it to the backend. + + :param args: adsk.core.CommandEventArgs: inputs. + :return: + """ futil.log(f"{CMD_NAME} Command Execute Event") global request_attributes, request_parts @@ -345,11 +390,13 @@ def command_execute(args: adsk.core.CommandEventArgs): print(response.read().decode()) -def command_preview(args: adsk.core.CommandEventArgs): - futil.log(f"{CMD_NAME} Command Preview Event") - - def command_destroy(args: adsk.core.CommandEventArgs): + """ + Cleans up all collected data after the user has pressed the "Okay" button. + + :param args: adsk.core.CommandEventArgs: + :return: + """ global local_handlers, request_attributes, request_parts request_parts = [] request_attributes = [] diff --git a/applications/cls-cad-fusion-plugin/commands/togglecustomgraphics/entry.py b/applications/cls-cad-fusion-plugin/commands/togglecustomgraphics/entry.py index 2a14eb9..42c986d 100644 --- a/applications/cls-cad-fusion-plugin/commands/togglecustomgraphics/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/togglecustomgraphics/entry.py @@ -27,6 +27,12 @@ def start(): + """ + Creates the promoted "Toggle Display" command in the CLS-CAD tab. + + Registers the commandCreated handler. + :return: + """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER ) @@ -39,6 +45,13 @@ def start(): def stop(): + """ + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + + This does not fail, even if the panel is emptied by multiple commands. + :return: + """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) command_definition = ui.commandDefinitions.itemById(CMD_ID) @@ -52,27 +65,33 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): + """ + Called when the user clicks the command in CLS-CAD tab. Registers execute and + destroy handlers. + + :param args: A CommandCreatedEventArgs that allows access to the commands properties + and inputs. + :return: + """ futil.log(f"{CMD_NAME} Command Created Event") futil.add_handler( args.command.execute, command_execute, local_handlers=local_handlers ) - futil.add_handler( - args.command.inputChanged, command_input_changed, local_handlers=local_handlers - ) - futil.add_handler( - args.command.executePreview, command_preview, local_handlers=local_handlers - ) futil.add_handler( args.command.destroy, command_destroy, local_handlers=local_handlers ) -def command_execute_preview(args: adsk.core.CommandEventHandler): - return - - def command_execute(args: adsk.core.CommandEventArgs): + """ + Executes immediately when the command is clicked in the CLS-CAD tab, since there are + no inputs. + + Toggles if custom graphics for typed joints are displayed or not. + :param args: adsk.core.CommandEventArgs: + :return: + """ futil.log(f"{CMD_NAME} Command Execute Event") if assemble_result.NO_GRAPHICS: return @@ -120,18 +139,14 @@ def command_execute(args: adsk.core.CommandEventArgs): config.custom_graphics_displaying = True -def command_preview(args: adsk.core.CommandEventArgs): - futil.log(f"{CMD_NAME} Command Preview Event") - - -def command_input_changed(args: adsk.core.InputChangedEventArgs): - changed_input = args.input - futil.log( - f"{CMD_NAME} Input Changed Event fired from a change to {changed_input.id}" - ) - - def command_destroy(args: adsk.core.CommandEventArgs): + """ + Logs that the command was destroyed (window closed). Currently, does not clean up + anything. + + :param args: adsk.core.CommandEventArgs: inputs. + :return: + """ global local_handlers local_handlers = [] futil.log(f"{CMD_NAME} Command Destroy Event") diff --git a/applications/cls-cad-fusion-plugin/commands/typecrawlingproject/entry.py b/applications/cls-cad-fusion-plugin/commands/typecrawlingproject/entry.py index 0d6125b..44f01b1 100644 --- a/applications/cls-cad-fusion-plugin/commands/typecrawlingproject/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/typecrawlingproject/entry.py @@ -26,10 +26,9 @@ def start(): """ Creates the promoted "Crawl Project" command in the CLS-CAD tab. - Registers the commandCreated handler. - - Returns: + Registers the commandCreated handler. + :return: """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER @@ -44,11 +43,11 @@ def start(): def stop(): """ - Removes this command from the CLS-CAD tab along with all others it shares a panel with. - This does not fail, even if the panel is emptied by multiple commands. - - Returns: + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + This does not fail, even if the panel is emptied by multiple commands. + :return: """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) @@ -64,14 +63,11 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): """ - Called when the user clicks the command in CLS-CAD tab. - Registers execute and destroy handlers. - - Args: - args: A CommandCreatedEventArgs that allows access to the commands properties and inputs. - - Returns: + Called when the user clicks the command in CLS-CAD tab. Registers execute and + destroy handlers. + :param args: adsk.core.CommandCreatedEventArgs: and inputs. + :return: """ futil.log(f"{CMD_NAME} Command Created Event") @@ -85,16 +81,14 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): def recursively_submit(folders: adsk.core.DataFolders): """ - For every passed DataFolder, all contained Files are opened that are f3d files. - The corresponding JSON for the backend is created and submitted if the file is valid. (Provides at least one format) - If the file is not valid, it is skipped. This may be changed to provide an error report in the future. - The file traversal is DFS, after a single folder is done, its children are processed immediately. - - Args: - folders: A list of DataFolders to process. - - Returns: - + For every passed DataFolder, all contained Files are opened that are f3d files. The + corresponding JSON for the backend is created and submitted if the file is valid. + (Provides at least one format) If the file is not valid, it is skipped. This may be + changed to provide an error report in the future. The file traversal is DFS, after a + single folder is done, its children are processed immediately. + + :param folders: adsk.core.DataFolders: The set of folders to recursively crawl. + :return: """ global progress_dialog for folder in wrapped_forge_call(folders.asArray, progress_dialog): @@ -108,6 +102,13 @@ def recursively_submit(folders: adsk.core.DataFolders): def submit_files_in_folder(folder): + """ + Open all files contained in a folder and submit them the backend by calling the + CheckAndSubmit command. + + :param folder: The folder in which to submit all files. + :return: + """ global progress_dialog folder_data_files = wrapped_forge_call(folder.dataFiles.asArray, progress_dialog) for file in folder_data_files: @@ -146,16 +147,13 @@ def submit_files_in_folder(folder): def command_execute(args: adsk.core.CommandEventArgs): """ - Executes immediately when user clicks the button in CLS-CAD tab as there are no command inputs. - Prompts the user to confirm beginning the lengthy crawling process. - Fetches the activeDocuments (priority, else active side browser position) projects root folder. - Recursively traverses this initial set of folders. - - Args: - args: - - Returns: + Executes immediately when user clicks the button in CLS-CAD tab as there are no + command inputs. Prompts the user to confirm beginning the lengthy crawling process. + Fetches the activeDocuments (priority, else active side browser position) projects + root folder. Recursively traverses this initial set of folders. + :param args: adsk.core.CommandEventArgs: + :return: """ futil.log(f"{CMD_NAME} Command Execute Event") @@ -189,13 +187,11 @@ def command_execute(args: adsk.core.CommandEventArgs): def command_destroy(args: adsk.core.CommandEventArgs): """ - Logs that the command was destroyed (window closed). Currently, does not clean up anything. - - Args: - args: A CommandEventArgs that allows access to the commands properties and inputs. - - Returns: + Logs that the command was destroyed (window closed). Currently, does not clean up + anything. + :param args: adsk.core.CommandEventArgs: inputs. + :return: """ global local_handlers local_handlers = [] diff --git a/applications/cls-cad-fusion-plugin/commands/uploadtaxonomy/entry.py b/applications/cls-cad-fusion-plugin/commands/uploadtaxonomy/entry.py index f6d2800..de3e1cb 100644 --- a/applications/cls-cad-fusion-plugin/commands/uploadtaxonomy/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/uploadtaxonomy/entry.py @@ -26,6 +26,12 @@ def start(): + """ + Creates the promoted "Upload Taxonomy" command in the CLS-CAD tab. + + Registers the commandCreated handler. + :return: + """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER ) @@ -38,6 +44,13 @@ def start(): def stop(): + """ + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + + This does not fail, even if the panel is emptied by multiple commands. + :return: + """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) command_definition = ui.commandDefinitions.itemById(CMD_ID) @@ -51,27 +64,33 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): + """ + Called when the user clicks the command in CLS-CAD tab. Registers execute and + destroy handlers. + + :param args: adsk.core.CommandCreatedEventArgs: and inputs. + :return: + """ futil.log(f"{CMD_NAME} Command Created Event") futil.add_handler( args.command.execute, command_execute, local_handlers=local_handlers ) - futil.add_handler( - args.command.inputChanged, command_input_changed, local_handlers=local_handlers - ) - futil.add_handler( - args.command.executePreview, command_preview, local_handlers=local_handlers - ) futil.add_handler( args.command.destroy, command_destroy, local_handlers=local_handlers ) -def command_execute_preview(args: adsk.core.CommandEventHandler): - return - - def command_execute(args: adsk.core.CommandEventArgs): + """ + Executes immediately when user clicks the button in CLS-CAD tab as there are no + command inputs. Prompts the user to select a taxonomy from their filesystem. Then, + attempts to load the taxonomy as the current active project taxonomy, as well as + uploading and associating it with the active project in the backend database. + + :param args: adsk.core.CommandEventArgs: + :return: + """ futil.log(f"{CMD_NAME} Command Execute Event") file_dlg = ui.createFileDialog() file_dlg.isMultiSelectEnabled = False @@ -90,18 +109,14 @@ def command_execute(args: adsk.core.CommandEventArgs): return -def command_preview(args: adsk.core.CommandEventArgs): - futil.log(f"{CMD_NAME} Command Preview Event") - - -def command_input_changed(args: adsk.core.InputChangedEventArgs): - changed_input = args.input - futil.log( - f"{CMD_NAME} Input Changed Event fired from a change to {changed_input.id}" - ) - - def command_destroy(args: adsk.core.CommandEventArgs): + """ + Logs that the command was destroyed (window closed). Currently, does not clean up + anything. + + :param args: adsk.core.CommandEventArgs: inputs. + :return: + """ global local_handlers local_handlers = [] futil.log(f"{CMD_NAME} Command Destroy Event") diff --git a/applications/cls-cad-fusion-plugin/commands/uuidupdatecrawlingproject/entry.py b/applications/cls-cad-fusion-plugin/commands/uuidupdatecrawlingproject/entry.py index b5e8880..99c922f 100644 --- a/applications/cls-cad-fusion-plugin/commands/uuidupdatecrawlingproject/entry.py +++ b/applications/cls-cad-fusion-plugin/commands/uuidupdatecrawlingproject/entry.py @@ -26,10 +26,9 @@ def start(): """ Creates the promoted "Migrate UUIDs" command in the CLS-CAD tab. - Registers the commandCreated handler. - - Returns: + Registers the commandCreated handler. + :return: """ cmd_def = ui.commandDefinitions.addButtonDefinition( CMD_ID, CMD_NAME, CMD_DESCRIPTION, ICON_FOLDER @@ -44,11 +43,11 @@ def start(): def stop(): """ - Removes this command from the CLS-CAD tab along with all others it shares a panel with. - This does not fail, even if the panel is emptied by multiple commands. - - Returns: + Removes this command from the CLS-CAD tab along with all others it shares a panel + with. + This does not fail, even if the panel is emptied by multiple commands. + :return: """ workspace = ui.workspaces.itemById(WORKSPACE_ID) panel = workspace.toolbarPanels.itemById(PANEL_ID) @@ -64,14 +63,11 @@ def stop(): def command_created(args: adsk.core.CommandCreatedEventArgs): """ - Called when the user clicks the command in CLS-CAD tab. - Registers execute and destroy handlers. - - Args: - args: A CommandCreatedEventArgs that allows access to the commands properties and inputs. - - Returns: + Called when the user clicks the command in CLS-CAD tab. Registers execute and + destroy handlers. + :param args: adsk.core.CommandCreatedEventArgs: and inputs. + :return: """ futil.log(f"{CMD_NAME} Command Created Event") @@ -85,16 +81,14 @@ def command_created(args: adsk.core.CommandCreatedEventArgs): def recursively_submit(folders: adsk.core.DataFolders): """ - For every passed DataFolder, all contained Files are opened that are f3d files. - The corresponding JSON for the backend is created and submitted if the file is valid. (Provides at least one format) - If the file is not valid, it is skipped. This may be changed to provide an error report in the future. - The file traversal is DFS, after a single folder is done, its children are processed immediately. - - Args: - folders: A list of DataFolders to process. - - Returns: - + For every passed DataFolder, all contained Files are opened that are f3d files. The + corresponding JSON for the backend is created and submitted if the file is valid. + (Provides at least one format) If the file is not valid, it is skipped. This may be + changed to provide an error report in the future. The file traversal is DFS, after a + single folder is done, its children are processed immediately. + + :param folders: adsk.core.DataFolders: + :return: """ global progress_dialog for folder in wrapped_forge_call(folders.asArray, progress_dialog): @@ -107,6 +101,14 @@ def recursively_submit(folders: adsk.core.DataFolders): def submit_and_update_files_in_folder(folder): + """ + Open all files contained in a folder and submit them the backend by calling the + CheckAndSubmit command. + + Before submitting, remove all nested subcomponents and flatten the assembly tree. + :param folder: The folder in which to submit all files. + :return: + """ global progress_dialog for file in wrapped_forge_call(folder.dataFiles.asArray, progress_dialog): if not file.fileExtension == "f3d": @@ -176,16 +178,13 @@ def submit_and_update_files_in_folder(folder): def command_execute(args: adsk.core.CommandEventArgs): """ - Executes immediately when user clicks the button in CLS-CAD tab as there are no command inputs. - Prompts the user to confirm beginning the lengthy crawling process. - Fetches the activeDocuments (priority, else active side browser position) projects root folder. - Recursively traverses this initial set of folders. - - Args: - args: - - Returns: + Executes immediately when user clicks the button in CLS-CAD tab as there are no + command inputs. Prompts the user to confirm beginning the lengthy crawling process. + Fetches the activeDocuments (priority, else active side browser position) projects + root folder. Recursively traverses this initial set of folders. + :param args: adsk.core.CommandEventArgs: + :return: """ futil.log(f"{CMD_NAME} Command Execute Event") @@ -223,13 +222,11 @@ def command_execute(args: adsk.core.CommandEventArgs): def command_destroy(args: adsk.core.CommandEventArgs): """ - Logs that the command was destroyed (window closed). Currently, does not clean up anything. - - Args: - args: A CommandEventArgs that allows access to the commands properties and inputs. - - Returns: + Logs that the command was destroyed (window closed). Currently, does not clean up + anything. + :param args: adsk.core.CommandEventArgs: inputs. + :return: """ global local_handlers local_handlers = [] diff --git a/applications/cls-cad-fusion-plugin/lib/cls_python_compat/types.py b/applications/cls-cad-fusion-plugin/lib/cls_python_compat/types.py index 18755a5..499cbd2 100644 --- a/applications/cls-cad-fusion-plugin/lib/cls_python_compat/types.py +++ b/applications/cls-cad-fusion-plugin/lib/cls_python_compat/types.py @@ -5,8 +5,13 @@ @dataclass(frozen=True) class Type(ABC): + """""" + @staticmethod def intersect(types: Sequence["Type"]) -> "Type": + """ + :param types: Sequence["Type"]: + """ if len(types) > 0: rtypes = reversed(types) result: "Type" = next(rtypes) @@ -19,28 +24,36 @@ def intersect(types: Sequence["Type"]) -> "Type": @dataclass(frozen=True) class Omega(Type): - pass + """""" @dataclass(frozen=True) class Constructor(Type): + """""" + name: object = field(init=True) arg: Type = field(default=Omega(), init=True) @dataclass(frozen=True) class Product(Type): + """""" + left: Type = field(init=True) right: Type = field(init=True) @dataclass(frozen=True) class Arrow(Type): + """""" + source: Type = field(init=True) target: Type = field(init=True) @dataclass(frozen=True) class Intersection(Type): + """""" + left: Type = field(init=True) right: Type = field(init=True) diff --git a/applications/cls-cad-fusion-plugin/lib/fusion360utils/event_utils.py b/applications/cls-cad-fusion-plugin/lib/fusion360utils/event_utils.py index 08ea78f..5569055 100644 --- a/applications/cls-cad-fusion-plugin/lib/fusion360utils/event_utils.py +++ b/applications/cls-cad-fusion-plugin/lib/fusion360utils/event_utils.py @@ -16,21 +16,50 @@ def add_handler( name: str = None, local_handlers: list = None ): - """Adds an event handler to the specified event. - - Arguments: - event -- The event object you want to connect a handler to. - callback -- The function that will handle the event. - name -- A name to use in logging errors associated with this event. - Otherwise the name of the event object is used. This argument - must be specified by its keyword. - local_handlers -- A list of handlers you manage that is used to maintain - a reference to the handlers so they aren't released. - This argument must be specified by its keyword. If not - specified the handler is added to a global list and can - be cleared using the clear_handlers function. You may want - to maintain your own handler list so it can be managed - independently for each command. + """ + Adds an event handler to the specified event. + + Args: + event: The event object you want to connect a handler to + callback: The function that will handle the event + name: A name to use in logging errors associated with this event + Otherwise: the name of the event object is used + must: be specified by its keyword + local_handlers: A list of handlers you manage that is used to maintain + a: reference to the handlers so they aren + This: argument must be specified by its keyword + specified: the handler is added to a global list and can + be: cleared using the clear_handlers function + to: maintain your own handler list so it can be managed + independently: for each command + event: adsk.core.Event: + callback: Callable: :param *: + name: str: (Default value = None) + local_handlers: list: (Default value = None) + event: adsk.core.Event: + callback: Callable: + *: + name: str: (Default value = None) + local_handlers: list: (Default value = None) + + Returns: + + Args: + event: adsk.core.Event: + callback: Callable: + event: adsk.core.Event: + callback: Callable: + *: + name: str: (Default value = None) + local_handlers: list: (Default value = None) + + Returns: + + :param event: adsk.core.Event: + :param callback: Callable: + :param *: + :param name: str: (Default value = None) + :param local_handlers: list: (Default value = None) """ module = sys.modules[event.__module__] handler_type = module.__dict__[event.add.__annotations__["handler"]] @@ -51,19 +80,38 @@ def _create_handler( name: str = None, local_handlers: list = None, ): + """ + :param handler_type: param callback: Callable: + :param event: adsk.core.Event: + :param name: str: (Default value = None) + :param local_handlers: list: (Default value = None) + :param callback: Callable: + """ handler = _define_handler(handler_type, callback, name)() (local_handlers or _handlers).append(handler) return handler def _define_handler(handler_type, callback, name: str = None): + """ + :param handler_type: param callback: + :param name: str: (Default value = None) + :param callback: param name: str: (Default value = None) + """ name = name or handler_type.__name__ class Handler(handler_type): + """""" + def __init__(self): super().__init__() def notify(self, args): + """ + + :param args: + + """ try: callback(args) except: diff --git a/applications/cls-cad-fusion-plugin/lib/fusion360utils/general_utils.py b/applications/cls-cad-fusion-plugin/lib/fusion360utils/general_utils.py index ed16287..4121fe6 100644 --- a/applications/cls-cad-fusion-plugin/lib/fusion360utils/general_utils.py +++ b/applications/cls-cad-fusion-plugin/lib/fusion360utils/general_utils.py @@ -19,12 +19,14 @@ def log( level: adsk.core.LogLevels = adsk.core.LogLevels.InfoLogLevel, force_console: bool = False, ): - """Utility function to easily handle logging in your app. + """ + Utility function to easily handle logging in your app. - Arguments: - message -- The message to log. - level -- The logging severity level. - force_console -- Forces the message to be written to the Text Command window. + :param message: str: + :param level: adsk.core.LogLevels: (Default value = + adsk.core.LogLevels.InfoLogLevel) + :param force_console: bool: (Default value = False) + adsk.core.LogLevels.InfoLogLevel) """ # Always print to console, only seen through IDE. print(message) @@ -41,13 +43,13 @@ def log( def handle_error(name: str, show_message_box: bool = False): - """Utility function to simplify error handling. + """ + Utility function to simplify error handling. - Arguments: - name -- A name used to label the error. - show_message_box -- Indicates if the error should be shown in the message box. - If False, it will only be shown in the Text Command window - and logged to the log file. + :param name: str: + :param show_message_box: bool: (Default value = False) + :param If: False + :param and: logged to the log file """ log("===== Error =====", adsk.core.LogLevels.ErrorLogLevel) diff --git a/applications/cls-cad-fusion-plugin/lib/general_utils/hrid.py b/applications/cls-cad-fusion-plugin/lib/general_utils/hrid.py index 8974064..8ac7e50 100644 --- a/applications/cls-cad-fusion-plugin/lib/general_utils/hrid.py +++ b/applications/cls-cad-fusion-plugin/lib/general_utils/hrid.py @@ -11,11 +11,29 @@ def generate_id(separator="-", seed: Hashable = None, word_count=6) -> str: """ - Generate a human readable ID - :param separator: The string to use to separate words - :param seed: The seed to use. The same seed will produce the same ID - :param word_count: The number of words to use. Minimum of 3. - :return: A human readable ID + Generate a human readable ID :param separator: The string to use to separate words. + + Args: + seed: The seed to use. The same seed will produce the same ID + word_count: The number of words to use. Minimum of 3. (Default value = 6) + separator: Default value = "-") + seed: Hashable: (Default value = None) + seed: Hashable: (Default value = None) + + Returns: + A human readable ID + + Args: + separator: Default value = "-") + seed: Hashable: (Default value = None) + word_count: Default value = 6) + seed: Hashable: (Default value = None) + + Returns: + + :param separator: (Default value = "-") + :param seed: Hashable: (Default value = None) + :param word_count: (Default value = 6) """ if word_count < 3: raise ValueError("word_count cannot be lower than 3") diff --git a/applications/cls-cad-fusion-plugin/lib/general_utils/util.py b/applications/cls-cad-fusion-plugin/lib/general_utils/util.py index 47f01a1..2e13268 100644 --- a/applications/cls-cad-fusion-plugin/lib/general_utils/util.py +++ b/applications/cls-cad-fusion-plugin/lib/general_utils/util.py @@ -17,14 +17,15 @@ def wrapped_forge_call(forge_call: Callable, progress_dialog=None): """ - Attempts to get array from forge object until it succeeds. - What can I say, for some reason that part of the API is super flaky. - - Args: - forge_call: - - Returns: - + Attempts to get array from forge object until it succeeds. What can I say, for some + reason that part of the API is super flaky. + + :param forge_call: param forge_call: Callable: + :param progress_dialog: Default value = None) + :param forge_call: Callable: + :param forge_call: Callable: + :param forge_call: Callable: + :param forge_call: Callable: """ success = False once = True @@ -57,6 +58,9 @@ def wrapped_forge_call(forge_call: Callable, progress_dialog=None): def invert_sub_taxonomy(sub_taxonomy): + """ + :param sub_taxonomy: + """ subtypes = defaultdict(list) for key, values in sub_taxonomy.items(): for value in values: @@ -65,6 +69,7 @@ def invert_sub_taxonomy(sub_taxonomy): def load_project_taxonomy_to_config(): + """""" app = adsk.core.Application.get() active_id = ( app.activeDocument.dataFile.parentProject.id @@ -84,6 +89,10 @@ def load_project_taxonomy_to_config(): def winapi_path(dos_path, encoding=None): + """ + :param dos_path: param encoding: (Default value = None) + :param encoding: Default value = None) + """ if not isinstance(dos_path, str) and encoding is not None: dos_path = dos_path.decode(encoding) path = os.path.abspath(dos_path) @@ -93,6 +102,7 @@ def winapi_path(dos_path, encoding=None): def update_taxonomy_in_backend(): + """""" payload_dict = create_backend_taxonomy() req = urllib.request.Request("http://127.0.0.1:8000/submit/taxonomy") req.add_header("Content-Type", "application/json; charset=utf-8") @@ -108,11 +118,11 @@ def update_taxonomy_in_backend(): def create_backend_taxonomy(): """ - Creates a taxonomy in the format that the backend expects. Each individual taxonomy is suffixed with its identifier, - guaranteeing that namespaces don't overlap. The Forge ProjectID is also added to the JSON. + Creates a taxonomy in the format that the backend expects. Each individual taxonomy + is suffixed with its identifier, guaranteeing that namespaces don't overlap. The + Forge ProjectID is also added to the JSON. Returns: The created JSON/dict. - """ suffixed_taxonomy = {} update_map_for_taxonomies(config.taxonomies, config.mappings) @@ -132,6 +142,10 @@ def create_backend_taxonomy(): def apply_map_to_taxonomy(taxonomies: dict, mapping: dict): + """ + :param taxonomies: dict: + :param mapping: dict: + """ resolved_taxonomies = defaultdict(dict) for name, taxonomy in taxonomies.items(): for k, v in taxonomy.items(): @@ -140,6 +154,10 @@ def apply_map_to_taxonomy(taxonomies: dict, mapping: dict): def update_map_for_taxonomies(taxonomies: dict, mapping=defaultdict(dict)): + """ + :param taxonomies: dict: + :param mapping: Default value = defaultdict(dict)) + """ for name, taxonomy in taxonomies.items(): for k, v in taxonomy.items(): if k not in mapping[name]: @@ -151,6 +169,9 @@ def update_map_for_taxonomies(taxonomies: dict, mapping=defaultdict(dict)): def invert_map(mapping: dict): + """ + :param mapping: dict: + """ inverted_mapping = defaultdict(dict) for name, s_map in mapping.items(): inverted_mapping[name] = {v: k for k, v in s_map.items()}