refactor
Browse files- onshape/README.md +58 -0
- onshape/onshape_base.py +21 -0
- onshape/onshape_download.py +38 -32
- onshape/onshape_schema.py +39 -0
- onshape/onshape_translation.py +87 -26
onshape/README.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Onshape API Utilities
|
| 2 |
+
|
| 3 |
+
This module provides utilities for interacting with the Onshape API, including uploading and translating files and downloading translated files.
|
| 4 |
+
|
| 5 |
+
## Prerequisites
|
| 6 |
+
|
| 7 |
+
1. Set the following environment variables:
|
| 8 |
+
- `ONSHAPE_ACCESS_KEY`
|
| 9 |
+
- `ONSHAPE_SECRET_KEY`
|
| 10 |
+
|
| 11 |
+
2. Install required Python packages:
|
| 12 |
+
```bash
|
| 13 |
+
pip install requests loguru
|
| 14 |
+
```
|
| 15 |
+
|
| 16 |
+
## Modules
|
| 17 |
+
|
| 18 |
+
### `onshape_base.py`
|
| 19 |
+
|
| 20 |
+
Provides the `OnshapeBase` class for shared functionality like authentication and base URL construction.
|
| 21 |
+
|
| 22 |
+
### `onshape_translation.py`
|
| 23 |
+
|
| 24 |
+
Handles file upload and translation. Supports any file type and export format.
|
| 25 |
+
|
| 26 |
+
#### Example Usage:
|
| 27 |
+
```python
|
| 28 |
+
from onshape_translation import OnshapeTranslation
|
| 29 |
+
|
| 30 |
+
did = "your_document_id"
|
| 31 |
+
wid = "your_workspace_id"
|
| 32 |
+
file_path = "path_to_your_file"
|
| 33 |
+
format_name = "desired_format" # e.g., "STEP", "IGES", etc.
|
| 34 |
+
|
| 35 |
+
translator = OnshapeTranslation(did, wid, file_path, format_name)
|
| 36 |
+
translator.upload_and_translate()
|
| 37 |
+
```
|
| 38 |
+
|
| 39 |
+
### `onshape_download.py`
|
| 40 |
+
|
| 41 |
+
Handles downloading translated files.
|
| 42 |
+
|
| 43 |
+
#### Example Usage:
|
| 44 |
+
```python
|
| 45 |
+
from onshape_download import OnshapeDownload
|
| 46 |
+
|
| 47 |
+
did = "your_document_id"
|
| 48 |
+
wid = "your_workspace_id"
|
| 49 |
+
eid = "your_element_id" # you can find it in `resultElementIds` when `requestState` of `TranslationStatusResponse` is `DONE`
|
| 50 |
+
output_file = "output_file_name"
|
| 51 |
+
|
| 52 |
+
downloader = OnshapeDownload(did, wid, eid, output_file)
|
| 53 |
+
downloader.download()
|
| 54 |
+
```
|
| 55 |
+
|
| 56 |
+
## Logging
|
| 57 |
+
|
| 58 |
+
This module uses `loguru` for logging. Logs will display detailed information about the operations performed.
|
onshape/onshape_base.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
import base64
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
class OnshapeBase:
|
| 6 |
+
def __init__(self):
|
| 7 |
+
self.access_key = os.environ.get("ONSHAPE_ACCESS_KEY")
|
| 8 |
+
self.secret_key = os.environ.get("ONSHAPE_SECRET_KEY")
|
| 9 |
+
self.base_url = "https://cad.onshape.com/api/v10"
|
| 10 |
+
|
| 11 |
+
if not self.access_key or not self.secret_key:
|
| 12 |
+
raise ValueError(
|
| 13 |
+
"ONSHAPE_ACCESS_KEY and ONSHAPE_SECRET_KEY must be set in environment variables."
|
| 14 |
+
)
|
| 15 |
+
|
| 16 |
+
def get_auth_header(self):
|
| 17 |
+
credentials_raw = f"{self.access_key}:{self.secret_key}"
|
| 18 |
+
credentials_base64 = base64.b64encode(credentials_raw.encode("utf-8")).decode(
|
| 19 |
+
"utf-8"
|
| 20 |
+
)
|
| 21 |
+
return {"Authorization": f"Basic {credentials_base64}"}
|
onshape/onshape_download.py
CHANGED
|
@@ -1,33 +1,39 @@
|
|
| 1 |
-
import os
|
| 2 |
import requests
|
| 3 |
-
import
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
wid
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
import requests
|
| 2 |
+
from loguru import logger
|
| 3 |
+
from onshape_base import OnshapeBase
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class OnshapeDownload(OnshapeBase):
|
| 7 |
+
def __init__(self, did, wid, eid, output_file):
|
| 8 |
+
super().__init__()
|
| 9 |
+
self.did = did
|
| 10 |
+
self.wid = wid
|
| 11 |
+
self.eid = eid
|
| 12 |
+
self.output_file = output_file
|
| 13 |
+
|
| 14 |
+
def download(self):
|
| 15 |
+
url = f"{self.base_url}/blobelements/d/{self.did}/w/{self.wid}/e/{self.eid}"
|
| 16 |
+
headers = {
|
| 17 |
+
"Accept": "application/json;charset=UTF-8; qs=0.09",
|
| 18 |
+
**self.get_auth_header(),
|
| 19 |
+
}
|
| 20 |
+
|
| 21 |
+
logger.info(
|
| 22 |
+
f"Downloading file with element ID '{self.eid}' to '{self.output_file}'."
|
| 23 |
+
)
|
| 24 |
+
response = requests.get(url, headers=headers)
|
| 25 |
+
|
| 26 |
+
with open(self.output_file, "wb") as f:
|
| 27 |
+
f.write(response.content)
|
| 28 |
+
logger.success(f"File downloaded successfully as '{self.output_file}'.")
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
# Example usage
|
| 32 |
+
if __name__ == "__main__":
|
| 33 |
+
did = "ef42d7639096f3e61a4d4f07"
|
| 34 |
+
wid = "5fcd0f25ce3dee08bbb823bf"
|
| 35 |
+
eid = "fd0dbebc1ac05de1f1d1ed07" # you can find it in `resultElementIds` when `requestState` of `TranslationStatusResponse` is `DONE`
|
| 36 |
+
output_file = "output_file.step"
|
| 37 |
+
|
| 38 |
+
downloader = OnshapeDownload(did, wid, eid, output_file)
|
| 39 |
+
downloader.download()
|
onshape/onshape_schema.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from pydantic import BaseModel, Field
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
class TranslationResponse(BaseModel):
|
| 5 |
+
document_id: str = Field(alias="documentId")
|
| 6 |
+
failure_reason: str | None = Field(alias="failureReason", default=None)
|
| 7 |
+
href: str
|
| 8 |
+
id: str
|
| 9 |
+
name: str
|
| 10 |
+
request_element_id: str = Field(alias="requestElementId")
|
| 11 |
+
request_state: str = Field(alias="requestState")
|
| 12 |
+
result_document_id: str = Field(alias="resultDocumentId")
|
| 13 |
+
result_element_ids: list[str] | None = Field(alias="resultElementIds", default=None)
|
| 14 |
+
result_external_data_ids: list[str] | None = Field(
|
| 15 |
+
alias="resultExternalDataIds", default=None
|
| 16 |
+
)
|
| 17 |
+
result_workspace_id: str | None = Field(alias="resultWorkspaceId", default=None)
|
| 18 |
+
version_id: str | None = Field(alias="versionId", default=None)
|
| 19 |
+
view_ref: str | None = Field(alias="viewRef", default=None)
|
| 20 |
+
workspace_id: str = Field(alias="workspaceId")
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class TranslationStatusResponse(BaseModel):
|
| 24 |
+
id: str
|
| 25 |
+
request_state: str = Field(alias="requestState")
|
| 26 |
+
request_element_id: str = Field(alias="requestElementId")
|
| 27 |
+
result_external_data_ids: list[str] | None = Field(
|
| 28 |
+
alias="resultExternalDataIds", default=None
|
| 29 |
+
)
|
| 30 |
+
export_rule_file_name: str | None = Field(alias="exportRuleFileName", default=None)
|
| 31 |
+
version_id: str | None = Field(alias="versionId", default=None)
|
| 32 |
+
workspace_id: str = Field(alias="workspaceId")
|
| 33 |
+
document_id: str = Field(alias="documentId")
|
| 34 |
+
result_element_ids: list[str] | None = Field(alias="resultElementIds", default=None)
|
| 35 |
+
result_document_id: str = Field(alias="resultDocumentId")
|
| 36 |
+
failure_reason: str | None = Field(alias="failureReason", default=None)
|
| 37 |
+
result_workspace_id: str | None = Field(alias="resultWorkspaceId", default=None)
|
| 38 |
+
name: str
|
| 39 |
+
href: str
|
onshape/onshape_translation.py
CHANGED
|
@@ -1,35 +1,96 @@
|
|
| 1 |
-
import
|
|
|
|
| 2 |
import requests
|
| 3 |
-
import
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
prt_file_path = "prt0002.prt"
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
"Authorization": f"Basic {credentials_base64}",
|
| 20 |
-
# "X-XSRF-TOKEN": "QcscTFRgL6vE7h7UFHKmng==",
|
| 21 |
-
# DO NOT set Content-Type manually when sending files with requests
|
| 22 |
-
}
|
| 23 |
|
| 24 |
-
|
| 25 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
-
files = {"file": open(prt_file_path, "rb")}
|
| 28 |
|
| 29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import time
|
| 2 |
+
|
| 3 |
import requests
|
| 4 |
+
from loguru import logger
|
| 5 |
+
from onshape_base import OnshapeBase
|
| 6 |
+
from onshape_schema import TranslationResponse, TranslationStatusResponse
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class OnshapeTranslation(OnshapeBase):
|
| 10 |
+
def __init__(self, did: str, wid: str, file_path: str, format_name: str):
|
| 11 |
+
super().__init__()
|
| 12 |
+
self.did = did
|
| 13 |
+
self.wid = wid
|
| 14 |
+
self.file_path = file_path
|
| 15 |
+
self.format_name = format_name
|
| 16 |
+
|
| 17 |
+
def upload_and_translate(self):
|
| 18 |
+
url = f"{self.base_url}/translations/d/{self.did}/w/{self.wid}"
|
| 19 |
+
headers = {
|
| 20 |
+
"Accept": "application/json;charset=UTF-8; qs=0.09",
|
| 21 |
+
**self.get_auth_header(),
|
| 22 |
+
}
|
| 23 |
+
data = {"translate": "true", "formatName": self.format_name}
|
| 24 |
+
files = {"file": open(self.file_path, "rb")}
|
| 25 |
|
| 26 |
+
logger.info(
|
| 27 |
+
f"Uploading file '{self.file_path}' for translation to '{self.format_name}' format."
|
| 28 |
+
)
|
| 29 |
+
response = requests.post(url, headers=headers, data=data, files=files)
|
|
|
|
| 30 |
|
| 31 |
+
try:
|
| 32 |
+
translate_response = response.json()
|
| 33 |
+
if response.status_code != 200:
|
| 34 |
+
logger.error(
|
| 35 |
+
f"Failed to upload file. Status code: {response.status_code}"
|
| 36 |
+
)
|
| 37 |
+
logger.error(response.text)
|
| 38 |
+
raise Exception(
|
| 39 |
+
f"Failed to upload file. Status code: {response.status_code}"
|
| 40 |
+
)
|
| 41 |
+
logger.success(f"Translate response: {translate_response}")
|
| 42 |
+
return TranslationResponse(**translate_response)
|
| 43 |
+
except Exception:
|
| 44 |
+
logger.error(f"Status: {response.status_code}")
|
| 45 |
+
logger.error(response.text)
|
| 46 |
+
raise Exception(
|
| 47 |
+
f"Failed to upload and translate file. Status code: {response.status_code}"
|
| 48 |
+
)
|
| 49 |
|
| 50 |
+
def get_translation_status(self, translation_id: str):
|
| 51 |
+
url = f"{self.base_url}/translations/{translation_id}"
|
| 52 |
+
headers = {
|
| 53 |
+
"Accept": "application/json;charset=UTF-8; qs=0.09",
|
| 54 |
+
**self.get_auth_header(),
|
| 55 |
+
}
|
| 56 |
|
| 57 |
+
logger.info(f"Checking translation status for ID '{translation_id}'.")
|
| 58 |
+
response = requests.get(url, headers=headers)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 59 |
|
| 60 |
+
try:
|
| 61 |
+
status = response.json()
|
| 62 |
+
if response.status_code != 200:
|
| 63 |
+
logger.error(
|
| 64 |
+
f"Failed to get translation status. Status code: {response.status_code}"
|
| 65 |
+
)
|
| 66 |
+
logger.error(response.text)
|
| 67 |
+
raise Exception(
|
| 68 |
+
f"Failed to get translation status. Status code: {response.status_code}"
|
| 69 |
+
)
|
| 70 |
+
logger.success(f"Translation status: {status}")
|
| 71 |
+
return TranslationStatusResponse(**status)
|
| 72 |
+
except Exception:
|
| 73 |
+
logger.error(f"Status: {response.status_code}")
|
| 74 |
+
logger.error(response.text)
|
| 75 |
+
raise Exception(
|
| 76 |
+
f"Failed to get translation status. Status code: {response.status_code}"
|
| 77 |
+
)
|
| 78 |
|
|
|
|
| 79 |
|
| 80 |
+
# Example usage
|
| 81 |
+
if __name__ == "__main__":
|
| 82 |
+
did = "ef42d7639096f3e61a4d4f07"
|
| 83 |
+
wid = "5fcd0f25ce3dee08bbb823bf"
|
| 84 |
+
file_path = "onshape/example/prt0002.prt"
|
| 85 |
+
format_name = "STEP"
|
| 86 |
|
| 87 |
+
translator = OnshapeTranslation(did, wid, file_path, format_name)
|
| 88 |
+
response = translator.upload_and_translate()
|
| 89 |
+
response = translator.get_translation_status(response.id)
|
| 90 |
+
while response.request_state not in ["DONE", "FAILED"]:
|
| 91 |
+
logger.info(
|
| 92 |
+
f"Waiting for translation to complete. Current state: {response.request_state}"
|
| 93 |
+
)
|
| 94 |
+
response = translator.get_translation_status(response.id)
|
| 95 |
+
time.sleep(3)
|
| 96 |
+
logger.success(f"Translation completed with state: {response.request_state}")
|