Merge pull request #12531 from Ultimaker/CURA-8555_link_ufp_to_3mf_in_digital_library

Cura 8555 link ufp to 3mf in digital library
This commit is contained in:
Remco Burema 2022-06-15 15:16:11 +02:00 committed by GitHub
commit 188b63703b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 49 additions and 55 deletions

View File

@ -14,6 +14,9 @@ import DigitalFactory 1.0 as DF
Item Item
{ {
id: base id: base
property variant catalog: UM.I18nCatalog { name: "cura" }
width: parent.width width: parent.width
height: parent.height height: parent.height
@ -190,53 +193,29 @@ Item
text: "Save" text: "Save"
enabled: (asProjectCheckbox.checked || asSlicedCheckbox.checked) && dfFilenameTextfield.text.length >= 1 && dfFilenameTextfield.state !== 'invalid' enabled: (asProjectCheckbox.checked || asSlicedCheckbox.checked) && dfFilenameTextfield.text.length >= 1 && dfFilenameTextfield.state !== 'invalid'
onClicked: onClicked: manager.saveFileToSelectedProject(dfFilenameTextfield.text, asProjectComboBox.currentValue)
{
let saveAsFormats = [];
if (asProjectCheckbox.checked)
{
saveAsFormats.push("3mf");
}
if (asSlicedCheckbox.checked)
{
saveAsFormats.push("ufp");
}
manager.saveFileToSelectedProject(dfFilenameTextfield.text, saveAsFormats);
}
busy: false busy: false
} }
Row Cura.ComboBox
{ {
id: asProjectComboBox
id: saveAsFormatRow width: UM.Theme.getSize("combobox_wide").width
height: saveButton.height
anchors.verticalCenter: saveButton.verticalCenter anchors.verticalCenter: saveButton.verticalCenter
anchors.right: saveButton.left anchors.right: saveButton.left
anchors.rightMargin: UM.Theme.getSize("thin_margin").height anchors.rightMargin: UM.Theme.getSize("thin_margin").height
width: childrenRect.width
spacing: UM.Theme.getSize("default_margin").width
UM.CheckBox enabled: UM.Backend.state == UM.Backend.Done
{ currentIndex: UM.Backend.state == UM.Backend.Done ? 0 : 1
id: asProjectCheckbox textRole: "text"
height: UM.Theme.getSize("checkbox").height valueRole: "value"
anchors.verticalCenter: parent.verticalCenter
checked: true
text: "Save Cura project"
font: UM.Theme.getFont("medium")
}
UM.CheckBox model: [
{ { text: catalog.i18nc("@option", "Save Cura project and print file"), key: "3mf_ufp", value: ["3mf", "ufp"] },
id: asSlicedCheckbox { text: catalog.i18nc("@option", "Save Cura project"), key: "3mf", value: ["3mf"] },
height: UM.Theme.getSize("checkbox").height ]
anchors.verticalCenter: parent.verticalCenter
enabled: UM.Backend.state == UM.Backend.Done
checked: UM.Backend.state == UM.Backend.Done
text: "Save print file"
font: UM.Theme.getFont("medium")
}
} }
Component.onCompleted: Component.onCompleted:

View File

