Skip to content

Commit

Permalink
Add source code documentation to entirety of the add-in
Browse files Browse the repository at this point in the history
  • Loading branch information
Jekannadar committed Nov 22, 2023
1 parent 9b4400d commit 5ff59d8
Show file tree
Hide file tree
Showing 32 changed files with 1,093 additions and 580 deletions.
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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:
"""
Expand Down Expand Up @@ -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:
"""
Expand Down
70 changes: 44 additions & 26 deletions applications/cls-cad-backend/cls_cad_backend/repository_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -48,15 +49,17 @@ 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.
"""
self.info = info

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.
Expand All @@ -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 = [
Expand All @@ -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 (
Expand All @@ -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(
Expand All @@ -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.
Expand All @@ -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)
Expand All @@ -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.
Expand All @@ -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.
Expand Down Expand Up @@ -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"]:
Expand Down Expand Up @@ -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):
Expand Down
4 changes: 2 additions & 2 deletions applications/cls-cad-backend/cls_cad_backend/responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
65 changes: 40 additions & 25 deletions applications/cls-cad-backend/cls_cad_backend/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand All @@ -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"
Expand All @@ -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 = {}
Expand Down Expand Up @@ -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))

Expand All @@ -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.
Expand All @@ -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)
Expand Down Expand Up @@ -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 []
Expand All @@ -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)
Expand Down
7 changes: 4 additions & 3 deletions applications/cls-cad-backend/cls_cad_backend/util/hrid.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
"""
Expand Down
Loading

0 comments on commit 5ff59d8

Please sign in to comment.