Cura/cura/PrinterOutput/UploadMaterialsJob.py
Ghostkeeper 2b785343b5
Implement basic uploading of material
Steps involved are:
- Create an archive of all materials.
- Request the cloud API to provide a URL to upload the archive to.
- Upload the archive to that API.

Currently the two internet requests are asynchronous, meaning that the job will 'end' before the upload is complete. Most likely the job instance will even be deleted before we get a response from the server. So this won't work, really. Need to structure that a bit differently. But I want to save this progress because it embodies the happy path well.

Contributes to issue CURA-8609.
2021-10-08 16:02:31 +02:00

83 lines
No EOL
3.3 KiB
Python

# Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher.
from PyQt5.QtCore import QUrl
import os # To delete the archive when we're done.
import tempfile # To create an archive before we upload it.
import enum
import cura.CuraApplication # Imported like this to prevent circular imports.
from cura.UltimakerCloud import UltimakerCloudConstants # To know where the API is.
from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope # To know how to communicate with this server.
from UM.Job import Job
from UM.Logger import Logger
from UM.TaskManagement.HttpRequestManager import HttpRequestManager # To call the API.
from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope
from typing import Optional, TYPE_CHECKING
if TYPE_CHECKING:
from PyQt5.QtNetwork import QNetworkReply
class UploadMaterialsJob(Job):
"""
Job that uploads a set of materials to the Digital Factory.
"""
UPLOAD_REQUEST_URL = f"{UltimakerCloudConstants.CuraDigitalFactoryURL}/materials/profile_upload"
class Result(enum.IntEnum):
SUCCCESS = 0
FAILED = 1
def __init__(self):
super().__init__()
self._scope = JsonDecoratorScope(UltimakerCloudScope(cura.CuraApplication.CuraApplication.getInstance())) # type: JsonDecoratorScope
def run(self):
archive_file = tempfile.NamedTemporaryFile("wb", delete = False)
archive_file.close()
cura.CuraApplication.CuraApplication.getInstance().getMaterialManagementModel().exportAll(QUrl.fromLocalFile(archive_file.name))
http = HttpRequestManager.getInstance()
http.get(
url = self.UPLOAD_REQUEST_URL,
callback = self.onUploadRequestCompleted,
error_callback = self.onError,
scope = self._scope
)
def onUploadRequestCompleted(self, reply: "QNetworkReply", error: Optional["QNetworkReply.NetworkError"]):
if error is not None:
Logger.error(f"Could not request URL to upload material archive to: {error}")
self.setResult(self.Result.FAILED)
return
response_data = HttpRequestManager.readJSON(reply)
if response_data is None:
Logger.error(f"Invalid response to material upload request. Could not parse JSON data.")
self.setResult(self.Result.FAILED)
return
if "upload_url" not in response_data:
Logger.error(f"Invalid response to material upload request: Missing 'upload_url' field to upload archive to.")
self.setResult(self.Result.Failed)
return
upload_url = response_data["upload_url"]
http = HttpRequestManager.getInstance()
http.put(
url = upload_url,
callback = self.onUploadCompleted,
error_callback = self.onError,
scope = self._scope
)
def onUploadCompleted(self, reply: "QNetworkReply", error: Optional["QNetworkReply.NetworkError"]):
if error is not None:
Logger.error(f"Failed to upload material archive: {error}")
self.setResult(self.Result.FAILED)
return
self.setResult(self.Result.SUCCESS)
def onError(self, reply: "QNetworkReply", error: Optional["QNetworkReply.NetworkError"]):
pass # TODO: Handle errors.