@ -37,17 +37,18 @@ class DFFileExportAndUploadManager:
formats: List[str], formats: List[str],
on_upload_error: Callable[[], Any], on_upload_error: Callable[[], Any],
on_upload_success: Callable[[], Any], on_upload_success: Callable[[], Any],
on_upload_finished: Callable[[], Any] , on_upload_finished: Callable[[], Any],
on_upload_progress: Callable[[int], Any]) -> None: on_upload_progress: Callable[[int], Any]) -> None:
self._file_handlers = file_handlers # type: Dict[str, FileHandler] self._file_handlers: Dict[str, FileHandler] = file_handlers
self._nodes = nodes # type: List[SceneNode] self._nodes: List[SceneNode] = nodes
self._library_project_id = library_project_id # type: str self._library_project_id: str = library_project_id
self._library_project_name = library_project_name # type: str self._library_project_name: str = library_project_name
self._file_name = file_name # type: str self._file_name: str = file_name
self._upload_jobs = [] # type: List[ExportFileJob] self._upload_jobs: List[ExportFileJob] = []
self._formats = formats # type: List[str] self._formats: List[str] = formats
self._api = DigitalFactoryApiClient(application = CuraApplication.getInstance(), on_error = lambda error: Logger.log("e", str(error))) self._api = DigitalFactoryApiClient(application = CuraApplication.getInstance(), on_error = lambda error: Logger.log("e", str(error)))
self._source_file_id: Optional[str] = None
# Functions of the parent class that should be called based on the upload process output # Functions of the parent class that should be called based on the upload process output
self._on_upload_error = on_upload_error self._on_upload_error = on_upload_error
@ -59,7 +60,7 @@ class DFFileExportAndUploadManager:
# show the success message (once both upload jobs are done) # show the success message (once both upload jobs are done)
self._message_lock = threading.Lock() self._message_lock = threading.Lock()
self._file_upload_job_metadata = self.initializeFileUploadJobMetadata() # type: Dict[str, Dict[str, Any]] self._file_upload_job_metadata: Dict[str, Dict[str, Any]] = self.initializeFileUploadJobMetadata()
self.progress_message = Message( self.progress_message = Message(
title = "Uploading...", title = "Uploading...",
@ -113,7 +114,8 @@ class DFFileExportAndUploadManager:
content_type = job.getMimeType(), content_type = job.getMimeType(),
job_name = job.getFileName(), job_name = job.getFileName(),
file_size = len(job.getOutput()), file_size = len(job.getOutput()),
library_project_id = self._library_project_id library_project_id = self._library_project_id,
source_file_id = self._source_file_id
) )
self._api.requestUploadUFP(request, on_finished = self._uploadFileData, on_error = self._onRequestUploadPrintFileFailed) self._api.requestUploadUFP(request, on_finished = self._uploadFileData, on_error = self._onRequestUploadPrintFileFailed)
@ -125,6 +127,9 @@ class DFFileExportAndUploadManager:
""" """
if isinstance(file_upload_response, DFLibraryFileUploadResponse): if isinstance(file_upload_response, DFLibraryFileUploadResponse):
file_name = file_upload_response.file_name file_name = file_upload_response.file_name
# store the `file_id` so it can be as `source_file_id` when uploading the print file
self._source_file_id = file_upload_response.file_id
elif isinstance(file_upload_response, DFPrintJobUploadResponse): elif isinstance(file_upload_response, DFPrintJobUploadResponse):
file_name = file_upload_response.job_name if file_upload_response.job_name is not None else "" file_name = file_upload_response.job_name if file_upload_response.job_name is not None else ""
else: else:
@ -145,6 +150,8 @@ class DFFileExportAndUploadManager:
on_progress = self._onUploadProgress, on_progress = self._onUploadProgress,
on_error = self._onUploadError) on_error = self._onUploadError)
self._handleNextUploadJob()
def _onUploadProgress(self, filename: str, progress: int) -> None: def _onUploadProgress(self, filename: str, progress: int) -> None:
""" """
Updates the progress message according to the total progress of the two files and displays it to the user. It is Updates the progress message according to the total progress of the two files and displays it to the user. It is
@ -325,8 +332,13 @@ class DFFileExportAndUploadManager:
message.hide() message.hide()
def start(self) -> None: def start(self) -> None:
for job in self._upload_jobs: self._handleNextUploadJob()
job.start()
def _handleNextUploadJob(self):
match self._upload_jobs:
case [job, *jobs]:
job.start()
self._upload_jobs = jobs
def initializeFileUploadJobMetadata(self) -> Dict[str, Any]: def initializeFileUploadJobMetadata(self) -> Dict[str, Any]:
metadata = {} metadata = {}

View File

