diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index e5524a2e45..4feb77222a 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -5,6 +5,7 @@ import urllib.parse from json import JSONDecodeError from time import time from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any, cast +from pathlib import Path from PyQt6.QtCore import QUrl from PyQt6.QtNetwork import QNetworkRequest, QNetworkReply @@ -38,14 +39,17 @@ class CloudApiClient: # The cloud URL to use for this remote cluster. ROOT_PATH = UltimakerCloudConstants.CuraCloudAPIRoot - CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH) - CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH) + CLUSTER_API_ROOT = f"{ROOT_PATH}/connect/v1" + CURA_API_ROOT = f"{ROOT_PATH}/cura/v1" DEFAULT_REQUEST_TIMEOUT = 10 # seconds # In order to avoid garbage collection we keep the callbacks in this list. _anti_gc_callbacks = [] # type: List[Callable[[Any], None]] + # Custom machine definition ID to cloud cluster name mapping + _machine_id_to_name: Dict[str, str] = None + def __init__(self, app: CuraApplication, on_error: Callable[[List[CloudError]], None]) -> None: """Initializes a new cloud API client. @@ -73,10 +77,10 @@ class CloudApiClient: url = f"{self.CLUSTER_API_ROOT}/clusters?status=active" self._http.get(url, - scope = self._scope, - callback = self._parseCallback(on_finished, CloudClusterResponse, failed), - error_callback = failed, - timeout = self.DEFAULT_REQUEST_TIMEOUT) + scope=self._scope, + callback=self._parseCallback(on_finished, CloudClusterResponse, failed), + error_callback=failed, + timeout=self.DEFAULT_REQUEST_TIMEOUT) def getClustersByMachineType(self, machine_type, on_finished: Callable[[List[CloudClusterWithConfigResponse]], Any], failed: Callable) -> None: # HACK: There is something weird going on with the API, as it reports printer types in formats like @@ -84,13 +88,9 @@ class CloudApiClient: # conversion! # API points to "MakerBot Method" for a makerbot printertypes which we already changed to allign with other printer_type - method_x = { - "ultimaker_method":"MakerBot Method", - "ultimaker_methodx":"MakerBot Method X", - "ultimaker_methodxl":"MakerBot Method XL" - } - if machine_type in method_x: - machine_type = method_x[machine_type] + machine_id_to_name = self.getMachineIDMap() + if machine_type in machine_id_to_name: + machine_type = machine_id_to_name[machine_type] else: machine_type = machine_type.replace("_plus", "+") machine_type = machine_type.replace("_", " ") @@ -114,9 +114,9 @@ class CloudApiClient: url = f"{self.CLUSTER_API_ROOT}/clusters/{cluster_id}/status" self._http.get(url, - scope = self._scope, - callback = self._parseCallback(on_finished, CloudClusterStatus), - timeout = self.DEFAULT_REQUEST_TIMEOUT) + scope=self._scope, + callback=self._parseCallback(on_finished, CloudClusterStatus), + timeout=self.DEFAULT_REQUEST_TIMEOUT) def requestUpload(self, request: CloudPrintJobUploadRequest, on_finished: Callable[[CloudPrintJobResponse], Any]) -> None: @@ -131,10 +131,10 @@ class CloudApiClient: data = json.dumps({"data": request.toDict()}).encode() self._http.put(url, - scope = self._scope, - data = data, - callback = self._parseCallback(on_finished, CloudPrintJobResponse), - timeout = self.DEFAULT_REQUEST_TIMEOUT) + scope=self._scope, + data=data, + callback=self._parseCallback(on_finished, CloudPrintJobResponse), + timeout=self.DEFAULT_REQUEST_TIMEOUT) def uploadToolPath(self, print_job: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[], Any], on_progress: Callable[[int], Any], on_error: Callable[[], Any]): @@ -160,11 +160,11 @@ class CloudApiClient: def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], Any], on_error) -> None: url = f"{self.CLUSTER_API_ROOT}/clusters/{cluster_id}/print/{job_id}" self._http.post(url, - scope = self._scope, - data = b"", - callback = self._parseCallback(on_finished, CloudPrintResponse), - error_callback = on_error, - timeout = self.DEFAULT_REQUEST_TIMEOUT) + scope=self._scope, + data=b"", + callback=self._parseCallback(on_finished, CloudPrintResponse), + error_callback=on_error, + timeout=self.DEFAULT_REQUEST_TIMEOUT) def doPrintJobAction(self, cluster_id: str, cluster_job_id: str, action: str, data: Optional[Dict[str, Any]] = None) -> None: @@ -174,14 +174,15 @@ class CloudApiClient: :param cluster_id: The ID of the cluster. :param cluster_job_id: The ID of the print job within the cluster. :param action: The name of the action to execute. + :param data: Optional data to send with the POST request """ body = json.dumps({"data": data}).encode() if data else b"" url = f"{self.CLUSTER_API_ROOT}/clusters/{cluster_id}/print_jobs/{cluster_job_id}/action/{action}" self._http.post(url, - scope = self._scope, - data = body, - timeout = self.DEFAULT_REQUEST_TIMEOUT) + scope=self._scope, + data=body, + timeout=self.DEFAULT_REQUEST_TIMEOUT) def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest: """We override _createEmptyRequest in order to add the user credentials. @@ -216,8 +217,11 @@ class CloudApiClient: Logger.logException("e", "Could not parse the stardust response: %s", error.toDict()) return status_code, {"errors": [error.toDict()]} - def _parseResponse(self, response: Dict[str, Any], on_finished: Union[Callable[[CloudApiClientModel], Any], - Callable[[List[CloudApiClientModel]], Any]], model_class: Type[CloudApiClientModel]) -> None: + def _parseResponse(self, + response: Dict[str, Any], + on_finished: Union[Callable[[CloudApiClientModel], Any], + Callable[[List[CloudApiClientModel]], Any]], + model_class: Type[CloudApiClientModel]) -> None: """Parses the given response and calls the correct callback depending on the result. :param response: The response from the server, after being converted to a dict. @@ -276,3 +280,14 @@ class CloudApiClient: self._anti_gc_callbacks.append(parse) return parse + + @classmethod + def getMachineIDMap(cls) -> Dict[str, str]: + if cls._machine_id_to_name is None: + try: + with open(Path(__file__).parent / "machine_id_to_name.json", "rt") as f: + cls._machine_id_to_name = json.load(f) + except Exception as e: + Logger.logException("e", f"Could not load machine_id_to_name.json: '{e}'") + cls._machine_id_to_name = {} + return cls._machine_id_to_name diff --git a/plugins/UM3NetworkPrinting/src/Cloud/machine_id_to_name.json b/plugins/UM3NetworkPrinting/src/Cloud/machine_id_to_name.json new file mode 100644 index 0000000000..ccd76ab34b --- /dev/null +++ b/plugins/UM3NetworkPrinting/src/Cloud/machine_id_to_name.json @@ -0,0 +1,5 @@ +{ + "ultimaker_method": "MakerBot Method", + "ultimaker_methodx": "MakerBot Method X", + "ultimaker_methodxl": "MakerBot Method XL" +}