Merge pull request #18741 from Ultimaker/CURA-11786_cloud_plugin_cleanup

[CURA-11786] [PP-452] Clean up the CloudAPIClient
This commit is contained in:
HellAholic 2024-03-27 13:21:01 +01:00 committed by GitHub
commit f89d4ddb4c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 50 additions and 30 deletions

View File

@ -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

View File

@ -0,0 +1,5 @@
{
"ultimaker_method": "MakerBot Method",
"ultimaker_methodx": "MakerBot Method X",
"ultimaker_methodxl": "MakerBot Method XL"
}