Always subscribe to packages after installing them

Now that we subscribe for all situations where a package is installed,
it makes sense to watch package installs and create a 1:1 relation that
way. Prevents code duplication.

CURA-7099
This commit is contained in:
Nino van Hooff 2020-01-28 13:08:57 +01:00
parent 5cedb2933e
commit d4eb463f2d
5 changed files with 48 additions and 23 deletions

View file

@ -191,8 +191,6 @@ class CuraApplication(QtApplication):
self._cura_formula_functions = None # type: Optional[CuraFormulaFunctions] self._cura_formula_functions = None # type: Optional[CuraFormulaFunctions]
self._cura_package_manager = None
self._machine_action_manager = None # type: Optional[MachineActionManager.MachineActionManager] self._machine_action_manager = None # type: Optional[MachineActionManager.MachineActionManager]
self.empty_container = None # type: EmptyInstanceContainer self.empty_container = None # type: EmptyInstanceContainer
@ -476,7 +474,7 @@ class CuraApplication(QtApplication):
"CuraEngineBackend", #Cura is useless without this one since you can't slice. "CuraEngineBackend", #Cura is useless without this one since you can't slice.
"FileLogger", #You want to be able to read the log if something goes wrong. "FileLogger", #You want to be able to read the log if something goes wrong.
"XmlMaterialProfile", #Cura crashes without this one. "XmlMaterialProfile", #Cura crashes without this one.
"Toolbox", #This contains the interface to enable/disable plug-ins, so if you disable it you can't enable it back. "Toolbox", #This contains the interface to enable/disable plug-ins and the Cloud functionality.
"PrepareStage", #Cura is useless without this one since you can't load models. "PrepareStage", #Cura is useless without this one since you can't load models.
"PreviewStage", #This shows the list of the plugin views that are installed in Cura. "PreviewStage", #This shows the list of the plugin views that are installed in Cura.
"MonitorStage", #Major part of Cura's functionality. "MonitorStage", #Major part of Cura's functionality.
@ -632,6 +630,12 @@ class CuraApplication(QtApplication):
def showPreferences(self) -> None: def showPreferences(self) -> None:
self.showPreferencesWindow.emit() self.showPreferencesWindow.emit()
# This is called by drag-and-dropping curapackage files.
@pyqtSlot(QUrl)
def installPackageViaDragAndDrop(self, file_url: str) -> Optional[str]:
filename = QUrl(file_url).toLocalFile()
return self._package_manager.installPackage(filename)
@override(Application) @override(Application)
def getGlobalContainerStack(self) -> Optional["GlobalStack"]: def getGlobalContainerStack(self) -> Optional["GlobalStack"]:
return self._global_container_stack return self._global_container_stack

View file

@ -1,23 +1,53 @@
from UM.Logger import Logger
from UM.TaskManagement.HttpRequestManager import HttpRequestManager
from cura.API import Account
from cura.CuraApplication import CuraApplication from cura.CuraApplication import CuraApplication
from ..CloudApiModel import CloudApiModel from ..CloudApiModel import CloudApiModel
from ..UltimakerCloudScope import UltimakerCloudScope from ..UltimakerCloudScope import UltimakerCloudScope
## Manages Cloud subscriptions. When a package is added to a user's account, the user is 'subscribed' to that package
# Whenever the user logs in on another instance of Cura, these subscriptions can be used to sync the user's plugins
class CloudPackageManager: class CloudPackageManager:
"""Manages Cloud subscriptions
When a package is added to a user's account, the user is 'subscribed' to that package.
Whenever the user logs in on another instance of Cura, these subscriptions can be used to sync the user's plugins
Singleton: use CloudPackageManager.getInstance() instead of CloudPackageManager()
"""
__instance = None
@classmethod
def getInstance(cls, app: CuraApplication):
if not cls.__instance:
cls.__instance = CloudPackageManager(app)
return cls.__instance
def __init__(self, app: CuraApplication) -> None: def __init__(self, app: CuraApplication) -> None:
self._request_manager = app.getHttpRequestManager() if self.__instance is not None:
self._scope = UltimakerCloudScope(app) raise Exception("This is a Singleton. use getInstance()")
self._request_manager = app.getHttpRequestManager() # type: HttpRequestManager
self.account = app.getCuraAPI().account # type: Account
self._scope = UltimakerCloudScope(app) # type: UltimakerCloudScope
app.getPackageManager().packageInstalled.connect(self._onPackageInstalled)
def unsubscribe(self, package_id: str) -> None:
url = CloudApiModel.userPackageUrl(package_id)
self._request_manager.delete(url=url, scope=self._scope)
def subscribe(self, package_id: str) -> None: def subscribe(self, package_id: str) -> None:
"""You probably don't want to use this directly. All installed packages will be automatically subscribed."""
Logger.debug("Subscribing to {}", package_id)
data = "{\"data\": {\"package_id\": \"%s\", \"sdk_version\": \"%s\"}}" % (package_id, CloudApiModel.sdk_version) data = "{\"data\": {\"package_id\": \"%s\", \"sdk_version\": \"%s\"}}" % (package_id, CloudApiModel.sdk_version)
self._request_manager.put(url=CloudApiModel.api_url_user_packages, self._request_manager.put(url=CloudApiModel.api_url_user_packages,
data=data.encode(), data=data.encode(),
scope=self._scope scope=self._scope
) )
def unsubscribe(self, package_id: str) -> None: def _onPackageInstalled(self, package_id: str):
url = CloudApiModel.userPackageUrl(package_id) if self.account.isLoggedIn:
self._request_manager.delete(url=url, scope=self._scope) # We might already be subscribed, but checking would take one extra request. Instead, simply subscribe
self.subscribe(package_id)

