STAR-322: Removing the print job after it was done

This commit is contained in:
Daniel Schiavini 2018-12-11 14:28:00 +01:00
parent 073e8cd6dc
commit a5d8e6ceb8
3 changed files with 27 additions and 24 deletions

View file

@ -2,7 +2,7 @@
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
import json import json
from json import JSONDecodeError from json import JSONDecodeError
from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict from typing import Callable, List, Type, TypeVar, Union, Optional, Tuple, Dict, Any
from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply
@ -19,7 +19,7 @@ from .Models.CloudPrintJobResponse import CloudPrintJobResponse
## The cloud API client is responsible for handling the requests and responses from the cloud. ## The cloud API client is responsible for handling the requests and responses from the cloud.
# Each method should only handle models instead of exposing any HTTP details. # Each method should only handle models instead of exposing Any HTTP details.
class CloudApiClient(NetworkClient): class CloudApiClient(NetworkClient):
# The cloud URL to use for this remote cluster. # The cloud URL to use for this remote cluster.
@ -43,21 +43,21 @@ class CloudApiClient(NetworkClient):
## Retrieves all the clusters for the user that is currently logged in. ## Retrieves all the clusters for the user that is currently logged in.
# \param on_finished: The function to be called after the result is parsed. # \param on_finished: The function to be called after the result is parsed.
def getClusters(self, on_finished: Callable[[List[CloudClusterResponse]], any]) -> None: def getClusters(self, on_finished: Callable[[List[CloudClusterResponse]], Any]) -> None:
url = "{}/clusters".format(self.CLUSTER_API_ROOT) url = "{}/clusters".format(self.CLUSTER_API_ROOT)
self.get(url, on_finished=self._wrapCallback(on_finished, CloudClusterResponse)) self.get(url, on_finished=self._wrapCallback(on_finished, CloudClusterResponse))
## Retrieves the status of the given cluster. ## Retrieves the status of the given cluster.
# \param cluster_id: The ID of the cluster. # \param cluster_id: The ID of the cluster.
# \param on_finished: The function to be called after the result is parsed. # \param on_finished: The function to be called after the result is parsed.
def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], any]) -> None: def getClusterStatus(self, cluster_id: str, on_finished: Callable[[CloudClusterStatus], Any]) -> None:
url = "{}/clusters/{}/status".format(self.CLUSTER_API_ROOT, cluster_id) url = "{}/clusters/{}/status".format(self.CLUSTER_API_ROOT, cluster_id)
self.get(url, on_finished=self._wrapCallback(on_finished, CloudClusterStatus)) self.get(url, on_finished=self._wrapCallback(on_finished, CloudClusterStatus))
## Requests the cloud to register the upload of a print job mesh. ## Requests the cloud to register the upload of a print job mesh.
# \param request: The request object. # \param request: The request object.
# \param on_finished: The function to be called after the result is parsed. # \param on_finished: The function to be called after the result is parsed.
def requestUpload(self, request: CloudPrintJobUploadRequest, on_finished: Callable[[CloudPrintJobResponse], any] def requestUpload(self, request: CloudPrintJobUploadRequest, on_finished: Callable[[CloudPrintJobResponse], Any]
) -> None: ) -> None:
url = "{}/jobs/upload".format(self.CURA_API_ROOT) url = "{}/jobs/upload".format(self.CURA_API_ROOT)
body = json.dumps({"data": request.toDict()}) body = json.dumps({"data": request.toDict()})
@ -69,8 +69,8 @@ class CloudApiClient(NetworkClient):
# \param on_finished: The function to be called after the result is parsed. It receives the print job ID. # \param on_finished: The function to be called after the result is parsed. It receives the print job ID.
# \param on_progress: A function to be called during upload progress. It receives a percentage (0-100). # \param on_progress: A function to be called during upload progress. It receives a percentage (0-100).
# \param on_error: A function to be called if the upload fails. It receives a dict with the error. # \param on_error: A function to be called if the upload fails. It receives a dict with the error.
def uploadMesh(self, upload_response: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[str], any], def uploadMesh(self, upload_response: CloudPrintJobResponse, mesh: bytes, on_finished: Callable[[str], Any],
on_progress: Callable[[int], any], on_error: Callable[[dict], any]): on_progress: Callable[[int], Any], on_error: Callable[[dict], Any]):
def progressCallback(bytes_sent: int, bytes_total: int) -> None: def progressCallback(bytes_sent: int, bytes_total: int) -> None:
if bytes_total: if bytes_total:
@ -92,7 +92,7 @@ class CloudApiClient(NetworkClient):
# \param cluster_id: The ID of the cluster. # \param cluster_id: The ID of the cluster.
# \param job_id: The ID of the print job. # \param job_id: The ID of the print job.
# \param on_finished: The function to be called after the result is parsed. # \param on_finished: The function to be called after the result is parsed.
def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], any]) -> None: def requestPrint(self, cluster_id: str, job_id: str, on_finished: Callable[[CloudPrintResponse], Any]) -> None:
url = "{}/clusters/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id) url = "{}/clusters/{}/print/{}".format(self.CLUSTER_API_ROOT, cluster_id, job_id)
self.post(url, data = "", on_finished=self._wrapCallback(on_finished, CloudPrintResponse)) self.post(url, data = "", on_finished=self._wrapCallback(on_finished, CloudPrintResponse))
@ -110,7 +110,7 @@ class CloudApiClient(NetworkClient):
# \param reply: The reply from the server. # \param reply: The reply from the server.
# \return A tuple with a status code and a dictionary. # \return A tuple with a status code and a dictionary.
@staticmethod @staticmethod
def _parseReply(reply: QNetworkReply) -> Tuple[int, Dict[str, any]]: def _parseReply(reply: QNetworkReply) -> Tuple[int, Dict[str, Any]]:
status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute) status_code = reply.attribute(QNetworkRequest.HttpStatusCodeAttribute)
try: try:
response = bytes(reply.readAll()).decode() response = bytes(reply.readAll()).decode()
@ -128,8 +128,8 @@ class CloudApiClient(NetworkClient):
# \param response: The response from the server, after being converted to a dict. # \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 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. # \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], def _parseModels(self, response: Dict[str, Any],
on_finished: Callable[[Union[Model, List[Model]]], any], on_finished: Callable[[Union[Model, List[Model]]], Any],
model_class: Type[Model]) -> None: model_class: Type[Model]) -> None:
if "data" in response: if "data" in response:
data = response["data"] data = response["data"]
@ -145,7 +145,7 @@ class CloudApiClient(NetworkClient):
# \param model: The type of the model to convert the response to. It may either be a single record or a list. # \param model: The type of the model to convert the response to. It may either be a single record or a list.
# \return: A function that can be passed to the # \return: A function that can be passed to the
def _wrapCallback(self, def _wrapCallback(self,
on_finished: Callable[[Union[Model, List[Model]]], any], on_finished: Callable[[Union[Model, List[Model]]], Any],
model: Type[Model], model: Type[Model],
) -> Callable[[QNetworkReply], None]: ) -> Callable[[QNetworkReply], None]:
def parse(reply: QNetworkReply) -> None: def parse(reply: QNetworkReply) -> None:

