From 7d31dbf7bceb96bf0da4846b4c9b7f137d4011ec Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 5 Oct 2010 22:10:45 -0500 Subject: [PATCH] Implementing caches for a session --- src/backends/gv_backend.py | 7 +++- src/constants.py | 2 +- src/dialcentral_qt.py | 28 +++++++------ src/session.py | 97 ++++++++++++++++++++++++++++++++++++++++---- src/util/qore_utils.py | 2 +- 5 files changed, 112 insertions(+), 24 deletions(-) diff --git a/src/backends/gv_backend.py b/src/backends/gv_backend.py index 012be7e..e820ffb 100644 --- a/src/backends/gv_backend.py +++ b/src/backends/gv_backend.py @@ -65,6 +65,9 @@ class GVDialer(object): def logout(self): return self._gvoice.logout() + def persist(self): + return self._gvoice.persist() + def is_dnd(self): return self._gvoice.is_dnd() @@ -160,10 +163,10 @@ class GVDialer(object): conversations = itertools.chain(voicemails, smss) for conversation in conversations: messages = conversation.messages - messageParts = ( + messageParts = [ (message.whoFrom, self._format_message(message), message.when) for message in messages - ) + ] messageDetails = { "id": conversation.id, diff --git a/src/constants.py b/src/constants.py index 4755b74..ea28584 100644 --- a/src/constants.py +++ b/src/constants.py @@ -2,7 +2,7 @@ import os __pretty_app_name__ = "DialCentral" __app_name__ = "dialcentral" -__version__ = "1.1.11" +__version__ = "1.2.0" __build__ = 1 __app_magic__ = 0xdeadbeef _data_path_ = os.path.join(os.path.expanduser("~"), ".%s" % __app_name__) diff --git a/src/dialcentral_qt.py b/src/dialcentral_qt.py index 5d95ebc..44024f0 100755 --- a/src/dialcentral_qt.py +++ b/src/dialcentral_qt.py @@ -88,6 +88,7 @@ class Dialcentral(object): } with open(constants._user_settings_, "w") as settingsFile: simplejson.dump(settings, settingsFile) + self._mainWindow.destroy() @property def fullscreenAction(self): @@ -491,9 +492,9 @@ class DelayedWidget(object): if self._child is not None: self._child.clear() - def refresh(self): + def refresh(self, force=True): if self._child is not None: - self._child.refresh() + self._child.refresh(force) class Dialpad(object): @@ -576,7 +577,7 @@ class Dialpad(object): def clear(self): pass - def refresh(self): + def refresh(self, force = True): pass def _generate_key_button(self, center, letters): @@ -711,8 +712,8 @@ class History(object): def clear(self): self._itemView.clear() - def refresh(self): - self._session.update_history() + def refresh(self, force=True): + self._session.update_history(force) def _populate_items(self): self._itemStore.clear() @@ -862,8 +863,8 @@ class Messages(object): def clear(self): self._itemView.clear() - def refresh(self): - self._session.update_messages() + def refresh(self, force=True): + self._session.update_messages(force) def _populate_items(self): self._itemStore.clear() @@ -1012,8 +1013,8 @@ class Contacts(object): def clear(self): self._itemView.clear() - def refresh(self): - self._session.update_contacts() + def refresh(self, force=True): + self._session.update_contacts(force) def _populate_items(self): self._itemStore.clear() @@ -1110,7 +1111,7 @@ class MainWindow(object): def __init__(self, parent, app): self._fsContactsPath = os.path.join(constants._data_path_, "contacts") self._app = app - self._session = session.Session() + self._session = session.Session(constants._data_path_) self._session.error.connect(self._on_session_error) self._session.loggedIn.connect(self._on_login) self._session.loggedOut.connect(self._on_logout) @@ -1223,6 +1224,9 @@ class MainWindow(object): child.close() self._window.close() + def destroy(self): + self._session.logout() + def set_fullscreen(self, isFullscreen): if isFullscreen: self._window.showFullScreen() @@ -1236,7 +1240,7 @@ class MainWindow(object): if not self._tabsContents[index].has_child(): tab = self._TAB_CLASS[index](self._app, self._session, self._errorLog) self._tabsContents[index].set_child(tab) - self._tabsContents[index].refresh() + self._tabsContents[index].refresh(force=False) @QtCore.pyqtSlot(str) @misc_utils.log_exception(_moduleLogger) @@ -1284,7 +1288,7 @@ class MainWindow(object): @misc_utils.log_exception(_moduleLogger) def _on_refresh(self, checked = True): index = self._tabWidget.currentIndex() - self._tabsContents[index].refresh() + self._tabsContents[index].refresh(force=True) @QtCore.pyqtSlot() @QtCore.pyqtSlot(bool) diff --git a/src/session.py b/src/session.py index 17c32b5..ae98cdd 100644 --- a/src/session.py +++ b/src/session.py @@ -2,11 +2,19 @@ import os import time import logging +try: + import cPickle + pickle = cPickle +except ImportError: + import pickle + from PyQt4 import QtCore from util import qore_utils from util import concurrent +from util import misc as misc_utils +import constants from backends import gv_backend @@ -144,6 +152,8 @@ class Session(QtCore.QObject): LOGGINGIN_STATE = "logging in" LOGGEDIN_STATE = "logged in" + _OLDEST_COMPATIBLE_FORMAT_VERSION = misc_utils.parse_version("1.2.0") + _LOGGEDOUT_TIME = -1 _LOGGINGIN_TIME = 0 @@ -209,21 +219,27 @@ class Session(QtCore.QObject): self._loggedInTime = self._LOGGEDOUT_TIME self.clear() - def update_contacts(self): + def update_contacts(self, force = True): + if not force and self._contacts: + return le = concurrent.AsyncLinearExecution(self._pool, self._update_contacts) self._perform_op_while_loggedin(le) def get_contacts(self): return self._contacts - def update_messages(self): + def update_messages(self, force = True): + if not force and self._messages: + return le = concurrent.AsyncLinearExecution(self._pool, self._update_messages) self._perform_op_while_loggedin(le) def get_messages(self): return self._messages - def update_history(self): + def update_history(self, force = True): + if not force and self._history: + return le = concurrent.AsyncLinearExecution(self._pool, self._update_history) self._perform_op_while_loggedin(le) @@ -319,8 +335,13 @@ class Session(QtCore.QObject): finalState = self.LOGGEDIN_STATE self.loggedIn.emit() if oldUsername != self._username: - self._load_from_cache() - loginOps = self._loginOps[:] + needOps = not self._load() + else: + needOps = True + if needOps: + loginOps = self._loginOps[:] + else: + loginOps = [] del self._loginOps[:] for asyncOp in loginOps: asyncOp.start() @@ -329,7 +350,7 @@ class Session(QtCore.QObject): finally: self.stateChange.emit(finalState) - def _load_from_cache(self): + def _load(self): updateContacts = len(self._contacts) != 0 updateMessages = len(self._messages) != 0 updateHistory = len(self._history) != 0 @@ -342,6 +363,12 @@ class Session(QtCore.QObject): self._dnd = False self._callback = "" + loadedFromCache = self._load_from_cache() + if loadedFromCache: + updateContacts = True + updateMessages = True + updateHistory = True + if updateContacts: self.contactsUpdated.emit() if updateMessages: @@ -353,9 +380,63 @@ class Session(QtCore.QObject): if oldCallback != self._callback: self.callbackNumberChanged.emit(self._callback) + return loadedFromCache + + def _load_from_cache(self): + if self._cachePath is None: + return False + cachePath = os.path.join(self._cachePath, "%s.cache" % self._username) + + try: + with open(cachePath, "rb") as f: + dumpedData = pickle.load(f) + except (pickle.PickleError, IOError, EOFError, ValueError): + _moduleLogger.exception("Pickle fun loading") + return False + except: + _moduleLogger.exception("Weirdness loading") + return False + + ( + version, build, + contacts, messages, history, dnd, callback + ) = dumpedData + + if misc_utils.compare_versions( + self._OLDEST_COMPATIBLE_FORMAT_VERSION, + misc_utils.parse_version(version), + ) <= 0: + _moduleLogger.info("Loaded cache") + self._contacts = contacts + self._messages = messages + self._history = history + self._dnd = dnd + self._callback = callback + return True + else: + _moduleLogger.debug( + "Skipping cache due to version mismatch (%s-%s)" % ( + version, build + ) + ) + return False + def _save_to_cache(self): - # @todo - pass + _moduleLogger.info("Saving cache") + if self._cachePath is None: + return + cachePath = os.path.join(self._cachePath, "%s.cache" % self._username) + + try: + dataToDump = ( + constants.__version__, constants.__build__, + self._contacts, self._messages, self._history, self._dnd, self._callback + ) + with open(cachePath, "wb") as f: + pickle.dump(dataToDump, f, pickle.HIGHEST_PROTOCOL) + _moduleLogger.info("Cache saved") + except (pickle.PickleError, IOError): + _moduleLogger.exception("While saving") def _clear_cache(self): updateContacts = len(self._contacts) != 0 diff --git a/src/util/qore_utils.py b/src/util/qore_utils.py index a592d34..e000f16 100644 --- a/src/util/qore_utils.py +++ b/src/util/qore_utils.py @@ -74,7 +74,7 @@ class _WorkerThread(QtCore.QObject): @QtCore.pyqtSlot() @misc.log_exception(_moduleLogger) def _on_stop_requested(self): - self._thread.quit() + self._pool._thread.quit() class AsyncPool(QtCore.QObject): -- 1.7.9.5