View file

@ -38,7 +38,8 @@ class SyncOrchestrator(Extension):
self._name = "SyncOrchestrator" self._name = "SyncOrchestrator"
self._package_manager = app.getPackageManager() self._package_manager = app.getPackageManager()
self._cloud_package_manager = CloudPackageManager(app) # Keep a reference to the CloudPackageManager. it watches for installed packages and subscribes to them
self._cloud_package_manager = CloudPackageManager.getInstance(app) # type: CloudPackageManager
self._checker = CloudPackageChecker(app) # type: CloudPackageChecker self._checker = CloudPackageChecker(app) # type: CloudPackageChecker
self._checker.discrepancies.connect(self._onDiscrepancies) self._checker.discrepancies.connect(self._onDiscrepancies)
@ -86,7 +87,6 @@ class SyncOrchestrator(Extension):
message = "Could not install {}".format(item["package_id"]) message = "Could not install {}".format(item["package_id"])
self._showErrorMessage(message) self._showErrorMessage(message)
continue continue
self._cloud_package_manager.subscribe(item["package_id"])
has_changes = True has_changes = True
else: else:
self._cloud_package_manager.unsubscribe(item["package_id"]) self._cloud_package_manager.unsubscribe(item["package_id"])

View file

@ -21,7 +21,6 @@ from cura.Machines.ContainerTree import ContainerTree
from .CloudApiModel import CloudApiModel from .CloudApiModel import CloudApiModel
from .AuthorsModel import AuthorsModel from .AuthorsModel import AuthorsModel
from .CloudSync.CloudPackageManager import CloudPackageManager
from .CloudSync.LicenseModel import LicenseModel from .CloudSync.LicenseModel import LicenseModel
from .PackagesModel import PackagesModel from .PackagesModel import PackagesModel
from .UltimakerCloudScope import UltimakerCloudScope from .UltimakerCloudScope import UltimakerCloudScope
@ -44,7 +43,6 @@ class Toolbox(QObject, Extension):
self._sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int] self._sdk_version = ApplicationMetadata.CuraSDKVersion # type: Union[str, int]
# Network: # Network:
self._cloud_package_manager = CloudPackageManager(application) # type: CloudPackageManager
self._download_request_data = None # type: Optional[HttpRequestData] self._download_request_data = None # type: Optional[HttpRequestData]
self._download_progress = 0 # type: float self._download_progress = 0 # type: float
self._is_downloading = False # type: bool self._is_downloading = False # type: bool
@ -147,11 +145,6 @@ class Toolbox(QObject, Extension):
self._application.getHttpRequestManager().put(url, data = data.encode(), scope = self._scope) self._application.getHttpRequestManager().put(url, data = data.encode(), scope = self._scope)
@pyqtSlot(str)
def subscribe(self, package_id: str) -> None:
if self._application.getCuraAPI().account.isLoggedIn:
self._cloud_package_manager.subscribe(package_id)
def getLicenseDialogPluginFileLocation(self) -> str: def getLicenseDialogPluginFileLocation(self) -> str:
return self._license_dialog_plugin_file_location return self._license_dialog_plugin_file_location
@ -378,7 +371,6 @@ class Toolbox(QObject, Extension):
def onLicenseAccepted(self): def onLicenseAccepted(self):
self.closeLicenseDialog.emit() self.closeLicenseDialog.emit()
package_id = self.install(self.getLicenseDialogPluginFileLocation()) package_id = self.install(self.getLicenseDialogPluginFileLocation())
self.subscribe(package_id)
@pyqtSlot() @pyqtSlot()
@ -682,7 +674,6 @@ class Toolbox(QObject, Extension):
installed_id = self.install(file_path) installed_id = self.install(file_path)
if installed_id != package_id: if installed_id != package_id:
Logger.error("Installed package {} does not match {}".format(installed_id, package_id)) Logger.error("Installed package {} does not match {}".format(installed_id, package_id))
self.subscribe(installed_id)
# Getter & Setters for Properties: # Getter & Setters for Properties:
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------

View file

@ -238,7 +238,7 @@ UM.MainWindow
if (filename.toLowerCase().endsWith(".curapackage")) if (filename.toLowerCase().endsWith(".curapackage"))
{ {
// Try to install plugin & close. // Try to install plugin & close.
CuraApplication.getPackageManager().installPackageViaDragAndDrop(filename); CuraApplication.installPackageViaDragAndDrop(filename);
packageInstallDialog.text = catalog.i18nc("@label", "This package will be installed after restarting."); packageInstallDialog.text = catalog.i18nc("@label", "This package will be installed after restarting.");
packageInstallDialog.icon = StandardIcon.Information; packageInstallDialog.icon = StandardIcon.Information;
packageInstallDialog.open(); packageInstallDialog.open();