View file

@ -229,6 +229,9 @@ class CloudOutputDevice(NetworkedPrinterOutputDevice):
removed_jobs, added_jobs, updated_jobs = findChanges(previous, received) removed_jobs, added_jobs, updated_jobs = findChanges(previous, received)
for removed_job in removed_jobs: for removed_job in removed_jobs:
if removed_job.assignedPrinter:
removed_job.assignedPrinter.updateActivePrintJob(None)
removed_job.stateChanged.disconnect(self._onPrintJobStateChanged)
self._print_jobs.remove(removed_job) self._print_jobs.remove(removed_job)
for added_job in added_jobs: for added_job in added_jobs:

View file

@ -56,8 +56,9 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._number_of_extruders = 2 self._number_of_extruders = 2
self._dummy_lambdas = ("", {}, io.BytesIO() self._dummy_lambdas = (
) # type: Tuple[str, Dict[str, Union[str, int, bool]], Union[io.StringIO, io.BytesIO]] "", {}, io.BytesIO()
) # type: Tuple[Optional[str], Dict[str, Union[str, int, bool]], Union[io.StringIO, io.BytesIO]]
self._print_jobs = [] # type: List[UM3PrintJobOutputModel] self._print_jobs = [] # type: List[UM3PrintJobOutputModel]
self._received_print_jobs = False # type: bool self._received_print_jobs = False # type: bool
@ -165,7 +166,8 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
self._sending_gcode = True self._sending_gcode = True
target_printer = yield #Potentially wait on the user to select a target printer. # Potentially wait on the user to select a target printer.
target_printer = yield # type: Optional[str]
# Using buffering greatly reduces the write time for many lines of gcode # Using buffering greatly reduces the write time for many lines of gcode
@ -179,13 +181,12 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
use_inactivity_timer = False) use_inactivity_timer = False)
self._write_job_progress_message.show() self._write_job_progress_message.show()
self._dummy_lambdas = (target_printer, mesh_format.preferred_format, stream) if mesh_format.preferred_format is not None:
job.finished.connect(self._sendPrintJobWaitOnWriteJobFinished) self._dummy_lambdas = (target_printer, mesh_format.preferred_format, stream)
job.finished.connect(self._sendPrintJobWaitOnWriteJobFinished)
job.start() job.start()
yield True # Return that we had success!
yield True # Return that we had success! yield # To prevent having to catch the StopIteration exception.
yield # To prevent having to catch the StopIteration exception.
def _sendPrintJobWaitOnWriteJobFinished(self, job: WriteFileJob) -> None: def _sendPrintJobWaitOnWriteJobFinished(self, job: WriteFileJob) -> None:
if self._write_job_progress_message: if self._write_job_progress_message:
@ -255,8 +256,7 @@ class ClusterUM3OutputDevice(NetworkedPrinterOutputDevice):
# Treat upload progress as response. Uploading can take more than 10 seconds, so if we don't, we can get # Treat upload progress as response. Uploading can take more than 10 seconds, so if we don't, we can get
# timeout responses if this happens. # timeout responses if this happens.
self._last_response_time = time() self._last_response_time = time()
old_progress = self._progress_message.getProgress() if self._progress_message is not None and new_progress > self._progress_message.getProgress():
if self._progress_message and (old_progress is None or new_progress > old_progress):
self._progress_message.show() # Ensure that the message is visible. self._progress_message.show() # Ensure that the message is visible.
self._progress_message.setProgress(bytes_sent / bytes_total * 100) self._progress_message.setProgress(bytes_sent / bytes_total * 100)