From a315171d7c442bb66664096d1b4765e9f03d84f6 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 14 Jun 2022 16:19:56 +0200 Subject: [PATCH 1/5] Replace checkboxes in save project to DL dialog with a combobox CURA-8555 --- .../resources/qml/SaveProjectFilesPage.qml | 55 ++++++------------- 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml b/plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml index 90b9bd8b05..a8e672ffa2 100644 --- a/plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml +++ b/plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml @@ -14,6 +14,9 @@ import DigitalFactory 1.0 as DF Item { id: base + + property variant catalog: UM.I18nCatalog { name: "cura" } + width: parent.width height: parent.height @@ -63,7 +66,7 @@ Item anchors.topMargin: UM.Theme.getSize("thin_margin").height validator: RegularExpressionValidator { - regularExpression: /^[\w\-\. ()]{0,255}$/ +// regularExpression: /^[\w\-\. ()]{0,255}$/ } text: PrintInformation.jobName @@ -190,53 +193,29 @@ Item text: "Save" enabled: (asProjectCheckbox.checked || asSlicedCheckbox.checked) && dfFilenameTextfield.text.length >= 1 && dfFilenameTextfield.state !== 'invalid' - onClicked: - { - let saveAsFormats = []; - if (asProjectCheckbox.checked) - { - saveAsFormats.push("3mf"); - } - if (asSlicedCheckbox.checked) - { - saveAsFormats.push("ufp"); - } - manager.saveFileToSelectedProject(dfFilenameTextfield.text, saveAsFormats); - } + onClicked: manager.saveFileToSelectedProject(dfFilenameTextfield.text, asProjectComboBox.currentValue) busy: false } - Row + Cura.ComboBox { + id: asProjectComboBox - id: saveAsFormatRow + width: UM.Theme.getSize("combobox_wide").width + height: saveButton.height anchors.verticalCenter: saveButton.verticalCenter anchors.right: saveButton.left anchors.rightMargin: UM.Theme.getSize("thin_margin").height - width: childrenRect.width - spacing: UM.Theme.getSize("default_margin").width - UM.CheckBox - { - id: asProjectCheckbox - height: UM.Theme.getSize("checkbox").height - anchors.verticalCenter: parent.verticalCenter - checked: true - text: "Save Cura project" - font: UM.Theme.getFont("medium") - } + enabled: UM.Backend.state == UM.Backend.Done + currentIndex: UM.Backend.state == UM.Backend.Done ? 0 : 1 + textRole: "text" + valueRole: "value" - UM.CheckBox - { - id: asSlicedCheckbox - 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") - } + model: [ + { text: catalog.i18nc("@option", "Save Cura project and print file"), key: "3mf_ufp", value: ["3mf", "ufp"] }, + { text: catalog.i18nc("@option", "Save Cura project"), key: "3mf", value: ["3mf"] }, + ] } Component.onCompleted: From 0d4c3fa34d4690c16e5f536553e3f4d242d5620c Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 14 Jun 2022 16:20:38 +0200 Subject: [PATCH 2/5] Use modern python typing notation CURA-8555 --- .../src/DFFileExportAndUploadManager.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py b/plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py index 940711f19c..bea5e0a6db 100644 --- a/plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py +++ b/plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py @@ -37,16 +37,16 @@ class DFFileExportAndUploadManager: formats: List[str], on_upload_error: Callable[[], Any], on_upload_success: Callable[[], Any], - on_upload_finished: Callable[[], Any] , + on_upload_finished: Callable[[], Any], on_upload_progress: Callable[[int], Any]) -> None: - self._file_handlers = file_handlers # type: Dict[str, FileHandler] - self._nodes = nodes # type: List[SceneNode] - self._library_project_id = library_project_id # type: str - self._library_project_name = library_project_name # type: str - self._file_name = file_name # type: str - self._upload_jobs = [] # type: List[ExportFileJob] - self._formats = formats # type: List[str] + self._file_handlers: Dict[str, FileHandler] = file_handlers + self._nodes: List[SceneNode] = nodes + self._library_project_id: str = library_project_id + self._library_project_name: str = library_project_name + self._file_name: str = file_name + self._upload_jobs: List[ExportFileJob] = [] + self._formats: List[str] = formats self._api = DigitalFactoryApiClient(application = CuraApplication.getInstance(), on_error = lambda error: Logger.log("e", str(error))) # Functions of the parent class that should be called based on the upload process output @@ -59,7 +59,7 @@ class DFFileExportAndUploadManager: # show the success message (once both upload jobs are done) 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( title = "Uploading...", From a7fcc1519786dc4dd623df2184cd439a002624c4 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Tue, 14 Jun 2022 16:49:40 +0200 Subject: [PATCH 3/5] Re-enable regular expression CURA-8555 --- plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml b/plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml index a8e672ffa2..0a94a4f48a 100644 --- a/plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml +++ b/plugins/DigitalLibrary/resources/qml/SaveProjectFilesPage.qml @@ -66,7 +66,7 @@ Item anchors.topMargin: UM.Theme.getSize("thin_margin").height validator: RegularExpressionValidator { -// regularExpression: /^[\w\-\. ()]{0,255}$/ + regularExpression: /^[\w\-\. ()]{0,255}$/ } text: PrintInformation.jobName From f67d0861820b20ea7f82c316945327af414f7a76 Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 15 Jun 2022 13:27:47 +0200 Subject: [PATCH 4/5] Boyscouting modern python typing CURA-8555 --- plugins/DigitalLibrary/src/DFFileUploader.py | 6 +++--- plugins/DigitalLibrary/src/DigitalFactoryApiClient.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/DigitalLibrary/src/DFFileUploader.py b/plugins/DigitalLibrary/src/DFFileUploader.py index 6cad7828e2..23a4620bcf 100644 --- a/plugins/DigitalLibrary/src/DFFileUploader.py +++ b/plugins/DigitalLibrary/src/DFFileUploader.py @@ -39,8 +39,8 @@ class DFFileUploader: :param on_error: The method to be called when an error occurs. """ - self._http = http # type: HttpRequestManager - self._df_file = df_file # type: Union[DFLibraryFileUploadResponse, DFPrintJobUploadResponse] + self._http: HttpRequestManager = http + self._df_file: Union[DFLibraryFileUploadResponse, DFPrintJobUploadResponse] = df_file self._file_name = "" if isinstance(self._df_file, DFLibraryFileUploadResponse): self._file_name = self._df_file.file_name @@ -51,7 +51,7 @@ class DFFileUploader: self._file_name = "" else: raise TypeError("Incorrect input type") - self._data = data # type: bytes + self._data: bytes = data self._on_finished = on_finished self._on_success = on_success diff --git a/plugins/DigitalLibrary/src/DigitalFactoryApiClient.py b/plugins/DigitalLibrary/src/DigitalFactoryApiClient.py index 3456638ba6..de09ea2a09 100644 --- a/plugins/DigitalLibrary/src/DigitalFactoryApiClient.py +++ b/plugins/DigitalLibrary/src/DigitalFactoryApiClient.py @@ -40,7 +40,7 @@ class DigitalFactoryApiClient: DEFAULT_REQUEST_TIMEOUT = 10 # seconds # 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: """Initializes a new digital factory API client. @@ -54,7 +54,7 @@ class DigitalFactoryApiClient: self._scope = JsonDecoratorScope(UltimakerCloudScope(application)) self._http = HttpRequestManager.getInstance() 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._projects_pagination_mgr = PaginationManager(limit = projects_limit_per_page) if projects_limit_per_page else None # type: Optional[PaginationManager] From 854607a7252ef686f18b687f9d5c482dbdb96baa Mon Sep 17 00:00:00 2001 From: "c.lamboo" Date: Wed, 15 Jun 2022 13:36:34 +0200 Subject: [PATCH 5/5] Provide `source_file_id` with print file Had to implement this a bit differently as stated in the ticket. This field is returned when uploading the project file. Logic needed a bit of a change as the new behavior dictates a sequence (we can only upload the print file after the project file is uploaded, and we know the correct `file_id`/`source_file_id`) where before these two api calls were done in parallel. CURA-8555 --- .../src/DFFileExportAndUploadManager.py | 18 +++++++++++++++--- .../src/DFPrintJobUploadRequest.py | 5 ++++- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py b/plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py index bea5e0a6db..69bf4b1233 100644 --- a/plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py +++ b/plugins/DigitalLibrary/src/DFFileExportAndUploadManager.py @@ -48,6 +48,7 @@ class DFFileExportAndUploadManager: self._upload_jobs: List[ExportFileJob] = [] self._formats: List[str] = formats 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 self._on_upload_error = on_upload_error @@ -113,7 +114,8 @@ class DFFileExportAndUploadManager: content_type = job.getMimeType(), job_name = job.getFileName(), 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) @@ -125,6 +127,9 @@ class DFFileExportAndUploadManager: """ if isinstance(file_upload_response, DFLibraryFileUploadResponse): 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): file_name = file_upload_response.job_name if file_upload_response.job_name is not None else "" else: @@ -145,6 +150,8 @@ class DFFileExportAndUploadManager: on_progress = self._onUploadProgress, on_error = self._onUploadError) + self._handleNextUploadJob() + 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 @@ -325,8 +332,13 @@ class DFFileExportAndUploadManager: message.hide() def start(self) -> None: - for job in self._upload_jobs: - job.start() + self._handleNextUploadJob() + + def _handleNextUploadJob(self): + match self._upload_jobs: + case [job, *jobs]: + job.start() + self._upload_jobs = jobs def initializeFileUploadJobMetadata(self) -> Dict[str, Any]: metadata = {} diff --git a/plugins/DigitalLibrary/src/DFPrintJobUploadRequest.py b/plugins/DigitalLibrary/src/DFPrintJobUploadRequest.py index ab434e3f04..b49336a2f2 100644 --- a/plugins/DigitalLibrary/src/DFPrintJobUploadRequest.py +++ b/plugins/DigitalLibrary/src/DFPrintJobUploadRequest.py @@ -1,12 +1,14 @@ # Copyright (c) 2021 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. +from typing import Optional + from .BaseModel import BaseModel # Model that represents the request to upload a print job to the cloud 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. :param job_name: The name of the print job. @@ -18,4 +20,5 @@ class DFPrintJobUploadRequest(BaseModel): self.file_size = file_size self.content_type = content_type self.library_project_id = library_project_id + self.source_file_id = source_file_id super().__init__(**kwargs)