summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--portato/constants.py3
-rw-r--r--portato/gui/utils.py249
-rw-r--r--portato/gui/windows/main.py1
3 files changed, 249 insertions, 4 deletions
diff --git a/portato/constants.py b/portato/constants.py
index 3d7217f..79ad0f2 100644
--- a/portato/constants.py
+++ b/portato/constants.py
@@ -22,6 +22,8 @@ These should be set during the installation.
@type HOME: string
@var SU_COMMAND: command to execute to "su"
@type SU_COMMAND: string
+@var USE_SQL: whether to use the sqlite db
+@type USE_SQL: boolean
@var CONFIG_DIR: The configuration directory.
@type CONFIG_DIR: string
@@ -56,6 +58,7 @@ APP = "portato"
VERSION = "9999"
HOME = os.environ["HOME"]
SU_COMMAND = "gksu -D 'Portato'"
+USE_SQL = True
# config
CONFIG_DIR = "/etc/portato/"
diff --git a/portato/gui/utils.py b/portato/gui/utils.py
index 8bf076e..3e46361 100644
--- a/portato/gui/utils.py
+++ b/portato/gui/utils.py
@@ -14,7 +14,7 @@ from __future__ import absolute_import, with_statement
# some stuff needed
import re
-import sys
+import sys, os
import logging
import gettext
from collections import defaultdict
@@ -26,7 +26,16 @@ import gtk
# some backend things
from ..backend import flags, system, set_system
from ..helper import debug, info, set_log_level
-from ..constants import APP, LOCALE_DIR
+from ..constants import APP, LOCALE_DIR, USE_SQL, SESSION_DIR
+
+if USE_SQL:
+ try:
+ import sqlite3 as sql
+ except ImportError:
+ from pysqlite2 import dbapi2 as sql
+
+ import anydbm
+ import hashlib
# parser
from ..config_parser import ConfigParser
@@ -153,7 +162,7 @@ class PkgData (object):
def __repr__ (self):
return "<Package (%(cat)s, %(pkg)s, %(inst)s)>" % {"cat" : self.cat, "pkg" : self.pkg, "inst" : self.inst}
-class Database (object):
+class DictDatabase (object):
"""An internal database which holds a simple dictionary cat -> [package_list]."""
ALL = _("ALL")
@@ -162,6 +171,7 @@ class Database (object):
"""Constructor."""
self.__initialize()
self._lock = RLock()
+ self.populate()
def lock (f):
@wraps(f)
@@ -309,3 +319,236 @@ class Database (object):
self._restrict = regex
restrict = property(get_restrict, set_restrict)
+
+class SQLDatabase (object):
+
+ ALL = _("ALL")
+ FORBIDDEN = (".bzr", ".svn", ".git", "CVS", ".hg", "_darcs")
+
+ def __init__ (self):
+ """Constructor."""
+ self._restrict = ""
+ self._lock = RLock()
+
+ pkgdb = os.path.join(SESSION_DIR, "package.db")
+ pkgdb_existed = os.path.exists(pkgdb)
+
+ if pkgdb_existed:
+ debug("package.db already existant")
+ else:
+ debug("package.db not existant")
+
+ pkg_conn = sql.connect(os.path.join(SESSION_DIR, "package.db"))
+ pkg_conn.row_factory = sql.Row
+ pkg_conn.execute("""
+ CREATE TABLE IF NOT EXISTS packages
+ (
+ name TEXT,
+ cat TEXT,
+ inst INTEGER
+ )""")
+
+ pkg_conn.commit()
+
+ self.was_updated = self.updated()
+ if self.was_updated or not pkgdb_existed:
+ info(_("Cleaning database..."))
+ pkg_conn.execute("DELETE FROM packages") # empty db at beginning
+ info(_("Populating database..."))
+ self.populate(connection = pkg_conn)
+
+ pkg_conn.close()
+
+ descr_conn = sql.connect(os.path.join(SESSION_DIR, "descr.db"))
+ descr_conn.execute("""
+ CREATE TABLE IF NOT EXISTS descriptions
+ (
+ cp TEXT,
+ descr TEXT
+ )""")
+ descr_conn.close()
+
+ def updated (self):
+ changed = False
+
+ def walk (path):
+ debug("Walking %s", path)
+
+ for root, dirs, files in os.walk(path):
+ for f in files:
+ path = os.path.join(root, f)
+ yield "%s %s" % (f, os.stat(path).st_mtime)
+
+ for forbidden in self.FORBIDDEN:
+ if forbidden in dirs:
+ dirs.remove(forbidden)
+
+ overlays = system.get_global_settings("PORTDIR_OVERLAY").split()
+ hashes = {}
+ for overlay in overlays:
+ hashes[overlay] = hashlib.md5("".join(walk(overlay))).hexdigest()
+
+ timestamp = os.path.join(system.get_global_settings("PORTDIR"), "metadata/timestamp")
+ hashes["ROOT"] = hashlib.md5("%s %s" % (timestamp, os.stat(timestamp).st_mtime)).hexdigest()
+
+ dbpath = os.path.join(SESSION_DIR, "portdirs.db")
+ db_existed = os.path.exists(dbpath)
+ db = anydbm.open(dbpath, "c")
+ try:
+ if db_existed:
+ debug("portdirs.db already existant")
+ for key in set(db.keys())- set(hashes.keys()):
+ debug("Overlay '%s' has been removed", key)
+ del db[key]
+ changed = True
+
+ for key in hashes.iterkeys():
+
+ if key not in db.keys():
+ debug("Overlay '%s' has been added.", key)
+ changed = True
+
+ elif db[key] != hashes[key]:
+ debug("Overlay '%s' has been changed.", key)
+ changed = True
+
+ db[key] = hashes[key]
+ else:
+ debug("portdirs.db not existant")
+ for key in hashes.iterkeys():
+ db[key] = hashes[key]
+
+ finally:
+ db.close()
+
+ return changed
+
+ def lock (f):
+ @wraps(f)
+ def wrapper (self, *args, **kwargs):
+ with self._lock:
+ r = f(self, *args, **kwargs)
+
+ return r
+
+ return wrapper
+
+ def con (f):
+ @wraps(f)
+ def wrapper (*args, **kwargs):
+ if not "connection" in kwargs:
+ con= sql.connect(os.path.join(SESSION_DIR, "package.db"))
+ con.row_factory = sql.Row
+ kwargs["connection"] = con
+
+ return f(*args, **kwargs)
+
+ return wrapper
+
+ @lock
+ @con
+ def populate (self, category = None, connection = None):
+ """Populates the database.
+
+ @param category: An optional category - so only packages of this category are inserted.
+ @type category: string
+ """
+
+ def _get():
+ # get the lists
+ inst = system.find_packages(pkgSet = system.SET_INSTALLED, key=category, with_version = False)
+ for p in system.find_packages(key = category, with_version = False):
+ cat, pkg = p.split("/")
+
+ yield (cat, pkg, p in inst)
+
+ connection.executemany("INSERT INTO packages (cat, name, inst) VALUES (?, ?, ?)", _get())
+ connection.commit()
+
+ @lock
+ @con
+ def get_cat (self, category = None, byName = True, connection = None):
+ """Returns the packages in the category.
+
+ @param cat: category to return the packages from; if None it defaults to "ALL"
+ @type cat: string
+ @param byName: selects whether to return the list sorted by name or by installation
+ @type byName: boolean
+ @return: an iterator over a list of tuples: (category, name, is_installed) or []
+ @rtype: L{PkgData}<iterator>
+ """
+
+ sort = "ORDER BY name"
+ if not byName:
+ sort = "ORDER BY inst DESC, name"
+
+ if not category or category == self.ALL:
+ c = connection.execute("SELECT cat, name, inst FROM packages WHERE 1=1 %s %s" % (self.restrict, sort))
+ else:
+ c = connection.execute("SELECT cat, name, inst FROM packages WHERE cat = ? %s %s" % (self.restrict ,sort), (category,))
+
+ for pkg in c:
+ yield PkgData(pkg["cat"], pkg["name"], pkg["inst"])
+ c.close()
+
+ @lock
+ @con
+ def get_categories (self, installed = False, connection = None):
+ """Returns all categories.
+
+ @param installed: Only return these with at least one installed package.
+ @type installed: boolean
+ @returns: the list of categories
+ @rtype: string<iterator>
+ """
+
+ if installed:
+ where = "inst = 1"
+ else:
+ where = "1 = 1"
+
+ c = connection.execute("SELECT cat FROM packages WHERE %s %s GROUP BY cat" % (where, self.restrict))
+
+ l = c.fetchall()
+ c.close()
+
+ if len(l) > 1:
+ yield self.ALL
+
+ for cat in l:
+ yield cat["cat"]
+
+ @lock
+ @con
+ def reload (self, cat = None, connection = None):
+ """Reloads the given category.
+
+ @param cat: category
+ @type cat: string
+ """
+
+ if cat:
+ connection.execute("DELETE FROM packages WHERE cat = ?", (cat,))
+ connection.commit()
+ self.populate(cat+"/", connection = connection)
+ else:
+ connection.execute("DELETE FROM packages")
+ connection.commit()
+ self.populate(connection = connection)
+
+ def get_restrict (self):
+ return self._restrict
+
+ @lock
+ def set_restrict (self, restrict):
+ if not restrict:
+ self._restrict = ""
+ else:
+ self._restrict = "AND name LIKE '%%%s%%'" % restrict
+
+ restrict = property(get_restrict, set_restrict)
+
+if USE_SQL:
+ Database = SQLDatabase
+else:
+ Database = DictDatabase
diff --git a/portato/gui/windows/main.py b/portato/gui/windows/main.py
index 8728d57..5a69e5b 100644
--- a/portato/gui/windows/main.py
+++ b/portato/gui/windows/main.py
@@ -596,7 +596,6 @@ class MainWindow (Window):
# package db
splash(_("Creating Database"))
self.db = Database()
- self.db.populate()
# set plugins and plugin-menu
splash(_("Loading Plugins"))