Also check for package updates automatically

Moves the 30-second sync timer from CloudOutputDeviceManager to
Account and subscribes CloudPackageChecker to the timer.

CURA-7290
This commit is contained in:
Nino van Hooff 2020-05-04 16:56:09 +02:00
parent 637a241d99
commit 88ff68e40c
3 changed files with 37 additions and 29 deletions

View file

@ -3,9 +3,8 @@
from datetime import datetime from datetime import datetime
from typing import Optional, Dict, TYPE_CHECKING from typing import Optional, Dict, TYPE_CHECKING
from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty from PyQt5.QtCore import QObject, pyqtSignal, pyqtSlot, pyqtProperty, QTimer
from UM.Logger import Logger
from UM.Message import Message from UM.Message import Message
from UM.i18n import i18nCatalog from UM.i18n import i18nCatalog
from cura.OAuth2.AuthorizationService import AuthorizationService from cura.OAuth2.AuthorizationService import AuthorizationService
@ -28,11 +27,14 @@ i18n_catalog = i18nCatalog("cura")
# api.account.userProfile # Who is logged in`` # api.account.userProfile # Who is logged in``
# #
class Account(QObject): class Account(QObject):
# The interval with which the remote clusters are checked
SYNC_INTERVAL = 30.0 # seconds
# Signal emitted when user logged in or out. # Signal emitted when user logged in or out.
loginStateChanged = pyqtSignal(bool) loginStateChanged = pyqtSignal(bool)
accessTokenChanged = pyqtSignal() accessTokenChanged = pyqtSignal()
cloudPrintersDetectedChanged = pyqtSignal(bool) cloudPrintersDetectedChanged = pyqtSignal(bool)
manualSyncRequested = pyqtSignal() syncRequested = pyqtSignal()
lastSyncDateTimeChanged = pyqtSignal() lastSyncDateTimeChanged = pyqtSignal()
syncStateChanged = pyqtSignal(str) syncStateChanged = pyqtSignal(str)
@ -66,6 +68,13 @@ class Account(QObject):
self._authorization_service = AuthorizationService(self._oauth_settings) self._authorization_service = AuthorizationService(self._oauth_settings)
# Create a timer for automatic account sync
self._update_timer = QTimer()
self._update_timer.setInterval(int(self.SYNC_INTERVAL * 1000))
# The timer is restarted explicitly after an update was processed. This prevents 2 concurrent updates
self._update_timer.setSingleShot(True)
self._update_timer.timeout.connect(self.syncRequested)
self._sync_clients = {} self._sync_clients = {}
"""contains entries "client_name" : "state["success"|"error|"syncing"]""" """contains entries "client_name" : "state["success"|"error|"syncing"]"""
@ -77,9 +86,9 @@ class Account(QObject):
self._authorization_service.loadAuthDataFromPreferences() self._authorization_service.loadAuthDataFromPreferences()
def setSyncState(self, service_name: str, state: str) -> None: def setSyncState(self, service_name: str, state: str) -> None:
""" Can be used to register and update account sync states """ Can be used to register sync services and update account sync states
Example: `setSyncState("packages", "syncing")` Example: `setSyncState("PluginSyncService", "syncing")`
:param service_name: A unique name for your service, such as `plugins` or `backups` :param service_name: A unique name for your service, such as `plugins` or `backups`
:param state: One of Account.SYNC_STATES :param state: One of Account.SYNC_STATES
""" """
@ -105,6 +114,11 @@ class Account(QObject):
self._last_sync_str = datetime.now().strftime("%d/%m/%Y %H:%M") self._last_sync_str = datetime.now().strftime("%d/%m/%Y %H:%M")
self.lastSyncDateTimeChanged.emit() self.lastSyncDateTimeChanged.emit()
if self._sync_state != "syncing":
# schedule new auto update after syncing completed (for whatever reason)
if not self._update_timer.isActive():
self._update_timer.start()
def _onAccessTokenChanged(self): def _onAccessTokenChanged(self):
self.accessTokenChanged.emit() self.accessTokenChanged.emit()
@ -181,7 +195,7 @@ class Account(QObject):
def sync(self) -> None: def sync(self) -> None:
"""Checks for new cloud printers""" """Checks for new cloud printers"""
self.manualSyncRequested.emit() self.syncRequested.emit()
@pyqtSlot() @pyqtSlot()
def logout(self) -> None: def logout(self) -> None:

View file