@ -39,8 +39,8 @@ class DFFileUploader:
:param on_error: The method to be called when an error occurs. :param on_error: The method to be called when an error occurs.
""" """
self._http = http # type: HttpRequestManager self._http: HttpRequestManager = http
self._df_file = df_file # type: Union[DFLibraryFileUploadResponse, DFPrintJobUploadResponse] self._df_file: Union[DFLibraryFileUploadResponse, DFPrintJobUploadResponse] = df_file
self._file_name = "" self._file_name = ""
if isinstance(self._df_file, DFLibraryFileUploadResponse): if isinstance(self._df_file, DFLibraryFileUploadResponse):
self._file_name = self._df_file.file_name self._file_name = self._df_file.file_name
@ -51,7 +51,7 @@ class DFFileUploader:
self._file_name = "" self._file_name = ""
else: else:
raise TypeError("Incorrect input type") raise TypeError("Incorrect input type")
self._data = data # type: bytes self._data: bytes = data
self._on_finished = on_finished self._on_finished = on_finished
self._on_success = on_success self._on_success = on_success

View File

@ -1,12 +1,14 @@
# Copyright (c) 2021 Ultimaker B.V. # Copyright (c) 2021 Ultimaker B.V.
# Cura is released under the terms of the LGPLv3 or higher. # Cura is released under the terms of the LGPLv3 or higher.
from typing import Optional
from .BaseModel import BaseModel from .BaseModel import BaseModel
# Model that represents the request to upload a print job to the cloud # Model that represents the request to upload a print job to the cloud
class DFPrintJobUploadRequest(BaseModel): class DFPrintJobUploadRequest(BaseModel):
def __init__(self, job_name: str, file_size: int, content_type: str, library_project_id: str, **kwargs) -> None: def __init__(self, job_name: str, file_size: int, content_type: str, library_project_id: str, source_file_id: str, **kwargs) -> None:
"""Creates a new print job upload request. """Creates a new print job upload request.
:param job_name: The name of the print job. :param job_name: The name of the print job.
@ -18,4 +20,5 @@ class DFPrintJobUploadRequest(BaseModel):
self.file_size = file_size self.file_size = file_size
self.content_type = content_type self.content_type = content_type
self.library_project_id = library_project_id self.library_project_id = library_project_id
self.source_file_id = source_file_id
super().__init__(**kwargs) super().__init__(**kwargs)

View File

@ -40,7 +40,7 @@ class DigitalFactoryApiClient:
DEFAULT_REQUEST_TIMEOUT = 10 # seconds DEFAULT_REQUEST_TIMEOUT = 10 # seconds
# In order to avoid garbage collection we keep the callbacks in this list. # In order to avoid garbage collection we keep the callbacks in this list.
_anti_gc_callbacks = [] # type: List[Callable[[Any], None]] _anti_gc_callbacks: List[Callable[[Any], None]] = []
def __init__(self, application: CuraApplication, on_error: Callable[[List[CloudError]], None], projects_limit_per_page: Optional[int] = None) -> None: def __init__(self, application: CuraApplication, on_error: Callable[[List[CloudError]], None], projects_limit_per_page: Optional[int] = None) -> None:
"""Initializes a new digital factory API client. """Initializes a new digital factory API client.
@ -54,7 +54,7 @@ class DigitalFactoryApiClient:
self._scope = JsonDecoratorScope(UltimakerCloudScope(application)) self._scope = JsonDecoratorScope(UltimakerCloudScope(application))
self._http = HttpRequestManager.getInstance() self._http = HttpRequestManager.getInstance()
self._on_error = on_error self._on_error = on_error
self._file_uploader = None # type: Optional[DFFileUploader] self._file_uploader: Optional[DFFileUploader] = None
self._library_max_private_projects: Optional[int] = None self._library_max_private_projects: Optional[int] = None
self._projects_pagination_mgr = PaginationManager(limit = projects_limit_per_page) if projects_limit_per_page else None # type: Optional[PaginationManager] self._projects_pagination_mgr = PaginationManager(limit = projects_limit_per_page) if projects_limit_per_page else None # type: Optional[PaginationManager]