mirror of
https://github.com/Ultimaker/Cura.git
synced 2025-07-09 07:56:22 -06:00
Merge more stuff, re-use models for local networking as well
This commit is contained in:
parent
8f37c83b9c
commit
4b212d6c05
31 changed files with 688 additions and 975 deletions
|
@ -1,10 +1,21 @@
|
|||
# Copyright (c) 2018 Ultimaker B.V.
|
||||
# Cura is released under the terms of the LGPLv3 or higher.
|
||||
from typing import Callable, List, Optional
|
||||
import json
|
||||
from json import JSONDecodeError
|
||||
from typing import Callable, List, Optional, Dict, Union, Any, Type, cast, TypeVar, Tuple
|
||||
|
||||
from PyQt5.QtCore import QUrl
|
||||
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest, QNetworkReply
|
||||
|
||||
from UM.Logger import Logger
|
||||
from plugins.UM3NetworkPrinting.src.Models.BaseModel import BaseModel
|
||||
|
||||
|
||||
## The generic type variable used to document the methods below.
|
||||
from plugins.UM3NetworkPrinting.src.Models.Http.ClusterPrinterStatus import ClusterPrinterStatus
|
||||
|
||||
ClusterApiClientModel = TypeVar("ClusterApiClientModel", bound=BaseModel)
|
||||
|
||||
|
||||
## The ClusterApiClient is responsible for all network calls to local network clusters.
|
||||
class ClusterApiClient:
|
||||
|
@ -30,32 +41,95 @@ class ClusterApiClient:
|
|||
## Get printer system information.
|
||||
# \param on_finished: The callback in case the response is successful.
|
||||
def getSystem(self, on_finished: Callable) -> None:
|
||||
url = f"{self.PRINTER_API_PREFIX}/system"
|
||||
url = f"{self.PRINTER_API_PREFIX}/system/"
|
||||
self._manager.get(self._createEmptyRequest(url))
|
||||
|
||||
## Get the printers in the cluster.
|
||||
# \param on_finished: The callback in case the response is successful.
|
||||
def getPrinters(self, on_finished: Callable[[List[ClusterPrinterStatus]], Any]) -> None:
|
||||
url = f"{self.CLUSTER_API_PREFIX}/printers/"
|
||||
reply = self._manager.get(self._createEmptyRequest(url))
|
||||
self._addCallback(reply, on_finished)
|
||||
self._addCallback(reply, on_finished, ClusterPrinterStatus)
|
||||
|
||||
## Get the print jobs in the cluster.
|
||||
# \param on_finished: The callback in case the response is successful.
|
||||
def getPrintJobs(self, on_finished: Callable) -> None:
|
||||
url = f"{self.CLUSTER_API_PREFIX}/print_jobs/"
|
||||
# reply = self._manager.get(self._createEmptyRequest(url))
|
||||
# self._addCallback(reply, on_finished)
|
||||
|
||||
def requestPrint(self) -> None:
|
||||
pass
|
||||
|
||||
## Send a print job action to the cluster.
|
||||
# \param print_job_uuid: The UUID of the print job to perform the action on.
|
||||
# \param action: The action to perform.
|
||||
# \param data: The optional data to send along, used for 'move' and 'duplicate'.
|
||||
def doPrintJobAction(self, print_job_uuid: str, action: str, data: Optional[Dict[str, Union[str, int]]] = None
|
||||
) -> None:
|
||||
url = f"{self.CLUSTER_API_PREFIX}/print_jobs/{print_job_uuid}/action/{action}/"
|
||||
body = json.loads(data).encode() if data else b""
|
||||
self._manager.put(self._createEmptyRequest(url), body)
|
||||
|
||||
## We override _createEmptyRequest in order to add the user credentials.
|
||||
# \param url: The URL to request
|
||||
# \param content_type: The type of the body contents.
|
||||
def _createEmptyRequest(self, path: str, content_type: Optional[str] = "application/json") -> QNetworkRequest:
|
||||
request = QNetworkRequest(QUrl(self._address + path))
|
||||
url = QUrl("http://" + self._address + path)
|
||||
request = QNetworkRequest(url)
|
||||
if content_type:
|
||||
request.setHeader(QNetworkRequest.ContentTypeHeader, content_type)
|
||||
return request
|
||||
|
||||
## Parses the given JSON network reply into a status code and a dictionary, handling unexpected errors as well.
|
||||
# \param reply: The reply from the server.
|
||||
# \return A tuple with a status code and a dictionary.
|
||||
@staticmethod
|
||||
def _parseReply(reply: QNetworkReply) -> Tuple[int, Dict[str, Any]]:
|
||||
status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
|
||||
try:
|
||||
response = bytes(reply.readAll()).decode()
|
||||
return status_code, json.loads(response)
|
||||
except (UnicodeDecodeError, JSONDecodeError, ValueError) as err:
|
||||
Logger.logException("e", "Could not parse the cluster response: %s", err)
|
||||
return status_code, {"errors": [err]}
|
||||
|
||||
## Parses the given models and calls the correct callback depending on the result.
|
||||
# \param response: The response from the server, after being converted to a dict.
|
||||
# \param on_finished: The callback in case the response is successful.
|
||||
# \param model_class: The type of the model to convert the response to. It may either be a single record or a list.
|
||||
def _parseModels(self, response: Dict[str, Any],
|
||||
on_finished: Union[Callable[[ClusterApiClientModel], Any],
|
||||
Callable[[List[ClusterApiClientModel]], Any]],
|
||||
model_class: Type[ClusterApiClientModel]) -> None:
|
||||
if isinstance(response, list):
|
||||
results = [model_class(**c) for c in response] # type: List[ClusterApiClientModel]
|
||||
on_finished_list = cast(Callable[[List[ClusterApiClientModel]], Any], on_finished)
|
||||
on_finished_list(results)
|
||||
else:
|
||||
result = model_class(**response) # type: ClusterApiClientModel
|
||||
on_finished_item = cast(Callable[[ClusterApiClientModel], Any], on_finished)
|
||||
on_finished_item(result)
|
||||
|
||||
## Creates a callback function so that it includes the parsing of the response into the correct model.
|
||||
# The callback is added to the 'finished' signal of the reply.
|
||||
# \param reply: The reply that should be listened to.
|
||||
# \param on_finished: The callback in case the response is successful.
|
||||
def _addCallback(self, reply: QNetworkReply, on_finished: Callable) -> None:
|
||||
def _addCallback(self,
|
||||
reply: QNetworkReply,
|
||||
on_finished: Union[Callable[[ClusterApiClientModel], Any],
|
||||
Callable[[List[ClusterApiClientModel]], Any]],
|
||||
model: Type[ClusterApiClientModel],
|
||||
) -> None:
|
||||
def parse() -> None:
|
||||
# Don't try to parse the reply if we didn't get one
|
||||
if reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) is None:
|
||||
return
|
||||
status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
|
||||
response = bytes(reply.readAll()).decode()
|
||||
status_code, response = self._parseReply(reply)
|
||||
self._anti_gc_callbacks.remove(parse)
|
||||
on_finished(int(status_code), response)
|
||||
if on_finished:
|
||||
self._parseModels(response, on_finished, model)
|
||||
return
|
||||
self._anti_gc_callbacks.append(parse)
|
||||
reply.finished.connect(parse)
|
||||
if on_finished:
|
||||
reply.finished.connect(parse)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue