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.
This commit is contained in:
Ghostkeeper 2021-10-08 16:02:31 +02:00
parent 042bd46fba
commit 2b785343b5
No known key found for this signature in database
GPG key ID: D2A8871EE34EC59A

View file

@ -4,22 +4,80 @@
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))
print("Creating archive completed. Now we need to upload it.") # TODO: Upload that file.
http = HttpRequestManager.getInstance()
http.get(
url = self.UPLOAD_REQUEST_URL,
callback = self.onUploadRequestCompleted,
error_callback = self.onError,
scope = self._scope
)
os.remove(archive_file.name) # Clean up.
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.