From 65dd0250b3f2c52a0e733af15e7bbfd85318322b Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Thu, 4 Jun 2020 14:22:14 +0200 Subject: [PATCH 1/6] Change property connectionStatusMessage into a get function This way the tooltip text can be re-evaluated every time we hover over the instead of only once when the active machine is updated. With this change, the text will be updated properly when there is a change. CURA-7438 --- resources/qml/PrinterSelector/MachineSelector.qml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index 56a3d858ec..b3293c00cd 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -35,7 +35,7 @@ Cura.ExpandablePopup } } - readonly property string connectionStatusMessage: { + function getConnectionStatusMessage() { if (connectionStatus == "printer_cloud_not_available") { if(Cura.API.connectionStatus.isInternetReachable) @@ -139,12 +139,13 @@ Cura.ExpandablePopup { id: connectionStatusTooltipHoverArea anchors.fill: parent - hoverEnabled: connectionStatusMessage !== "" + hoverEnabled: getConnectionStatusMessage() !== "" acceptedButtons: Qt.NoButton // react to hover only, don't steal clicks onEntered: { machineSelector.mouseArea.entered() // we want both this and the outer area to be entered + tooltip.tooltipText = getConnectionStatusMessage() tooltip.show() } onExited: { tooltip.hide() } @@ -155,7 +156,7 @@ Cura.ExpandablePopup id: tooltip width: 250 * screenScaleFactor - tooltipText: connectionStatusMessage + tooltipText: getConnectionStatusMessage() arrowSize: UM.Theme.getSize("button_tooltip_arrow").width x: connectionStatusImage.x - UM.Theme.getSize("narrow_margin").width y: connectionStatusImage.y + connectionStatusImage.height + UM.Theme.getSize("narrow_margin").height From ee7f2d4b38fbc26aa86d113ccd5124a9f514821c Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Thu, 4 Jun 2020 14:56:58 +0200 Subject: [PATCH 2/6] Handle the case when a cloud printer is removed from the account When a cloud printer is no longer received from mycloud, it is safe to assume that the printer is removed from the account. In such a case, the new function _devicesRemovedFromAccount(..) will retrieve all the devices that are to be removed (i.e. they are added in Cura but they do not exist in the clusters received from mycloud) and will remove their output device. In addition the function will generate a message that mentions all the devices that were no longer linked to the account. In order to achieve that, the Cloud/CloudOutputDeviceManager is enriched with the dictionary self._um_cloud_printers that contains all the currently existing cloud-enabled devices that were previously added in Cura. In addition, the meta-data field "removed_from_account" is added to all the cloud-enabled Cura devices to distinguish the ones actually linked to the current account. With these additions, the cloud devices that are removed from the account will be automagically re-linked to it once the devices are once again retrieved from mycloud for the specific account. CURA-7438 --- .../src/Cloud/CloudOutputDeviceManager.py | 108 ++++++++++++++++-- 1 file changed, 97 insertions(+), 11 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 7ee8322ba1..b5ad079786 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -1,7 +1,7 @@ # Copyright (c) 2019 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. import os -from typing import Dict, List, Optional +from typing import Dict, List, Optional, Set from PyQt5.QtCore import QTimer from PyQt5.QtNetwork import QNetworkReply @@ -20,6 +20,8 @@ from .CloudApiClient import CloudApiClient from .CloudOutputDevice import CloudOutputDevice from ..Models.Http.CloudClusterResponse import CloudClusterResponse +META_REMOVED_FROM_ACCOUNT = "removed_from_account" + class CloudOutputDeviceManager: """The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. @@ -41,6 +43,10 @@ class CloudOutputDeviceManager: def __init__(self) -> None: # Persistent dict containing the remote clusters for the authenticated user. self._remote_clusters = {} # type: Dict[str, CloudOutputDevice] + + # Dictionary containing all the cloud printers loaded in Cura + self._um_cloud_printers = {} # type: Dict[str, GlobalStack] + self._account = CuraApplication.getInstance().getCuraAPI().account # type: Account self._api = CloudApiClient(CuraApplication.getInstance(), on_error = lambda error: Logger.log("e", str(error))) self._account.loginStateChanged.connect(self._onLoginStateChanged) @@ -100,23 +106,36 @@ class CloudOutputDeviceManager: def _onGetRemoteClustersFinished(self, clusters: List[CloudClusterResponse]) -> None: """Callback for when the request for getting the clusters is finished.""" + self._um_cloud_printers = {m.getMetaDataEntry(self.META_CLUSTER_ID): m for m in + CuraApplication.getInstance().getContainerRegistry().findContainerStacks( + type = "machine") if m.getMetaDataEntry(self.META_CLUSTER_ID, None)} new_clusters = [] all_clusters = {c.cluster_id: c for c in clusters} # type: Dict[str, CloudClusterResponse] online_clusters = {c.cluster_id: c for c in clusters if c.is_online} # type: Dict[str, CloudClusterResponse] + # Add the new printers in Cura. If a printer was previously added and is rediscovered, set its metadata to + # reflect that and mark the printer not removed from the account for device_id, cluster_data in all_clusters.items(): if device_id not in self._remote_clusters: new_clusters.append(cluster_data) - + if device_id in self._um_cloud_printers and self._um_cloud_printers[device_id].getMetaDataEntry(META_REMOVED_FROM_ACCOUNT, False): + self._um_cloud_printers[device_id].setMetaDataEntry(META_REMOVED_FROM_ACCOUNT, False) self._onDevicesDiscovered(new_clusters) - removed_device_keys = set(self._remote_clusters.keys()) - set(online_clusters.keys()) - for device_id in removed_device_keys: + # Remove the CloudOutput device for offline printers + offline_device_keys = set(self._remote_clusters.keys()) - set(online_clusters.keys()) + for device_id in offline_device_keys: self._onDiscoveredDeviceRemoved(device_id) - if new_clusters or removed_device_keys: - self.discoveredDevicesChanged.emit() + # Handle devices that were previously added in Cura but do not exist in the account anymore (i.e. they were + # removed from the account) + removed_device_keys = set(self._um_cloud_printers.keys()) - set(all_clusters.keys()) if removed_device_keys: + self._devicesRemovedFromAccount(removed_device_keys) + + if new_clusters or offline_device_keys or removed_device_keys: + self.discoveredDevicesChanged.emit() + if offline_device_keys: # If the removed device was active we should connect to the new active device self._connectToActiveMachine() @@ -146,10 +165,13 @@ class CloudOutputDeviceManager: if machine_manager.getMachine(device.printerType, {self.META_CLUSTER_ID: device.key}) is None \ and machine_manager.getMachine(device.printerType, {self.META_NETWORK_KEY: cluster_data.host_name + "*"}) is None: # The host name is part of the network key. new_devices.append(device) - elif device.getId() not in self._remote_clusters: self._remote_clusters[device.getId()] = device remote_clusters_added = True + # If a printer that was removed from the account is re-added, change its metadata to mark it not removed + # from the account + elif self._um_cloud_printers[device.key].getMetaDataEntry(META_REMOVED_FROM_ACCOUNT, False): + self._um_cloud_printers[device.key].setMetaDataEntry(META_REMOVED_FROM_ACCOUNT, False) # Inform the Cloud printers model about new devices. new_devices_list_of_dicts = [{ @@ -206,19 +228,83 @@ class CloudOutputDeviceManager: max_disp_devices = 3 if len(new_devices) > max_disp_devices: num_hidden = len(new_devices) - max_disp_devices + 1 - device_name_list = ["- {} ({})".format(device.name, device.printerTypeName) for device in new_devices[0:num_hidden]] - device_name_list.append(self.I18N_CATALOG.i18nc("info:hidden list items", "- and {} others", num_hidden)) + device_name_list = ["
  • {} ({})
  • ".format(device.name, device.printerTypeName) for device in new_devices[0:num_hidden]] + device_name_list.append(self.I18N_CATALOG.i18nc("info:hidden list items", "
  • ... and {} others
  • ", num_hidden)) device_names = "\n".join(device_name_list) else: - device_names = "\n".join(["- {} ({})".format(device.name, device.printerTypeName) for device in new_devices]) + device_names = "\n".join(["
  • {} ({})
  • ".format(device.name, device.printerTypeName) for device in new_devices]) message_text = self.I18N_CATALOG.i18nc( "info:status", - "Cloud printers added from your account:\n{}", + "Cloud printers added from your account:\n", device_names ) message.setText(message_text) + def _devicesRemovedFromAccount(self, removed_device_ids: Set[str]) -> None: + """ + Removes the CloudOutputDevice from the received device ids and marks the specific printers as "removed from + account". In addition, it generates a message to inform the user about the printers that are no longer linked to + his/her account. The message is not generated if all the printers have been previously reported as not linked + to the account. + + :param removed_device_ids: Set of device ids, whose CloudOutputDevice needs to be removed + :return: None + """ + + if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn: + return + + # Do not report device ids which have been reported previously + ignored_device_ids = set() + for device_id in removed_device_ids: + if self._um_cloud_printers[device_id].getMetaDataEntry(META_REMOVED_FROM_ACCOUNT, False): + ignored_device_ids.add(device_id) + report_device_ids = removed_device_ids - ignored_device_ids + if len(report_device_ids) == 0: + return + + # Generate message + removed_printers_message = Message( + title = self.I18N_CATALOG.i18ncp( + "info:status", + "Cloud connection is not available for a printer", + "Cloud connection is not available for some printers", + len(report_device_ids) + ), + lifetime = 0 + ) + device_names = "\n".join(["
  • {} ({})
  • ".format(self._um_cloud_printers[device].name, self._um_cloud_printers[device].definition.name) for device in report_device_ids]) + message_text = self.I18N_CATALOG.i18ncp( + "info:status", + "The following cloud printer is not linked to your account:\n", + "The following cloud printers are not linked to your account:\n", + len(report_device_ids) + ) + message_text += self.I18N_CATALOG.i18nc( + "info:status", + "\nTo establish a connection, please visit the " + "Ultimaker Digital Factory.", + device_names + ) + removed_printers_message.setText(message_text) + + # Remove the output device from the printers + for device_id in removed_device_ids: + device = self._um_cloud_printers.get(device_id, None) # type: Optional[GlobalStack] + if not device: + continue + output_device_manager = CuraApplication.getInstance().getOutputDeviceManager() + if device_id in output_device_manager.getOutputDeviceIds(): + output_device_manager.removeOutputDevice(device_id) + if device_id in self._remote_clusters: + del self._remote_clusters[device_id] + + # Update the printer's metadata to mark it as removed from account + device.setMetaDataEntry(META_REMOVED_FROM_ACCOUNT, True) + + removed_printers_message.show() + def _onDiscoveredDeviceRemoved(self, device_id: str) -> None: device = self._remote_clusters.pop(device_id, None) # type: Optional[CloudOutputDevice] if not device: From 4bebaaaddbc2043b69389fb4f85d389c05b1be25 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Thu, 4 Jun 2020 15:08:27 +0200 Subject: [PATCH 3/6] Display an appropriate tooltip when device is removed from the account The tooltip is updated to reflect the case where a cloud printer is removed from the users account. CURA-7438 --- cura/Settings/MachineManager.py | 5 +++++ resources/qml/PrinterSelector/MachineSelector.qml | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index e5286e7032..fd119d9c83 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -490,6 +490,11 @@ class MachineManager(QObject): def activeMachineIsGroup(self) -> bool: return bool(self._printer_output_devices) and len(self._printer_output_devices[0].printers) > 1 + @pyqtProperty(bool, notify = printerConnectedStatusChanged) + def activeMachineIsLinkedToCurrentAccount(self) -> bool: + from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager import META_REMOVED_FROM_ACCOUNT + return not self.activeMachine.getMetaDataEntry(META_REMOVED_FROM_ACCOUNT, False) + @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasNetworkConnection(self) -> bool: # A network connection is only available if any output device is actually a network connected device. diff --git a/resources/qml/PrinterSelector/MachineSelector.qml b/resources/qml/PrinterSelector/MachineSelector.qml index b3293c00cd..0907767eea 100644 --- a/resources/qml/PrinterSelector/MachineSelector.qml +++ b/resources/qml/PrinterSelector/MachineSelector.qml @@ -42,7 +42,14 @@ Cura.ExpandablePopup { if (Cura.API.account.isLoggedIn) { - return catalog.i18nc("@status", "The cloud printer is offline. Please check if the printer is turned on and connected to the internet.") + if (Cura.MachineManager.activeMachineIsLinkedToCurrentAccount) + { + return catalog.i18nc("@status", "The cloud printer is offline. Please check if the printer is turned on and connected to the internet.") + } + else + { + return catalog.i18nc("@status", "This printer is not linked to your account. Please visit the Ultimaker Digital Factory to establish a connection.") + } } else { From 458b439a9a6caf0f2c68dd37d5887753259c5835 Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Thu, 4 Jun 2020 16:14:09 +0200 Subject: [PATCH 4/6] Rename META_REMOVED_FROM_ACCOUNT to META_LINKED_TO_ACCOUNT The meta data entry was rename to be more representative of its function. In addition, the bool metadata entry is now properly handled using the parseBool function. CURA-7438 --- cura/Settings/MachineManager.py | 4 ++-- .../src/Cloud/CloudOutputDeviceManager.py | 20 +++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index fd119d9c83..060df10559 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -22,6 +22,7 @@ from UM.Settings.SettingFunction import SettingFunction from UM.Signal import postponeSignals, CompressTechnique import cura.CuraApplication # Imported like this to prevent circular references. +from UM.Util import parseBool from cura.Machines.ContainerNode import ContainerNode from cura.Machines.ContainerTree import ContainerTree @@ -492,8 +493,7 @@ class MachineManager(QObject): @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineIsLinkedToCurrentAccount(self) -> bool: - from plugins.UM3NetworkPrinting.src.Cloud.CloudOutputDeviceManager import META_REMOVED_FROM_ACCOUNT - return not self.activeMachine.getMetaDataEntry(META_REMOVED_FROM_ACCOUNT, False) + return parseBool(self.activeMachine.getMetaDataEntry("linked_to_account", "True")) @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasNetworkConnection(self) -> bool: diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index b5ad079786..c176873284 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -11,6 +11,7 @@ from UM.Logger import Logger # To log errors talking to the API. from UM.Message import Message from UM.Settings.Interfaces import ContainerInterface from UM.Signal import Signal +from UM.Util import parseBool from cura.API import Account from cura.API.Account import SyncState from cura.CuraApplication import CuraApplication @@ -20,8 +21,6 @@ from .CloudApiClient import CloudApiClient from .CloudOutputDevice import CloudOutputDevice from ..Models.Http.CloudClusterResponse import CloudClusterResponse -META_REMOVED_FROM_ACCOUNT = "removed_from_account" - class CloudOutputDeviceManager: """The cloud output device manager is responsible for using the Ultimaker Cloud APIs to manage remote clusters. @@ -33,6 +32,7 @@ class CloudOutputDeviceManager: META_CLUSTER_ID = "um_cloud_cluster_id" META_NETWORK_KEY = "um_network_key" SYNC_SERVICE_NAME = "CloudOutputDeviceManager" + META_LINKED_TO_ACCOUNT = "linked_to_account" # The translation catalog for this device. I18N_CATALOG = i18nCatalog("cura") @@ -118,8 +118,8 @@ class CloudOutputDeviceManager: for device_id, cluster_data in all_clusters.items(): if device_id not in self._remote_clusters: new_clusters.append(cluster_data) - if device_id in self._um_cloud_printers and self._um_cloud_printers[device_id].getMetaDataEntry(META_REMOVED_FROM_ACCOUNT, False): - self._um_cloud_printers[device_id].setMetaDataEntry(META_REMOVED_FROM_ACCOUNT, False) + if device_id in self._um_cloud_printers and not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(self.META_LINKED_TO_ACCOUNT, "true")): + self._um_cloud_printers[device_id].setMetaDataEntry(self.META_LINKED_TO_ACCOUNT, True) self._onDevicesDiscovered(new_clusters) # Remove the CloudOutput device for offline printers @@ -170,8 +170,8 @@ class CloudOutputDeviceManager: remote_clusters_added = True # If a printer that was removed from the account is re-added, change its metadata to mark it not removed # from the account - elif self._um_cloud_printers[device.key].getMetaDataEntry(META_REMOVED_FROM_ACCOUNT, False): - self._um_cloud_printers[device.key].setMetaDataEntry(META_REMOVED_FROM_ACCOUNT, False) + elif not parseBool(self._um_cloud_printers[device.key].getMetaDataEntry(self.META_LINKED_TO_ACCOUNT, "true")): + self._um_cloud_printers[device.key].setMetaDataEntry(self.META_LINKED_TO_ACCOUNT, True) # Inform the Cloud printers model about new devices. new_devices_list_of_dicts = [{ @@ -255,10 +255,10 @@ class CloudOutputDeviceManager: if not CuraApplication.getInstance().getCuraAPI().account.isLoggedIn: return - # Do not report device ids which have been reported previously + # Do not report device ids which have been previously marked as non-linked to the account ignored_device_ids = set() for device_id in removed_device_ids: - if self._um_cloud_printers[device_id].getMetaDataEntry(META_REMOVED_FROM_ACCOUNT, False): + if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(self.META_LINKED_TO_ACCOUNT, "true")): ignored_device_ids.add(device_id) report_device_ids = removed_device_ids - ignored_device_ids if len(report_device_ids) == 0: @@ -300,8 +300,8 @@ class CloudOutputDeviceManager: if device_id in self._remote_clusters: del self._remote_clusters[device_id] - # Update the printer's metadata to mark it as removed from account - device.setMetaDataEntry(META_REMOVED_FROM_ACCOUNT, True) + # Update the printer's metadata to mark it as not linked to the account + device.setMetaDataEntry(self.META_LINKED_TO_ACCOUNT, False) removed_printers_message.show() From 167a4c1f5823fd757d0453f2e365a0805030721a Mon Sep 17 00:00:00 2001 From: Kostas Karmas Date: Fri, 5 Jun 2020 11:25:44 +0200 Subject: [PATCH 5/6] Make reported_device_ids a class variable So that the actions that can be performed by the message buttons can properly access the list and take the necessary steps to achieve their purpose. CURA-7438 --- .../src/Cloud/CloudOutputDeviceManager.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index c176873284..2b3e6e3a60 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -260,8 +260,10 @@ class CloudOutputDeviceManager: for device_id in removed_device_ids: if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(self.META_LINKED_TO_ACCOUNT, "true")): ignored_device_ids.add(device_id) - report_device_ids = removed_device_ids - ignored_device_ids - if len(report_device_ids) == 0: + # Keep the reported_device_ids list in a class variable, so that the message button actions can access it and + # take the necessary steps to fulfill their purpose. + self.reported_device_ids = removed_device_ids - ignored_device_ids + if len(self.reported_device_ids) == 0: return # Generate message @@ -270,16 +272,16 @@ class CloudOutputDeviceManager: "info:status", "Cloud connection is not available for a printer", "Cloud connection is not available for some printers", - len(report_device_ids) + len(self.reported_device_ids) ), lifetime = 0 ) - device_names = "\n".join(["
  • {} ({})
  • ".format(self._um_cloud_printers[device].name, self._um_cloud_printers[device].definition.name) for device in report_device_ids]) + device_names = "\n".join(["
  • {} ({})
  • ".format(self._um_cloud_printers[device].name, self._um_cloud_printers[device].definition.name) for device in self.reported_device_ids]) message_text = self.I18N_CATALOG.i18ncp( "info:status", "The following cloud printer is not linked to your account:\n", "The following cloud printers are not linked to your account:\n", - len(report_device_ids) + len(self.reported_device_ids) ) message_text += self.I18N_CATALOG.i18nc( "info:status", From 1024879f9d871330104ae1e861e86fe601eb2fdd Mon Sep 17 00:00:00 2001 From: Nino van Hooff Date: Tue, 9 Jun 2020 13:41:30 +0200 Subject: [PATCH 6/6] Process Review feedback for CURA-7438 Renames UltimakerCloudAuthentication to UltimakerCloudConstants Cura-7438 --- cura/API/Account.py | 4 ++-- cura/API/ConnectionStatus.py | 4 ++-- cura/CuraApplication.py | 6 +++--- cura/Settings/MachineManager.py | 3 ++- ...tication.py => UltimakerCloudConstants.py} | 4 ++++ plugins/CuraDrive/src/Settings.py | 4 ++-- plugins/Toolbox/src/CloudApiModel.py | 6 +++--- .../src/Cloud/CloudApiClient.py | 4 ++-- .../src/Cloud/CloudOutputDeviceManager.py | 21 ++++++++++--------- 9 files changed, 31 insertions(+), 25 deletions(-) rename cura/UltimakerCloud/{UltimakerCloudAuthentication.py => UltimakerCloudConstants.py} (87%) diff --git a/cura/API/Account.py b/cura/API/Account.py index ef46368474..1250d8b81a 100644 --- a/cura/API/Account.py +++ b/cura/API/Account.py @@ -10,7 +10,7 @@ from UM.Message import Message from UM.i18n import i18nCatalog from cura.OAuth2.AuthorizationService import AuthorizationService from cura.OAuth2.Models import OAuth2Settings -from cura.UltimakerCloud import UltimakerCloudAuthentication +from cura.UltimakerCloud import UltimakerCloudConstants if TYPE_CHECKING: from cura.CuraApplication import CuraApplication @@ -69,7 +69,7 @@ class Account(QObject): self._last_sync_str = "-" self._callback_port = 32118 - self._oauth_root = UltimakerCloudAuthentication.CuraCloudAccountAPIRoot + self._oauth_root = UltimakerCloudConstants.CuraCloudAccountAPIRoot self._oauth_settings = OAuth2Settings( OAUTH_SERVER_URL= self._oauth_root, diff --git a/cura/API/ConnectionStatus.py b/cura/API/ConnectionStatus.py index 332e519ca9..007f03fdd1 100644 --- a/cura/API/ConnectionStatus.py +++ b/cura/API/ConnectionStatus.py @@ -4,14 +4,14 @@ from PyQt5.QtCore import QObject, pyqtSignal, QTimer, pyqtProperty from PyQt5.QtNetwork import QNetworkReply from UM.TaskManagement.HttpRequestManager import HttpRequestManager -from cura.UltimakerCloud import UltimakerCloudAuthentication +from cura.UltimakerCloud import UltimakerCloudConstants class ConnectionStatus(QObject): """Status info for some web services""" UPDATE_INTERVAL = 10.0 # seconds - ULTIMAKER_CLOUD_STATUS_URL = UltimakerCloudAuthentication.CuraCloudAPIRoot + "/connect/v1/" + ULTIMAKER_CLOUD_STATUS_URL = UltimakerCloudConstants.CuraCloudAPIRoot + "/connect/v1/" __instance = None # type: Optional[ConnectionStatus] diff --git a/cura/CuraApplication.py b/cura/CuraApplication.py index 26b3e9c3f7..aace3f49a5 100755 --- a/cura/CuraApplication.py +++ b/cura/CuraApplication.py @@ -106,7 +106,7 @@ from cura.UI.RecommendedMode import RecommendedMode from cura.UI.TextManager import TextManager from cura.UI.WelcomePagesModel import WelcomePagesModel from cura.UI.WhatsNewPagesModel import WhatsNewPagesModel -from cura.UltimakerCloud import UltimakerCloudAuthentication +from cura.UltimakerCloud import UltimakerCloudConstants from cura.Utils.NetworkingUtil import NetworkingUtil from . import BuildVolume from . import CameraAnimation @@ -255,11 +255,11 @@ class CuraApplication(QtApplication): @pyqtProperty(str, constant=True) def ultimakerCloudApiRootUrl(self) -> str: - return UltimakerCloudAuthentication.CuraCloudAPIRoot + return UltimakerCloudConstants.CuraCloudAPIRoot @pyqtProperty(str, constant = True) def ultimakerCloudAccountRootUrl(self) -> str: - return UltimakerCloudAuthentication.CuraCloudAccountAPIRoot + return UltimakerCloudConstants.CuraCloudAccountAPIRoot def addCommandLineOptions(self): """Adds command line options to the command line parser. diff --git a/cura/Settings/MachineManager.py b/cura/Settings/MachineManager.py index 060df10559..e5eec988e7 100755 --- a/cura/Settings/MachineManager.py +++ b/cura/Settings/MachineManager.py @@ -38,6 +38,7 @@ from cura.Settings.ExtruderStack import ExtruderStack from cura.Settings.cura_empty_instance_containers import (empty_definition_changes_container, empty_variant_container, empty_material_container, empty_quality_container, empty_quality_changes_container, empty_intent_container) +from cura.UltimakerCloud.UltimakerCloudConstants import META_UM_LINKED_TO_ACCOUNT from .CuraStackBuilder import CuraStackBuilder @@ -493,7 +494,7 @@ class MachineManager(QObject): @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineIsLinkedToCurrentAccount(self) -> bool: - return parseBool(self.activeMachine.getMetaDataEntry("linked_to_account", "True")) + return parseBool(self.activeMachine.getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "True")) @pyqtProperty(bool, notify = printerConnectedStatusChanged) def activeMachineHasNetworkConnection(self) -> bool: diff --git a/cura/UltimakerCloud/UltimakerCloudAuthentication.py b/cura/UltimakerCloud/UltimakerCloudConstants.py similarity index 87% rename from cura/UltimakerCloud/UltimakerCloudAuthentication.py rename to cura/UltimakerCloud/UltimakerCloudConstants.py index c8346e5c4e..624d2e7b8f 100644 --- a/cura/UltimakerCloud/UltimakerCloudAuthentication.py +++ b/cura/UltimakerCloud/UltimakerCloudConstants.py @@ -8,6 +8,10 @@ DEFAULT_CLOUD_API_ROOT = "https://api.ultimaker.com" # type: str DEFAULT_CLOUD_API_VERSION = "1" # type: str DEFAULT_CLOUD_ACCOUNT_API_ROOT = "https://account.ultimaker.com" # type: str +# Container Metadata keys +META_UM_LINKED_TO_ACCOUNT = "um_linked_to_account" +"""(bool) Whether a cloud printer is linked to an Ultimaker account""" + try: from cura.CuraVersion import CuraCloudAPIRoot # type: ignore if CuraCloudAPIRoot == "": diff --git a/plugins/CuraDrive/src/Settings.py b/plugins/CuraDrive/src/Settings.py index 639c63b45f..56158922dc 100644 --- a/plugins/CuraDrive/src/Settings.py +++ b/plugins/CuraDrive/src/Settings.py @@ -1,13 +1,13 @@ # Copyright (c) 2018 Ultimaker B.V. # Cura is released under the terms of the LGPLv3 or higher. -from cura.UltimakerCloud import UltimakerCloudAuthentication +from cura.UltimakerCloud import UltimakerCloudConstants class Settings: # Keeps the plugin settings. DRIVE_API_VERSION = 1 - DRIVE_API_URL = "{}/cura-drive/v{}".format(UltimakerCloudAuthentication.CuraCloudAPIRoot, str(DRIVE_API_VERSION)) + DRIVE_API_URL = "{}/cura-drive/v{}".format(UltimakerCloudConstants.CuraCloudAPIRoot, str(DRIVE_API_VERSION)) AUTO_BACKUP_ENABLED_PREFERENCE_KEY = "cura_drive/auto_backup_enabled" AUTO_BACKUP_LAST_DATE_PREFERENCE_KEY = "cura_drive/auto_backup_date" diff --git a/plugins/Toolbox/src/CloudApiModel.py b/plugins/Toolbox/src/CloudApiModel.py index b4ff00c6cd..bef37d8173 100644 --- a/plugins/Toolbox/src/CloudApiModel.py +++ b/plugins/Toolbox/src/CloudApiModel.py @@ -1,13 +1,13 @@ from typing import Union from cura import ApplicationMetadata -from cura.UltimakerCloud import UltimakerCloudAuthentication +from cura.UltimakerCloud import UltimakerCloudConstants class CloudApiModel: sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int] - cloud_api_version = UltimakerCloudAuthentication.CuraCloudAPIVersion # type: str - cloud_api_root = UltimakerCloudAuthentication.CuraCloudAPIRoot # type: str + cloud_api_version = UltimakerCloudConstants.CuraCloudAPIVersion # type: str + cloud_api_root = UltimakerCloudConstants.CuraCloudAPIRoot # type: str api_url = "{cloud_api_root}/cura-packages/v{cloud_api_version}/cura/v{sdk_version}".format( cloud_api_root = cloud_api_root, cloud_api_version = cloud_api_version, diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py index ab14d7ff06..713ee25170 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudApiClient.py @@ -13,7 +13,7 @@ from UM.TaskManagement.HttpRequestManager import HttpRequestManager from UM.TaskManagement.HttpRequestScope import JsonDecoratorScope from cura.API import Account from cura.CuraApplication import CuraApplication -from cura.UltimakerCloud import UltimakerCloudAuthentication +from cura.UltimakerCloud import UltimakerCloudConstants from cura.UltimakerCloud.UltimakerCloudScope import UltimakerCloudScope from .ToolPathUploader import ToolPathUploader from ..Models.BaseModel import BaseModel @@ -35,7 +35,7 @@ class CloudApiClient: """ # The cloud URL to use for this remote cluster. - ROOT_PATH = UltimakerCloudAuthentication.CuraCloudAPIRoot + ROOT_PATH = UltimakerCloudConstants.CuraCloudAPIRoot CLUSTER_API_ROOT = "{}/connect/v1".format(ROOT_PATH) CURA_API_ROOT = "{}/cura/v1".format(ROOT_PATH) diff --git a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py index 2b3e6e3a60..5341c3c4b9 100644 --- a/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py +++ b/plugins/UM3NetworkPrinting/src/Cloud/CloudOutputDeviceManager.py @@ -3,7 +3,6 @@ import os from typing import Dict, List, Optional, Set -from PyQt5.QtCore import QTimer from PyQt5.QtNetwork import QNetworkReply from UM import i18nCatalog @@ -17,6 +16,7 @@ from cura.API.Account import SyncState from cura.CuraApplication import CuraApplication from cura.Settings.CuraStackBuilder import CuraStackBuilder from cura.Settings.GlobalStack import GlobalStack +from cura.UltimakerCloud.UltimakerCloudConstants import META_UM_LINKED_TO_ACCOUNT from .CloudApiClient import CloudApiClient from .CloudOutputDevice import CloudOutputDevice from ..Models.Http.CloudClusterResponse import CloudClusterResponse @@ -31,8 +31,8 @@ class CloudOutputDeviceManager: META_CLUSTER_ID = "um_cloud_cluster_id" META_NETWORK_KEY = "um_network_key" + SYNC_SERVICE_NAME = "CloudOutputDeviceManager" - META_LINKED_TO_ACCOUNT = "linked_to_account" # The translation catalog for this device. I18N_CATALOG = i18nCatalog("cura") @@ -118,8 +118,8 @@ class CloudOutputDeviceManager: for device_id, cluster_data in all_clusters.items(): if device_id not in self._remote_clusters: new_clusters.append(cluster_data) - if device_id in self._um_cloud_printers and not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(self.META_LINKED_TO_ACCOUNT, "true")): - self._um_cloud_printers[device_id].setMetaDataEntry(self.META_LINKED_TO_ACCOUNT, True) + if device_id in self._um_cloud_printers and not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")): + self._um_cloud_printers[device_id].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True) self._onDevicesDiscovered(new_clusters) # Remove the CloudOutput device for offline printers @@ -170,8 +170,8 @@ class CloudOutputDeviceManager: remote_clusters_added = True # If a printer that was removed from the account is re-added, change its metadata to mark it not removed # from the account - elif not parseBool(self._um_cloud_printers[device.key].getMetaDataEntry(self.META_LINKED_TO_ACCOUNT, "true")): - self._um_cloud_printers[device.key].setMetaDataEntry(self.META_LINKED_TO_ACCOUNT, True) + elif not parseBool(self._um_cloud_printers[device.key].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")): + self._um_cloud_printers[device.key].setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, True) # Inform the Cloud printers model about new devices. new_devices_list_of_dicts = [{ @@ -258,12 +258,12 @@ class CloudOutputDeviceManager: # Do not report device ids which have been previously marked as non-linked to the account ignored_device_ids = set() for device_id in removed_device_ids: - if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(self.META_LINKED_TO_ACCOUNT, "true")): + if not parseBool(self._um_cloud_printers[device_id].getMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, "true")): ignored_device_ids.add(device_id) # Keep the reported_device_ids list in a class variable, so that the message button actions can access it and # take the necessary steps to fulfill their purpose. self.reported_device_ids = removed_device_ids - ignored_device_ids - if len(self.reported_device_ids) == 0: + if not self.reported_device_ids: return # Generate message @@ -291,19 +291,20 @@ class CloudOutputDeviceManager: ) removed_printers_message.setText(message_text) + output_device_manager = CuraApplication.getInstance().getOutputDeviceManager() + # Remove the output device from the printers for device_id in removed_device_ids: device = self._um_cloud_printers.get(device_id, None) # type: Optional[GlobalStack] if not device: continue - output_device_manager = CuraApplication.getInstance().getOutputDeviceManager() if device_id in output_device_manager.getOutputDeviceIds(): output_device_manager.removeOutputDevice(device_id) if device_id in self._remote_clusters: del self._remote_clusters[device_id] # Update the printer's metadata to mark it as not linked to the account - device.setMetaDataEntry(self.META_LINKED_TO_ACCOUNT, False) + device.setMetaDataEntry(META_UM_LINKED_TO_ACCOUNT, False) removed_printers_message.show()