# HG changeset patch # User Dirk Olmes # Date 1313891716 -7200 # Node ID 04a730f9d07dea251721d4bf7ee448c2a7227fb8 # Parent 0e73adb2dec431965d9647eafe8ca8f0fe52dbd6 move all sqlalchemy related classes to the respective sub-package. use a backend to abstract from access to the data diff -r 0e73adb2dec4 -r 04a730f9d07d Database.py --- a/Database.py Sun Aug 21 02:47:25 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,37 +0,0 @@ - -import Mapping -import sqlalchemy -import sqlalchemy.orm -import sys -import util - -# Keep the connection to the database only once. The feed updater and the GUI app will -# operate on a single engine/session but this comes in handy for interactive use -engine = None -SessionMaker = None - -def createSession(databaseUrl=None): - if databaseUrl is None: - databaseUrl = _getDatabaseUrl() - initEngine(databaseUrl) - Mapping.createMapping(engine) - initSessionMaker() - return SessionMaker() - -def _getDatabaseUrl(): - if len(sys.argv) < 2: - print("Usage: %s " % (sys.argv[0])) - sys.exit(1) - return sys.argv[1] - -def initEngine(databaseUrl): - global engine - if engine is None: - verbose = util.databaseLoggingEnabled() - engine = sqlalchemy.create_engine(databaseUrl, echo=verbose) - -def initSessionMaker(): - global SessionMaker - if SessionMaker is None: - SessionMaker = sqlalchemy.orm.sessionmaker(bind=engine) - \ No newline at end of file diff -r 0e73adb2dec4 -r 04a730f9d07d DisplayModel.py --- a/DisplayModel.py Sun Aug 21 02:47:25 2011 +0200 +++ b/DisplayModel.py Sun Aug 21 03:55:16 2011 +0200 @@ -2,19 +2,25 @@ from PyQt4.QtCore import QAbstractListModel, QModelIndex, QVariant, Qt class DisplayModel(QAbstractListModel): - def __init__(self, parent=None, list=None, displayFunction=None, **args): + def __init__(self, parent=None, list=None, displayAttribute=None, **args): QAbstractListModel.__init__(self, parent, *args) self.list = list - self.displayFunction = displayFunction - + self.displayAttribute = displayAttribute + def rowCount(self, parent=QModelIndex()): return len(self.list) - - def data(self, index, role): + + def data(self, index, role): if index.isValid() and role == Qt.DisplayRole: row = index.row() object = self.list[row] - displayString = self.displayFunction(object) + displayString = self._stringToDisplay(object) return QVariant(displayString) - else: + else: return QVariant() + + def _stringToDisplay(self, object): + if hasattr(object, self.displayAttribute): + return getattr(object, self.displayAttribute) + else: + return "invalid display attribute: " + self.displayAttribute diff -r 0e73adb2dec4 -r 04a730f9d07d Feed.py --- a/Feed.py Sun Aug 21 02:47:25 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,46 +0,0 @@ - -from datetime import datetime, timedelta -import FeedEntry - -class Feed(object): - @staticmethod - def all(session): - return session.query(Feed).order_by(Feed.title).all() - - def __init__(self, title, rss_url): - self.title = title - self.rss_url = rss_url - # default: update every 60 minutes - self.update_interval = 60 - self.incrementNextUpdateDate() - self.auto_load_entry_link = False - self.always_open_in_browser = False - - def __repr__(self): - return "" % (self.pk, self.title) - - def userPresentableString(self): - return self.title - - def entriesSortedByUpdateDate(self, hideReadEntries=False): - if hideReadEntries: - sortedEntries = self._unreadEntries() - else: - sortedEntries = list(self.entries) - sortedEntries.sort(FeedEntry.compareByUpdateDate) - return sortedEntries - - def _unreadEntries(self): - retValue = [] - for entry in self.entries: - if not entry.read: - retValue.append(entry) - return retValue - - def incrementNextUpdateDate(self): - delta = timedelta(minutes=self.update_interval) - self.next_update = datetime.now() + delta - - def markAllEntriesRead(self): - for entry in self.entries: - entry.markRead() \ No newline at end of file diff -r 0e73adb2dec4 -r 04a730f9d07d FeedEntry.py --- a/FeedEntry.py Sun Aug 21 02:47:25 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,43 +0,0 @@ - -from datetime import datetime - -def compareByUpdateDate(first, second): - return cmp(first.updated, second.updated) - -class FeedEntry(object): - @staticmethod - def findById(id, session): - result = session.query(FeedEntry).filter(FeedEntry.id == id) - return result.first() - - @staticmethod - def create(entry): - new = FeedEntry() - new.id = entry.id - new.link = entry.link - new.title = entry.title - new.updated = entry.updated_parsed - new.summary = entry.summary - return new - - def __init__(self): - self.create_timestamp = datetime.now() - self.read = 0 - - def __repr__(self): - return "" % (self.pk, self.title) - - def userPresentableString(self): - return self.title - - def toggleRead(self): - if self.read: - self.markUnread() - else: - self.markRead() - - def markRead(self): - self.read = 1 - - def markUnread(self): - self.read = 0 diff -r 0e73adb2dec4 -r 04a730f9d07d FeedList.py --- a/FeedList.py Sun Aug 21 02:47:25 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,23 +0,0 @@ - -from Feed import Feed -from FeedEntry import FeedEntry -from Preferences import Preferences -from sqlalchemy.orm import joinedload - -def getFeeds(session): - preferences = Preferences(session) - if preferences.showOnlyUnreadFeeds(): - return _getUnreadFeeds(session) - else: - return Feed.all(session) - -def _getUnreadFeeds(session): - query = session.query(FeedEntry).filter(FeedEntry.read == 0) - queryWithOptions = query.options(joinedload("feed")) - result = queryWithOptions.all() - return _collectFeeds(result) - -def _collectFeeds(feedEntries): - feeds = [entry.feed for entry in feedEntries] - uniqueFeeds = set(feeds) - return list(uniqueFeeds) diff -r 0e73adb2dec4 -r 04a730f9d07d MainWindow.py --- a/MainWindow.py Sun Aug 21 02:47:25 2011 +0200 +++ b/MainWindow.py Sun Aug 21 03:55:16 2011 +0200 @@ -1,35 +1,32 @@ -from AddFeed import AddFeed +#from AddFeed import AddFeed from DisplayModel import DisplayModel -from Feed import Feed from FeedEntryItemDelegate import FeedEntryItemDelegate from FeedEntryTableModel import FeedEntryTableModel from FeedSettings import FeedSettings -from Preferences import Preferences -from PreferencesDialog import PreferencesDialog +#from PreferencesDialog import PreferencesDialog from PyQt4.QtCore import QUrl from PyQt4.QtGui import QApplication from PyQt4.QtGui import QMainWindow from PyQt4.QtGui import QWidget from Ui_MainWindow import Ui_MainWindow -import FeedList import subprocess STATUS_MESSAGE_DISPLAY_MILLIS = 20000 class MainWindow(QMainWindow): - def __init__(self, session=None): + def __init__(self, backend=None): QWidget.__init__(self, None) - self.session = session - self.preferences = Preferences(session) + self.backend = backend + self.preferences = backend.preferences() self.ui = Ui_MainWindow() self.ui.setupUi(self) self.updateFeedList() self.initFeedEntryList() def updateFeedList(self): - self.allFeeds = FeedList.getFeeds(self.session) - feedModel = DisplayModel(self, self.allFeeds, Feed.userPresentableString) + self.allFeeds = self.backend.getFeeds() + feedModel = DisplayModel(self, self.allFeeds, "title") self.ui.feedList.setModel(feedModel) self.ui.feedList.update() @@ -145,17 +142,18 @@ self.ui.feedEntryList.doItemsLayout() def addFeed(self): - addFeed = AddFeed(self.session) - success = addFeed.exec_() - if not success: - return - - if addFeed.exception is not None: - message = "Error while adding feed: " + str(addFeed.exception) - self._updateStatusBar(message) - else: - self.session.commit() - self.updateFeedList() + pass +# addFeed = AddFeed(self.session) +# success = addFeed.exec_() +# if not success: +# return +# +# if addFeed.exception is not None: +# message = "Error while adding feed: " + str(addFeed.exception) +# self._updateStatusBar(message) +# else: +# self.session.commit() +# self.updateFeedList() def deleteFeed(self): try: @@ -167,8 +165,9 @@ self._updateStatusBar(message) def showPreferences(self): - preferences = PreferencesDialog(self.session) - preferences.exec_() + pass +# preferences = PreferencesDialog(self.session) +# preferences.exec_() def showFeedSettings(self): feedSettings = FeedSettings(self.session, self.selectedFeed) @@ -178,8 +177,7 @@ self.ui.statusbar.showMessage(message, STATUS_MESSAGE_DISPLAY_MILLIS) def close(self): - # save all uncommitted state, just in case - self.session.commit() + self.backend.dispose() QMainWindow.close(self) def copyArticleURLToClipboard(self): diff -r 0e73adb2dec4 -r 04a730f9d07d Mapping.py --- a/Mapping.py Sun Aug 21 02:47:25 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,75 +0,0 @@ - -from Feed import Feed -from FeedEntry import FeedEntry -from Preference import Preference -from sqlalchemy import Boolean -from sqlalchemy import Column -from sqlalchemy import DateTime -from sqlalchemy import ForeignKey -from sqlalchemy import Integer -from sqlalchemy import MetaData -from sqlalchemy import String -from sqlalchemy import Table -from sqlalchemy import Text -from sqlalchemy.orm import mapper -from sqlalchemy.orm import relation - -mappingDefined = False -feedEntryTable = None - -def createMapping(engine): - """ Make sure the mapping is defined only once. This is not really needed for the feed updater - or the GUI app but comes in handy when working interactively with the system. """ - global mappingDefined - if not mappingDefined: - _createMapping(engine) - mappingDefined = True - -def _createMapping(engine): - metadata = MetaData(engine) - metadata.bind = engine - - feedTable = Table("feed", metadata, - Column("pk", Integer, primary_key=True), - Column("title", String(255), nullable=False), - Column("rss_url", String(255), nullable=False), - # update interval is specified in minutes - Column("update_interval", Integer, nullable=False), - Column("next_update", DateTime, nullable=False), - # when displaying an entry of this feed, do not display the summary but rather load - # the link directly - Column("auto_load_entry_link", Boolean, nullable=False), - # this is actually a hack: when opening some sites in the QWebView it just crashes. - # This setting forces to open an entry's link in the external browser - Column("always_open_in_browser", Boolean, nullable=False) - ) - - global feedEntryTable - feedEntryTable = Table("feed_entry", metadata, - Column("pk", Integer, primary_key=True), - Column("create_timestamp", DateTime, nullable=False), - Column("read", Integer, nullable=False), - - Column("id", String(512), nullable=False), - Column("link", String(512), nullable=False), - Column("title", Text, nullable=False), - Column("summary", Text, nullable=False), - Column("updated", DateTime), - Column("feed_id", Integer, ForeignKey("feed.pk")) - ) - - preferencesTable = Table("preference", metadata, - Column("pk", Integer, primary_key=True), - Column("key", String(255), nullable=False), - Column("value", String(255), nullable=False) - ) - - metadata.create_all() - - mapper(FeedEntry, feedEntryTable) - mapper(Feed, feedTable, - properties = { - "entries" : relation(FeedEntry, backref="feed", lazy=True, cascade="delete, delete-orphan") - } - ) - mapper(Preference, preferencesTable) diff -r 0e73adb2dec4 -r 04a730f9d07d Preference.py --- a/Preference.py Sun Aug 21 02:47:25 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,12 +0,0 @@ - -class Preference(object): - @staticmethod - def forKey(key, session): - return session.query(Preference).filter(Preference.key == key).first() - - def __init__(self, key, value): - self.key = key - self.value = value - - def __repr__(self): - return "" % (self.key, self.value) diff -r 0e73adb2dec4 -r 04a730f9d07d Preferences.py --- a/Preferences.py Sun Aug 21 02:47:25 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,92 +0,0 @@ - -from Preference import Preference -import util - -DAYS_TO_KEEP_FEED_ENTRIES = "DAYS_TO_KEEP_FEED_ENTRIES" -HIDE_READ_ENTRIES = "HIDE_READ_FEED_ENTRIES" -PROXY_HOST = "PROXY_HOST" -PROXY_PORT = "PROXY_PORT" -SHOW_ONLY_UNREAD_FEEDS = "SHOW_ONLY_UNREAD_FEEDS" -START_MAXIMIZED = "START_MAXIMIZED" - -class Preferences(object): - def __init__(self, session): - self.session = session - self.cache = {} - - def _cachedPreference(self, key, defaultValue=None, addIfMissing=True): - if self.cache.has_key(key): - return self.cache[key] - else: - pref = Preference.forKey(key, self.session) - if pref is not None: - self.cache[key] = pref - elif pref is None and addIfMissing: - pref = Preference(key, str(defaultValue)) - self.session.add(pref) - self.cache[key] = pref - return pref - - def startMaximized(self): - pref = self._cachedPreference(START_MAXIMIZED, False) - return util.str2bool(pref.value) - - def setStartMaximized(self, flag): - pref = self._cachedPreference(START_MAXIMIZED) - pref.value = util.bool2str(flag) - - def hideReadFeedEntries(self): - pref = self._cachedPreference(HIDE_READ_ENTRIES, False) - return util.str2bool(pref.value) - - def setHideReadFeedEntries(self, flag): - pref = self._cachedPreference(HIDE_READ_ENTRIES) - pref.value = util.bool2str(flag) - - def showOnlyUnreadFeeds(self): - pref = self._cachedPreference(SHOW_ONLY_UNREAD_FEEDS, False) - return util.str2bool(pref.value) - - def setShowOnlyUnreadFeeds(self, flag): - pref = self._cachedPreference(SHOW_ONLY_UNREAD_FEEDS) - pref.value = util.bool2str(flag) - - def proxyHost(self): - pref = self._cachedPreference(PROXY_HOST) - return pref.value - - def setProxyHost(self, hostname): - if hostname is None: - pref = self._cachedPreference(PROXY_HOST, addIfMissing=False) - if pref is not None: - self.session.delete(pref) - del(self.cache[PROXY_HOST]) - else: - pref = self._cachedPreference(PROXY_HOST) - pref.value = str(hostname) - - def proxyPort(self): - pref = self._cachedPreference(PROXY_PORT, 3128) - return int(pref.value) - - def setProxyPort(self, port): - if port is None: - pref = self._cachedPreference(PROXY_PORT, addIfMissing=False) - if pref is not None: - self.session.delete(pref) - del(self.cache[PROXY_PORT]) - else: - pref = self._cachedPreference(PROXY_PORT) - pref.value = str(port) - - def isProxyConfigured(self): - pref = self._cachedPreference(PROXY_HOST, addIfMissing=False) - return pref is not None - - def daysToKeepFeedEntries(self): - pref = self._cachedPreference(DAYS_TO_KEEP_FEED_ENTRIES, 90, addIfMissing=True) - return int(pref.value) - - def setDaysToKeepFeedEntries(self, dayString): - pref = self._cachedPreference(DAYS_TO_KEEP_FEED_ENTRIES) - pref.value = dayString diff -r 0e73adb2dec4 -r 04a730f9d07d backend/__init__.py diff -r 0e73adb2dec4 -r 04a730f9d07d backend/sqlalchemy/Database.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/backend/sqlalchemy/Database.py Sun Aug 21 03:55:16 2011 +0200 @@ -0,0 +1,36 @@ + +from sqlalchemy.engine import create_engine +from sqlalchemy.orm import sessionmaker +import Mapping +import sys +import util + +# Keep the connection to the database only once. The feed updater and the GUI app will +# operate on a single engine/session but this comes in handy for interactive use +engine = None +SessionMaker = None + +def createSession(databaseUrl=None): + if databaseUrl is None: + databaseUrl = _getDatabaseUrl() + initEngine(databaseUrl) + Mapping.createMapping(engine) + initSessionMaker() + return SessionMaker() + +def _getDatabaseUrl(): + if len(sys.argv) < 2: + print("Usage: %s " % (sys.argv[0])) + sys.exit(1) + return sys.argv[1] + +def initEngine(databaseUrl): + global engine + if engine is None: + verbose = util.databaseLoggingEnabled() + engine = create_engine(databaseUrl, echo=verbose) + +def initSessionMaker(): + global SessionMaker + if SessionMaker is None: + SessionMaker = sessionmaker(bind=engine) diff -r 0e73adb2dec4 -r 04a730f9d07d backend/sqlalchemy/Feed.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/backend/sqlalchemy/Feed.py Sun Aug 21 03:55:16 2011 +0200 @@ -0,0 +1,46 @@ + +from datetime import datetime, timedelta +import FeedEntry + +class Feed(object): + @staticmethod + def all(session): + return session.query(Feed).order_by(Feed.title).all() + + def __init__(self, title, rss_url): + self.title = title + self.rss_url = rss_url + # default: update every 60 minutes + self.update_interval = 60 + self.incrementNextUpdateDate() + self.auto_load_entry_link = False + self.always_open_in_browser = False + + def __repr__(self): + return "" % (self.pk, self.title) + + def userPresentableString(self): + return self.title + + def entriesSortedByUpdateDate(self, hideReadEntries=False): + if hideReadEntries: + sortedEntries = self._unreadEntries() + else: + sortedEntries = list(self.entries) + sortedEntries.sort(FeedEntry.compareByUpdateDate) + return sortedEntries + + def _unreadEntries(self): + retValue = [] + for entry in self.entries: + if not entry.read: + retValue.append(entry) + return retValue + + def incrementNextUpdateDate(self): + delta = timedelta(minutes=self.update_interval) + self.next_update = datetime.now() + delta + + def markAllEntriesRead(self): + for entry in self.entries: + entry.markRead() \ No newline at end of file diff -r 0e73adb2dec4 -r 04a730f9d07d backend/sqlalchemy/FeedEntry.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/backend/sqlalchemy/FeedEntry.py Sun Aug 21 03:55:16 2011 +0200 @@ -0,0 +1,43 @@ + +from datetime import datetime + +def compareByUpdateDate(first, second): + return cmp(first.updated, second.updated) + +class FeedEntry(object): + @staticmethod + def findById(id, session): + result = session.query(FeedEntry).filter(FeedEntry.id == id) + return result.first() + + @staticmethod + def create(entry): + new = FeedEntry() + new.id = entry.id + new.link = entry.link + new.title = entry.title + new.updated = entry.updated_parsed + new.summary = entry.summary + return new + + def __init__(self): + self.create_timestamp = datetime.now() + self.read = 0 + + def __repr__(self): + return "" % (self.pk, self.title) + + def userPresentableString(self): + return self.title + + def toggleRead(self): + if self.read: + self.markUnread() + else: + self.markRead() + + def markRead(self): + self.read = 1 + + def markUnread(self): + self.read = 0 diff -r 0e73adb2dec4 -r 04a730f9d07d backend/sqlalchemy/FeedList.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/backend/sqlalchemy/FeedList.py Sun Aug 21 03:55:16 2011 +0200 @@ -0,0 +1,23 @@ + +from Feed import Feed +from FeedEntry import FeedEntry +from Preferences import Preferences +from sqlalchemy.orm import joinedload + +def getFeeds(session): + preferences = Preferences(session) + if preferences.showOnlyUnreadFeeds(): + return _getUnreadFeeds(session) + else: + return Feed.all(session) + +def _getUnreadFeeds(session): + query = session.query(FeedEntry).filter(FeedEntry.read == 0) + queryWithOptions = query.options(joinedload("feed")) + result = queryWithOptions.all() + return _collectFeeds(result) + +def _collectFeeds(feedEntries): + feeds = [entry.feed for entry in feedEntries] + uniqueFeeds = set(feeds) + return list(uniqueFeeds) diff -r 0e73adb2dec4 -r 04a730f9d07d backend/sqlalchemy/Mapping.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/backend/sqlalchemy/Mapping.py Sun Aug 21 03:55:16 2011 +0200 @@ -0,0 +1,75 @@ + +from Feed import Feed +from FeedEntry import FeedEntry +from Preference import Preference +from sqlalchemy import Boolean +from sqlalchemy import Column +from sqlalchemy import DateTime +from sqlalchemy import ForeignKey +from sqlalchemy import Integer +from sqlalchemy import MetaData +from sqlalchemy import String +from sqlalchemy import Table +from sqlalchemy import Text +from sqlalchemy.orm import mapper +from sqlalchemy.orm import relation + +mappingDefined = False +feedEntryTable = None + +def createMapping(engine): + """ Make sure the mapping is defined only once. This is not really needed for the feed updater + or the GUI app but comes in handy when working interactively with the system. """ + global mappingDefined + if not mappingDefined: + _createMapping(engine) + mappingDefined = True + +def _createMapping(engine): + metadata = MetaData(engine) + metadata.bind = engine + + feedTable = Table("feed", metadata, + Column("pk", Integer, primary_key=True), + Column("title", String(255), nullable=False), + Column("rss_url", String(255), nullable=False), + # update interval is specified in minutes + Column("update_interval", Integer, nullable=False), + Column("next_update", DateTime, nullable=False), + # when displaying an entry of this feed, do not display the summary but rather load + # the link directly + Column("auto_load_entry_link", Boolean, nullable=False), + # this is actually a hack: when opening some sites in the QWebView it just crashes. + # This setting forces to open an entry's link in the external browser + Column("always_open_in_browser", Boolean, nullable=False) + ) + + global feedEntryTable + feedEntryTable = Table("feed_entry", metadata, + Column("pk", Integer, primary_key=True), + Column("create_timestamp", DateTime, nullable=False), + Column("read", Integer, nullable=False), + + Column("id", String(512), nullable=False), + Column("link", String(512), nullable=False), + Column("title", Text, nullable=False), + Column("summary", Text, nullable=False), + Column("updated", DateTime), + Column("feed_id", Integer, ForeignKey("feed.pk")) + ) + + preferencesTable = Table("preference", metadata, + Column("pk", Integer, primary_key=True), + Column("key", String(255), nullable=False), + Column("value", String(255), nullable=False) + ) + + metadata.create_all() + + mapper(FeedEntry, feedEntryTable) + mapper(Feed, feedTable, + properties = { + "entries" : relation(FeedEntry, backref="feed", lazy=True, cascade="delete, delete-orphan") + } + ) + mapper(Preference, preferencesTable) diff -r 0e73adb2dec4 -r 04a730f9d07d backend/sqlalchemy/Preference.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/backend/sqlalchemy/Preference.py Sun Aug 21 03:55:16 2011 +0200 @@ -0,0 +1,12 @@ + +class Preference(object): + @staticmethod + def forKey(key, session): + return session.query(Preference).filter(Preference.key == key).first() + + def __init__(self, key, value): + self.key = key + self.value = value + + def __repr__(self): + return "" % (self.key, self.value) diff -r 0e73adb2dec4 -r 04a730f9d07d backend/sqlalchemy/Preferences.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/backend/sqlalchemy/Preferences.py Sun Aug 21 03:55:16 2011 +0200 @@ -0,0 +1,100 @@ + +from Preference import Preference + +DAYS_TO_KEEP_FEED_ENTRIES = "DAYS_TO_KEEP_FEED_ENTRIES" +HIDE_READ_ENTRIES = "HIDE_READ_FEED_ENTRIES" +PROXY_HOST = "PROXY_HOST" +PROXY_PORT = "PROXY_PORT" +SHOW_ONLY_UNREAD_FEEDS = "SHOW_ONLY_UNREAD_FEEDS" +START_MAXIMIZED = "START_MAXIMIZED" + +def str2bool(string): + return string.lower() in ["yes", "true", "t", "1"] + +def bool2str(bool): + if bool: + return "True" + else: + return "False" + +class Preferences(object): + def __init__(self, session): + self.session = session + self.cache = {} + + def _cachedPreference(self, key, defaultValue=None, addIfMissing=True): + if self.cache.has_key(key): + return self.cache[key] + else: + pref = Preference.forKey(key, self.session) + if pref is not None: + self.cache[key] = pref + elif pref is None and addIfMissing: + pref = Preference(key, str(defaultValue)) + self.session.add(pref) + self.cache[key] = pref + return pref + + def startMaximized(self): + pref = self._cachedPreference(START_MAXIMIZED, False) + return str2bool(pref.value) + + def setStartMaximized(self, flag): + pref = self._cachedPreference(START_MAXIMIZED) + pref.value = bool2str(flag) + + def hideReadFeedEntries(self): + pref = self._cachedPreference(HIDE_READ_ENTRIES, False) + return str2bool(pref.value) + + def setHideReadFeedEntries(self, flag): + pref = self._cachedPreference(HIDE_READ_ENTRIES) + pref.value = bool2str(flag) + + def showOnlyUnreadFeeds(self): + pref = self._cachedPreference(SHOW_ONLY_UNREAD_FEEDS, False) + return str2bool(pref.value) + + def setShowOnlyUnreadFeeds(self, flag): + pref = self._cachedPreference(SHOW_ONLY_UNREAD_FEEDS) + pref.value = bool2str(flag) + + def proxyHost(self): + pref = self._cachedPreference(PROXY_HOST) + return pref.value + + def setProxyHost(self, hostname): + if hostname is None: + pref = self._cachedPreference(PROXY_HOST, addIfMissing=False) + if pref is not None: + self.session.delete(pref) + del(self.cache[PROXY_HOST]) + else: + pref = self._cachedPreference(PROXY_HOST) + pref.value = str(hostname) + + def proxyPort(self): + pref = self._cachedPreference(PROXY_PORT, 3128) + return int(pref.value) + + def setProxyPort(self, port): + if port is None: + pref = self._cachedPreference(PROXY_PORT, addIfMissing=False) + if pref is not None: + self.session.delete(pref) + del(self.cache[PROXY_PORT]) + else: + pref = self._cachedPreference(PROXY_PORT) + pref.value = str(port) + + def isProxyConfigured(self): + pref = self._cachedPreference(PROXY_HOST, addIfMissing=False) + return pref is not None + + def daysToKeepFeedEntries(self): + pref = self._cachedPreference(DAYS_TO_KEEP_FEED_ENTRIES, 90, addIfMissing=True) + return int(pref.value) + + def setDaysToKeepFeedEntries(self, dayString): + pref = self._cachedPreference(DAYS_TO_KEEP_FEED_ENTRIES) + pref.value = dayString diff -r 0e73adb2dec4 -r 04a730f9d07d backend/sqlalchemy/SqlAlchemyBackend.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/backend/sqlalchemy/SqlAlchemyBackend.py Sun Aug 21 03:55:16 2011 +0200 @@ -0,0 +1,34 @@ + +from Preferences import Preferences +import Database +import logging +import util +import FeedList + +class SqlAlchemyBackend(object): + ''' + Backend that uses sqlalchemy for persistence + ''' + + def __init__(self): + self._initLogging() + self.session = Database.createSession() + self.prefs = Preferences(self.session) + + def _initLogging(self): + logging.getLogger("sqlalchemy.orm").setLevel(logging.WARN) + + sqlalchemyLogLevel = logging.ERROR + if util.databaseLoggingEnabled(): + sqlalchemyLogLevel = logging.INFO + logging.getLogger("sqlalchemy").setLevel(sqlalchemyLogLevel) + + def preferences(self): + return self.prefs + + def getFeeds(self): + return FeedList.getFeeds(self.session) + + def dispose(self): + # save all uncommitted state, just in case + self.session.commit() diff -r 0e73adb2dec4 -r 04a730f9d07d backend/sqlalchemy/__init__.py diff -r 0e73adb2dec4 -r 04a730f9d07d backend/sqlalchemy/util.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/backend/sqlalchemy/util.py Sun Aug 21 03:55:16 2011 +0200 @@ -0,0 +1,9 @@ + +import sys + +def databaseLoggingEnabled(): + loggingEnabled = False + for arg in sys.argv: + if arg == "--databaseLogging": + loggingEnabled = True + return loggingEnabled diff -r 0e73adb2dec4 -r 04a730f9d07d feedworm-gui.py --- a/feedworm-gui.py Sun Aug 21 02:47:25 2011 +0200 +++ b/feedworm-gui.py Sun Aug 21 03:55:16 2011 +0200 @@ -1,11 +1,10 @@ -import Database from MainWindow import MainWindow -from Preferences import Preferences from PyQt4 import QtGui from PyQt4.QtNetwork import QNetworkProxy +from backend.sqlalchemy.SqlAlchemyBackend import SqlAlchemyBackend +import logging import sys -import util def setupProxy(preferences): if preferences.isProxyConfigured(): @@ -15,14 +14,14 @@ QNetworkProxy.setApplicationProxy(proxy) if __name__ == '__main__': - util.configureLogging() - session = Database.createSession() - preferences = Preferences(session) + logging.basicConfig(level=logging.DEBUG) + backend = SqlAlchemyBackend() + preferences = backend.preferences() setupProxy(preferences) app = QtGui.QApplication(sys.argv) - mainWindow = MainWindow(session) + mainWindow = MainWindow(backend) maximized = preferences.startMaximized() if maximized: diff -r 0e73adb2dec4 -r 04a730f9d07d util.py --- a/util.py Sun Aug 21 02:47:25 2011 +0200 +++ b/util.py Sun Aug 21 03:55:16 2011 +0200 @@ -1,27 +1,28 @@ -from datetime import datetime, timedelta from Feed import Feed +from datetime import datetime, timedelta import logging -import sys logger = logging.getLogger("database") -def databaseLoggingEnabled(): - loggingEnabled = False - for arg in sys.argv: - if arg == "--databaseLogging": - loggingEnabled = True - return loggingEnabled +# TODO remove this +#def databaseLoggingEnabled(): +# loggingEnabled = False +# for arg in sys.argv: +# if arg == "--databaseLogging": +# loggingEnabled = True +# return loggingEnabled -def configureLogging(): - logging.basicConfig(level=logging.DEBUG) - - sqlalchemyLogLevel = logging.ERROR - if databaseLoggingEnabled(): - sqlalchemyLogLevel = logging.INFO - logging.getLogger("sqlalchemy").setLevel(sqlalchemyLogLevel) - - logging.getLogger("sqlalchemy.orm").setLevel(logging.WARN) +# TODO remove this +#def configureLogging(): +# logging.basicConfig(level=logging.DEBUG) +# +# sqlalchemyLogLevel = logging.ERROR +# if databaseLoggingEnabled(): +# sqlalchemyLogLevel = logging.INFO +# logging.getLogger("sqlalchemy").setLevel(sqlalchemyLogLevel) +# +# logging.getLogger("sqlalchemy.orm").setLevel(logging.WARN) def loadFeeds(session=None, filename="feeds.txt"): file = open(filename) @@ -39,11 +40,3 @@ feed.next_update = datetime.now() - timedelta(minutes=1) session.commit() -def str2bool(string): - return string.lower() in ["yes", "true", "t", "1"] - -def bool2str(bool): - if bool: - return "True" - else: - return "False"