@ -35,6 +35,9 @@ class CloudPackageChecker(QObject):
self._application.initializationFinished.connect(self._onAppInitialized) self._application.initializationFinished.connect(self._onAppInitialized)
self._i18n_catalog = i18nCatalog("cura") self._i18n_catalog = i18nCatalog("cura")
self._sdk_version = ApplicationMetadata.CuraSDKVersion self._sdk_version = ApplicationMetadata.CuraSDKVersion
self._last_check_packages = []
"""Result from a previous check within the same user session.
Used to prevent duplicate notifications"""
# This is a plugin, so most of the components required are not ready when # This is a plugin, so most of the components required are not ready when
# this is initialized. Therefore, we wait until the application is ready. # this is initialized. Therefore, we wait until the application is ready.
@ -43,8 +46,13 @@ class CloudPackageChecker(QObject):
# initial check # initial check
self._getPackagesIfLoggedIn() self._getPackagesIfLoggedIn()
self._application.getCuraAPI().account.loginStateChanged.connect(self._getPackagesIfLoggedIn) self._application.getCuraAPI().account.loginStateChanged.connect(self._onLoginStateChanged)
self._application.getCuraAPI().account.manualSyncRequested.connect(self._getPackagesIfLoggedIn) self._application.getCuraAPI().account.syncRequested.connect(self._getPackagesIfLoggedIn)
def _onLoginStateChanged(self) -> None:
# reset session
self._last_check_packages = []
self._getPackagesIfLoggedIn()
def _getPackagesIfLoggedIn(self) -> None: def _getPackagesIfLoggedIn(self) -> None:
if self._application.getCuraAPI().account.isLoggedIn: if self._application.getCuraAPI().account.isLoggedIn:
@ -87,6 +95,10 @@ class CloudPackageChecker(QObject):
user_subscribed_packages = [plugin["package_id"] for plugin in subscribed_packages_payload] user_subscribed_packages = [plugin["package_id"] for plugin in subscribed_packages_payload]
user_installed_packages = self._package_manager.getUserInstalledPackages() user_installed_packages = self._package_manager.getUserInstalledPackages()
if user_subscribed_packages == self._last_check_packages:
# nothing new here
return
# We need to re-evaluate the dismissed packages # We need to re-evaluate the dismissed packages
# (i.e. some package might got updated to the correct SDK version in the meantime, # (i.e. some package might got updated to the correct SDK version in the meantime,
# hence remove them from the Dismissed Incompatible list) # hence remove them from the Dismissed Incompatible list)
@ -102,6 +114,7 @@ class CloudPackageChecker(QObject):
self._model.addDiscrepancies(package_discrepancy) self._model.addDiscrepancies(package_discrepancy)
self._model.initialize(self._package_manager, subscribed_packages_payload) self._model.initialize(self._package_manager, subscribed_packages_payload)
self._showSyncMessage() self._showSyncMessage()
self._last_check_packages = user_subscribed_packages
def _showSyncMessage(self) -> None: def _showSyncMessage(self) -> None:
"""Show the message if it is not already shown""" """Show the message if it is not already shown"""

View file

@ -29,9 +29,6 @@ class CloudOutputDeviceManager:
META_NETWORK_KEY = "um_network_key" META_NETWORK_KEY = "um_network_key"
SYNC_SERVICE_NAME = "CloudOutputDeviceManager" SYNC_SERVICE_NAME = "CloudOutputDeviceManager"
# The interval with which the remote clusters are checked
CHECK_CLUSTER_INTERVAL = 30.0 # seconds
# The translation catalog for this device. # The translation catalog for this device.
I18N_CATALOG = i18nCatalog("cura") I18N_CATALOG = i18nCatalog("cura")
@ -45,13 +42,6 @@ class CloudOutputDeviceManager:
self._api = CloudApiClient(self._account, on_error = lambda error: Logger.log("e", str(error))) self._api = CloudApiClient(self._account, on_error = lambda error: Logger.log("e", str(error)))
self._account.loginStateChanged.connect(self._onLoginStateChanged) self._account.loginStateChanged.connect(self._onLoginStateChanged)
# Create a timer to update the remote cluster list
self._update_timer = QTimer()
self._update_timer.setInterval(int(self.CHECK_CLUSTER_INTERVAL * 1000))
# The timer is restarted explicitly after an update was processed. This prevents 2 concurrent updates
self._update_timer.setSingleShot(True)
self._update_timer.timeout.connect(self._getRemoteClusters)
# Ensure we don't start twice. # Ensure we don't start twice.
self._running = False self._running = False
@ -65,11 +55,9 @@ class CloudOutputDeviceManager:
if not self._account.isLoggedIn: if not self._account.isLoggedIn:
return return
self._running = True self._running = True
if not self._update_timer.isActive():
self._update_timer.start()
self._getRemoteClusters() self._getRemoteClusters()
self._account.manualSyncRequested.connect(self._getRemoteClusters) self._account.syncRequested.connect(self._getRemoteClusters)
def stop(self): def stop(self):
"""Stops running the cloud output device manager.""" """Stops running the cloud output device manager."""
@ -77,8 +65,6 @@ class CloudOutputDeviceManager:
if not self._running: if not self._running:
return return
self._running = False self._running = False
if self._update_timer.isActive():
self._update_timer.stop()
self._onGetRemoteClustersFinished([]) # Make sure we remove all cloud output devices. self._onGetRemoteClustersFinished([]) # Make sure we remove all cloud output devices.
def refreshConnections(self) -> None: def refreshConnections(self) -> None:
@ -100,8 +86,7 @@ class CloudOutputDeviceManager:
if self._syncing: if self._syncing:
return return
if self._update_timer.isActive(): Logger.info("Syncing cloud printer clusters")
self._update_timer.stop()
self._syncing = True self._syncing = True
self._account.setSyncState(self.SYNC_SERVICE_NAME, "syncing") self._account.setSyncState(self.SYNC_SERVICE_NAME, "syncing")
@ -135,14 +120,10 @@ class CloudOutputDeviceManager:
self._syncing = False self._syncing = False
self._account.setSyncState(self.SYNC_SERVICE_NAME, "success") self._account.setSyncState(self.SYNC_SERVICE_NAME, "success")
# Schedule a new update
self._update_timer.start()
def _onGetRemoteClusterFailed(self): def _onGetRemoteClusterFailed(self):
self._syncing = False self._syncing = False
self._account.setSyncState(self.SYNC_SERVICE_NAME, "error") self._account.setSyncState(self.SYNC_SERVICE_NAME, "error")
# Schedule a new update
self._update_timer.start()
def _onDevicesDiscovered(self, clusters: List[CloudClusterResponse]) -> None: def _onDevicesDiscovered(self, clusters: List[CloudClusterResponse]) -> None:
"""**Synchronously** create machines for discovered devices """**Synchronously** create machines for discovered devices