← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~cjwatson/launchpad:stormify-karma into launchpad:master

 

Colin Watson has proposed merging ~cjwatson/launchpad:stormify-karma into launchpad:master.

Commit message:
Convert Karma and friends to Storm

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~cjwatson/launchpad/+git/launchpad/+merge/425228
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~cjwatson/launchpad:stormify-karma into launchpad:master.
diff --git a/lib/lp/answers/doc/karma.rst b/lib/lp/answers/doc/karma.rst
index b96807d..8de44d0 100644
--- a/lib/lp/answers/doc/karma.rst
+++ b/lib/lp/answers/doc/karma.rst
@@ -12,7 +12,9 @@ actions we consider to be a reasonable contribution.
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> from lp.registry.interfaces.product import IProductSet
     >>> from lp.registry.model.karma import KarmaCategory
-    >>> answers_category = KarmaCategory.byName('answers')
+    >>> from lp.services.database.interfaces import IStore
+    >>> answers_category = IStore(KarmaCategory).find(
+    ...     KarmaCategory, name="answers").one()
     >>> answers_karma_actions = answers_category.karmaactions
     >>> for action in sorted(answers_karma_actions, key=attrgetter('title')):
     ...     print(action.title)
diff --git a/lib/lp/app/doc/tales.rst b/lib/lp/app/doc/tales.rst
index b82bb61..7bac04c 100644
--- a/lib/lp/app/doc/tales.rst
+++ b/lib/lp/app/doc/tales.rst
@@ -73,7 +73,9 @@ is not an actual launchpad user.
 We also have image:icon for KarmaCategory:
 
     >>> from lp.registry.model.karma import KarmaCategory
-    >>> for category in KarmaCategory.select(orderBy='title'):
+    >>> from lp.services.database.interfaces import IStore
+    >>> for category in IStore(KarmaCategory).find(
+    ...         KarmaCategory).order_by("title"):
     ...     print(test_tales("category/image:icon", category=category))
     <img ... title="Answer Tracker" src="/@@/question" />
     <img ... title="Bazaar Branches" src="/@@/branch" />
diff --git a/lib/lp/bugs/doc/bugwatch.rst b/lib/lp/bugs/doc/bugwatch.rst
index 4070e12..0390062 100644
--- a/lib/lp/bugs/doc/bugwatch.rst
+++ b/lib/lp/bugs/doc/bugwatch.rst
@@ -326,13 +326,13 @@ The Bug Watch Updater didn't receive any karma for the changed bug
 tasks, because it's not a valid person and only valid persons can get karma.
 
     >>> from lp.registry.model.karma import Karma
-    >>> Karma.selectBy(personID=bug_watch_updater_user.id).count()
+    >>> from lp.services.database.interfaces import IStore
+    >>> IStore(Karma).find(Karma, person=bug_watch_updater_user).count()
     0
 
 Finally, let's make sure that bug notifications were added:
 
     >>> from lp.bugs.model.bugnotification import BugNotification
-    >>> from lp.services.database.interfaces import IStore
     >>> unsent_notifications = IStore(BugNotification).find(
     ...     BugNotification, date_emailed=None).order_by(BugNotification.id)
 
diff --git a/lib/lp/bugs/doc/malone-karma.rst b/lib/lp/bugs/doc/malone-karma.rst
index 06d7529..5bcb3c7 100644
--- a/lib/lp/bugs/doc/malone-karma.rst
+++ b/lib/lp/bugs/doc/malone-karma.rst
@@ -222,7 +222,9 @@ actions, except for updating the obsolete "summary", "priority", and "Web
 links":
 
     >>> from lp.registry.model.karma import KarmaCategory
-    >>> bugs_category = KarmaCategory.byName('bugs')
+    >>> from lp.services.database.interfaces import IStore
+    >>> bugs_category = IStore(KarmaCategory).find(
+    ...     KarmaCategory, name="bugs").one()
     >>> bugs_karma_actions = bugs_category.karmaactions
     >>> summary_change = getUtility(
     ...     IKarmaActionSet).getByName('bugsummarychanged')
diff --git a/lib/lp/code/doc/branch-karma.rst b/lib/lp/code/doc/branch-karma.rst
index 5b44699..162ab2c 100644
--- a/lib/lp/code/doc/branch-karma.rst
+++ b/lib/lp/code/doc/branch-karma.rst
@@ -9,7 +9,9 @@ branches.  We want to encourage the linking of information, and so
 give karma for it.
 
     >>> from lp.registry.model.karma import KarmaCategory
-    >>> code_category = KarmaCategory.byName('code')
+    >>> from lp.services.database.interfaces import IStore
+    >>> code_category = IStore(KarmaCategory).find(
+    ...     KarmaCategory, name="code").one()
     >>> code_karma_actions = code_category.karmaactions
     >>> for summary in sorted(
     ...     [action.summary for action in code_karma_actions]):
diff --git a/lib/lp/registry/browser/tests/distributionsourcepackage-views.rst b/lib/lp/registry/browser/tests/distributionsourcepackage-views.rst
index 35daee3..4fdc6c5 100644
--- a/lib/lp/registry/browser/tests/distributionsourcepackage-views.rst
+++ b/lib/lp/registry/browser/tests/distributionsourcepackage-views.rst
@@ -120,17 +120,19 @@ package.
     # karma for their efforts.
     >>> from lp.registry.model.karma import KarmaCategory
     >>> from lp.registry.model.karma import KarmaTotalCache
+    >>> from lp.services.database.interfaces import IStore
     >>> from lp.testing.dbuser import dbuser
-    >>> soyuz_category = KarmaCategory.byName('soyuz')
+    >>> soyuz_category = IStore(KarmaCategory).find(
+    ...     KarmaCategory, name="soyuz").one()
     >>> sourcepackagerelease = gedit_nightly_src_breezy.sourcepackagerelease
     >>> gedit_name = sourcepackagerelease.sourcepackagename
-    >>> ppa_beta_owner_id = ppa_beta.owner.id
-    >>> ppa_nightly_owner_id = ppa_nightly.owner.id
+    >>> ppa_beta_owner = ppa_beta.owner
+    >>> ppa_nightly_owner = ppa_nightly.owner
     >>> with dbuser('karma'):
     ...     cache_entry = KarmaTotalCache(
-    ...         person=ppa_beta_owner_id, karma_total=200)
+    ...         person=ppa_beta_owner, karma_total=200)
     ...     cache_entry = KarmaTotalCache(
-    ...         person=ppa_nightly_owner_id, karma_total=201)
+    ...         person=ppa_nightly_owner, karma_total=201)
 
     # Because our connection has been closed during the reconnect, we
     # need to get the distro and source package again.
diff --git a/lib/lp/registry/browser/tests/test_person.py b/lib/lp/registry/browser/tests/test_person.py
index 9effeef..22975f8 100644
--- a/lib/lp/registry/browser/tests/test_person.py
+++ b/lib/lp/registry/browser/tests/test_person.py
@@ -603,11 +603,14 @@ class TestPersonViewKarma(TestCaseWithFactory):
         self.view = PersonView(
             person, LaunchpadTestRequest())
         self._makeKarmaCache(
-            person, product, KarmaCategory.byName('bugs'))
+            person, product,
+            IStore(KarmaCategory).find(KarmaCategory, name="bugs").one())
         self._makeKarmaCache(
-            person, product, KarmaCategory.byName('answers'))
+            person, product,
+            IStore(KarmaCategory).find(KarmaCategory, name="answers").one())
         self._makeKarmaCache(
-            person, product, KarmaCategory.byName('code'))
+            person, product,
+            IStore(KarmaCategory).find(KarmaCategory, name="code").one())
 
     def test_karma_category_sort(self):
         categories = self.view.contributed_categories
diff --git a/lib/lp/registry/doc/distribution-sourcepackage.rst b/lib/lp/registry/doc/distribution-sourcepackage.rst
index 5b6a88d..9ab1f97 100644
--- a/lib/lp/registry/doc/distribution-sourcepackage.rst
+++ b/lib/lp/registry/doc/distribution-sourcepackage.rst
@@ -294,15 +294,15 @@ versions of a given source package have been published in.
 
     # Give the creators of the above source packages some
     # karma for their efforts.
-    >>> ppa_beta_owner_id = ppa_beta.owner.id
-    >>> ppa_nightly_owner_id = ppa_nightly.owner.id
+    >>> ppa_beta_owner = ppa_beta.owner
+    >>> ppa_nightly_owner = ppa_nightly.owner
 
     >>> from lp.testing.dbuser import switch_dbuser
     >>> switch_dbuser('karma')
     >>> from lp.registry.model.karma import KarmaTotalCache
-    >>> cache_entry = KarmaTotalCache(person=ppa_beta_owner_id,
+    >>> cache_entry = KarmaTotalCache(person=ppa_beta_owner,
     ...     karma_total=200)
-    >>> cache_entry = KarmaTotalCache(person=ppa_nightly_owner_id,
+    >>> cache_entry = KarmaTotalCache(person=ppa_nightly_owner,
     ...     karma_total=201)
     >>> switch_dbuser('launchpad')
 
diff --git a/lib/lp/registry/doc/karmacache.rst b/lib/lp/registry/doc/karmacache.rst
index 6f367f0..f61706c 100644
--- a/lib/lp/registry/doc/karmacache.rst
+++ b/lib/lp/registry/doc/karmacache.rst
@@ -14,6 +14,7 @@ which runs daily. The script does that by using the IKarmaCacheManager API.
     >>> from lp.registry.interfaces.karma import IKarmaCacheManager
     >>> from lp.registry.interfaces.person import IPersonSet
     >>> from lp.registry.model.karma import KarmaCategory
+    >>> from lp.services.database.interfaces import IStore
 
     >>> switch_dbuser('karma')
     >>> karmacachemanager = getUtility(IKarmaCacheManager)
@@ -25,7 +26,7 @@ This is done using the new() method:
 
     >>> value = 199
     >>> person = getUtility(IPersonSet).getByName('salgado')
-    >>> bugs = KarmaCategory.byName('bugs')
+    >>> bugs = IStore(KarmaCategory).find(KarmaCategory, name="bugs").one()
 
     # The 'karma' dbuser doesn't have access to the Product table, so we'll
     # use firefox's id directly instead of trying to fetch the product from
diff --git a/lib/lp/registry/doc/karmacontext.rst b/lib/lp/registry/doc/karmacontext.rst
index 9824db8..64ec3a8 100644
--- a/lib/lp/registry/doc/karmacontext.rst
+++ b/lib/lp/registry/doc/karmacontext.rst
@@ -32,14 +32,15 @@ all categories.
     name16: 8
 
     >>> from lp.registry.model.karma import KarmaCategory
-    >>> bugs = KarmaCategory.byName('bugs')
+    >>> from lp.services.database.interfaces import IStore
+    >>> bugs = IStore(KarmaCategory).find(KarmaCategory, name="bugs").one()
     >>> top_bugmasters = firefox.getTopContributors(category=bugs, limit=2)
     >>> for person, karmavalue in top_bugmasters:
     ...     print('%s: %d' % (person.name, karmavalue))
     name12: 66
     name16: 8
 
-    >>> specs = KarmaCategory.byName('specs')
+    >>> specs = IStore(KarmaCategory).find(KarmaCategory, name="specs").one()
     >>> top_speccers = firefox.getTopContributors(category=specs, limit=1)
     >>> for person, karmavalue in top_speccers:
     ...     print('%s: %d' % (person.name, karmavalue))
diff --git a/lib/lp/registry/model/karma.py b/lib/lp/registry/model/karma.py
index 9ff7278..4a06e3e 100644
--- a/lib/lp/registry/model/karma.py
+++ b/lib/lp/registry/model/karma.py
@@ -13,7 +13,15 @@ __all__ = [
     'KarmaContextMixin',
     ]
 
-from storm.expr import Desc
+import pytz
+from storm.locals import (
+    DateTime,
+    Desc,
+    Int,
+    Reference,
+    ReferenceSet,
+    Unicode,
+    )
 from zope.interface import implementer
 
 from lp.app.errors import NotFoundError
@@ -32,19 +40,8 @@ from lp.registry.interfaces.karma import (
 from lp.registry.interfaces.product import IProduct
 from lp.registry.interfaces.projectgroup import IProjectGroup
 from lp.services.database.constants import UTC_NOW
-from lp.services.database.datetimecol import UtcDateTimeCol
 from lp.services.database.interfaces import IStore
-from lp.services.database.sqlbase import (
-    SQLBase,
-    sqlvalues,
-    )
-from lp.services.database.sqlobject import (
-    ForeignKey,
-    IntCol,
-    SQLMultipleJoin,
-    SQLObjectNotFound,
-    StringCol,
-    )
+from lp.services.database.stormbase import StormBase
 
 
 @implementer(IKarmaAssignedEvent)
@@ -57,41 +54,54 @@ class KarmaAssignedEvent:
 
 
 @implementer(IKarma)
-class Karma(SQLBase):
+class Karma(StormBase):
     """See IKarma."""
 
-    _table = 'Karma'
-    _defaultOrder = ['action', 'id']
-
-    person = ForeignKey(
-        dbName='person', foreignKey='Person', notNull=True)
-    action = ForeignKey(
-        dbName='action', foreignKey='KarmaAction', notNull=True)
-    product = ForeignKey(
-        dbName='product', foreignKey='Product', notNull=False)
-    distribution = ForeignKey(
-        dbName='distribution', foreignKey='Distribution', notNull=False)
-    sourcepackagename = ForeignKey(
-        dbName='sourcepackagename', foreignKey='SourcePackageName',
-        notNull=False)
-    datecreated = UtcDateTimeCol(
-        dbName='datecreated', notNull=True, default=UTC_NOW)
+    __storm_table__ = "Karma"
+    __storm_order__ = ["action", "id"]
+
+    id = Int(primary=True)
+
+    person_id = Int(name="person", allow_none=False)
+    person = Reference(person_id, "Person.id")
+    action_id = Int(name="action", allow_none=False)
+    action = Reference(action_id, "KarmaAction.id")
+    product_id = Int(name="product", allow_none=True)
+    product = Reference(product_id, "Product.id")
+    distribution_id = Int(name="distribution", allow_none=True)
+    distribution = Reference(distribution_id, "Distribution.id")
+    sourcepackagename_id = Int(name="sourcepackagename", allow_none=True)
+    sourcepackagename = Reference(sourcepackagename_id, "SourcePackageName.id")
+    datecreated = DateTime(
+        name="datecreated", allow_none=False, default=UTC_NOW, tzinfo=pytz.UTC)
+
+    def __init__(self, person, action, product=None, distribution=None,
+                 sourcepackagename=None, datecreated=None):
+        super().__init__()
+        self.person = person
+        self.action = action
+        self.product = product
+        self.distribution = distribution
+        self.sourcepackagename = sourcepackagename
+        self.datecreated = datecreated
 
 
 @implementer(IKarmaAction)
-class KarmaAction(SQLBase):
+class KarmaAction(StormBase):
     """See IKarmaAction."""
 
-    _table = 'KarmaAction'
-    sortingColumns = ['category', 'name']
-    _defaultOrder = sortingColumns
+    __storm_table__ = "KarmaAction"
+    sortingColumns = ["category", "name"]
+    __storm_order__ = sortingColumns
+
+    id = Int(primary=True)
 
-    name = StringCol(notNull=True, alternateID=True)
-    title = StringCol(notNull=True)
-    summary = StringCol(notNull=True)
-    category = ForeignKey(dbName='category', foreignKey='KarmaCategory',
-        notNull=True)
-    points = IntCol(dbName='points', notNull=True)
+    name = Unicode(name="name", allow_none=False)
+    title = Unicode(name="title", allow_none=False)
+    summary = Unicode(name="summary", allow_none=False)
+    category_id = Int(name="category", allow_none=False)
+    category = Reference(category_id, "KarmaCategory.id")
+    points = Int(name="points", allow_none=False)
 
 
 @implementer(IKarmaActionSet)
@@ -99,52 +109,67 @@ class KarmaActionSet:
     """See IKarmaActionSet."""
 
     def __iter__(self):
-        return iter(KarmaAction.select())
+        return iter(IStore(KarmaAction).find(KarmaAction))
 
     def getByName(self, name, default=None):
         """See IKarmaActionSet."""
-        try:
-            return KarmaAction.byName(name)
-        except SQLObjectNotFound:
+        action = IStore(KarmaAction).find(KarmaAction, name=name).one()
+        if action is None:
             return default
+        return action
 
     def selectByCategory(self, category):
         """See IKarmaActionSet."""
-        return KarmaAction.selectBy(category=category)
+        return IStore(KarmaAction).find(KarmaAction, category=category)
 
     def selectByCategoryAndPerson(self, category, person, orderBy=None):
         """See IKarmaActionSet."""
         if orderBy is None:
             orderBy = KarmaAction.sortingColumns
-        query = ('KarmaAction.category = %s '
-                 'AND Karma.action = KarmaAction.id '
-                 'AND Karma.person = %s' % sqlvalues(category.id, person.id))
-        return KarmaAction.select(
-                query, clauseTables=['Karma'], distinct=True, orderBy=orderBy)
+        return IStore(KarmaAction).find(
+            KarmaAction,
+            KarmaAction.category == category,
+            Karma.action == KarmaAction.id,
+            Karma.person == person).config(distinct=True).order_by(orderBy)
 
 
 @implementer(IKarmaCache)
-class KarmaCache(SQLBase):
+class KarmaCache(StormBase):
     """See IKarmaCache."""
 
-    _table = 'KarmaCache'
-    _defaultOrder = ['category', 'id']
-
-    person = ForeignKey(
-        dbName='person', foreignKey='Person', notNull=True)
-    category = ForeignKey(
-        dbName='category', foreignKey='KarmaCategory', notNull=False)
-    karmavalue = IntCol(
-        dbName='karmavalue', notNull=True)
-    product = ForeignKey(
-        dbName='product', foreignKey='Product', notNull=False)
-    projectgroup = ForeignKey(
-        dbName='project', foreignKey='ProjectGroup', notNull=False)
-    distribution = ForeignKey(
-        dbName='distribution', foreignKey='Distribution', notNull=False)
-    sourcepackagename = ForeignKey(
-        dbName='sourcepackagename', foreignKey='SourcePackageName',
-        notNull=False)
+    __storm_table__ = "KarmaCache"
+    __storm_order__ = ["category", "id"]
+
+    id = Int(primary=True)
+
+    person_id = Int(name="person", allow_none=False)
+    person = Reference(person_id, "Person.id")
+    category_id = Int(name="category", allow_none=True)
+    category = Reference(category_id, "KarmaCategory.id")
+    karmavalue = Int(name="karmavalue", allow_none=False)
+    product_id = Int(name="product", allow_none=True)
+    product = Reference(product_id, "Product.id")
+    projectgroup_id = Int(name="project", allow_none=True)
+    projectgroup = Reference(projectgroup_id, "ProjectGroup.id")
+    distribution_id = Int(name="distribution", allow_none=True)
+    distribution = Reference(distribution_id, "Distribution.id")
+    sourcepackagename_id = Int(name="sourcepackagename", allow_none=True)
+    sourcepackagename = Reference(sourcepackagename_id, "SourcePackageName.id")
+
+    # It's a little odd for the constructor to explicitly take IDs, but this
+    # is mainly called by cronscripts/foaf-update-karma-cache.py which only
+    # has the IDs available to it.
+    def __init__(self, person_id, karmavalue, category_id=None,
+                 product_id=None, projectgroup_id=None, distribution_id=None,
+                 sourcepackagename_id=None):
+        super().__init__()
+        self.person_id = person_id
+        self.karmavalue = karmavalue
+        self.category_id = category_id
+        self.product_id = product_id
+        self.projectgroup_id = projectgroup_id
+        self.distribution_id = distribution_id
+        self.sourcepackagename_id = sourcepackagename_id
 
 
 @implementer(IKarmaCacheManager)
@@ -155,11 +180,13 @@ class KarmaCacheManager:
             distribution_id=None, sourcepackagename_id=None,
             projectgroup_id=None):
         """See IKarmaCacheManager."""
-        return KarmaCache(
-            karmavalue=value, person=person_id, category=category_id,
-            product=product_id, distribution=distribution_id,
-            sourcepackagename=sourcepackagename_id,
-            projectgroup=projectgroup_id)
+        karma_cache = KarmaCache(
+            karmavalue=value, person_id=person_id, category_id=category_id,
+            product_id=product_id, distribution_id=distribution_id,
+            sourcepackagename_id=sourcepackagename_id,
+            projectgroup_id=projectgroup_id)
+        IStore(KarmaCache).add(karma_cache)
+        return karma_cache
 
     def updateKarmaValue(self, value, person_id, category_id, product_id=None,
                          distribution_id=None, sourcepackagename_id=None,
@@ -174,7 +201,7 @@ class KarmaCacheManager:
             raise NotFoundError("KarmaCache not found: %s" % vars())
         else:
             entry.karmavalue = value
-            entry.syncUpdate()
+            IStore(entry).flush()
 
     def _getEntry(self, person_id, category_id, product_id=None,
                   distribution_id=None, sourcepackagename_id=None,
@@ -185,37 +212,48 @@ class KarmaCacheManager:
         """
         return IStore(KarmaCache).find(
             KarmaCache,
-            KarmaCache.personID == person_id,
-            KarmaCache.categoryID == category_id,
-            KarmaCache.productID == product_id,
-            KarmaCache.projectgroupID == projectgroup_id,
-            KarmaCache.distributionID == distribution_id,
-            KarmaCache.sourcepackagenameID == sourcepackagename_id).one()
+            KarmaCache.person == person_id,
+            KarmaCache.category == category_id,
+            KarmaCache.product == product_id,
+            KarmaCache.projectgroup == projectgroup_id,
+            KarmaCache.distribution == distribution_id,
+            KarmaCache.sourcepackagename == sourcepackagename_id).one()
 
 
 @implementer(IKarmaTotalCache)
-class KarmaTotalCache(SQLBase):
+class KarmaTotalCache(StormBase):
     """A cached value of the total of a person's karma (all categories)."""
 
-    _table = 'KarmaTotalCache'
-    _defaultOrder = ['id']
+    __storm_table__ = "KarmaTotalCache"
+    __storm_order__ = ["id"]
+
+    id = Int(primary=True)
 
-    person = ForeignKey(dbName='person', foreignKey='Person', notNull=True)
-    karma_total = IntCol(dbName='karma_total', notNull=True)
+    person_id = Int(name="person", allow_none=False)
+    person = Reference(person_id, "Person.id")
+    karma_total = Int(name="karma_total", allow_none=False)
+
+    def __init__(self, person, karma_total):
+        super().__init__()
+        self.person = person
+        self.karma_total = karma_total
 
 
 @implementer(IKarmaCategory)
-class KarmaCategory(SQLBase):
+class KarmaCategory(StormBase):
     """See IKarmaCategory."""
 
-    _defaultOrder = ['title', 'id']
+    __storm_table__ = "KarmaCategory"
+    __storm_order__ = ["title", "id"]
+
+    id = Int(primary=True)
 
-    name = StringCol(notNull=True, alternateID=True)
-    title = StringCol(notNull=True)
-    summary = StringCol(notNull=True)
+    name = Unicode(allow_none=False)
+    title = Unicode(allow_none=False)
+    summary = Unicode(allow_none=False)
 
-    karmaactions = SQLMultipleJoin(
-        'KarmaAction', joinColumn='category', orderBy='name')
+    karmaactions = ReferenceSet(
+        "id", "KarmaAction.category_id", order_by="KarmaAction.name")
 
 
 @implementer(IKarmaContext)
@@ -229,7 +267,7 @@ class KarmaContextMixin:
     def getTopContributorsGroupedByCategory(self, limit=None):
         """See IKarmaContext."""
         contributors_by_category = {}
-        for category in KarmaCategory.select():
+        for category in IStore(KarmaCategory).find(KarmaCategory):
             results = self.getTopContributors(category=category, limit=limit)
             if results:
                 contributors_by_category[category] = results
@@ -240,11 +278,11 @@ class KarmaContextMixin:
         from lp.registry.model.person import Person
         store = IStore(Person)
         if IProduct.providedBy(self):
-            condition = KarmaCache.productID == self.id
+            condition = KarmaCache.product == self.id
         elif IDistribution.providedBy(self):
-            condition = KarmaCache.distributionID == self.id
+            condition = KarmaCache.distribution == self.id
         elif IProjectGroup.providedBy(self):
-            condition = KarmaCache.projectgroupID == self.id
+            condition = KarmaCache.projectgroup == self.id
         else:
             raise AssertionError(
                 "Not a product, project group or distribution: %r" % self)
@@ -253,7 +291,7 @@ class KarmaContextMixin:
             category = category.id
         contributors = store.find(
             (Person, KarmaCache.karmavalue),
-            KarmaCache.personID == Person.id,
-            KarmaCache.categoryID == category, condition).order_by(
+            KarmaCache.person_id == Person.id,
+            KarmaCache.category == category, condition).order_by(
                 Desc(KarmaCache.karmavalue)).config(limit=limit)
         return list(contributors)
diff --git a/lib/lp/registry/model/person.py b/lib/lp/registry/model/person.py
index e41ae7f..eca1632 100644
--- a/lib/lp/registry/model/person.py
+++ b/lib/lp/registry/model/person.py
@@ -178,6 +178,7 @@ from lp.registry.interfaces.jabber import (
     IJabberID,
     IJabberIDSet,
     )
+from lp.registry.interfaces.karma import IKarmaActionSet
 from lp.registry.interfaces.mailinglist import (
     IMailingListSet,
     MailingListStatus,
@@ -226,7 +227,6 @@ from lp.registry.interfaces.wikiname import (
 from lp.registry.model.codeofconduct import SignedCodeOfConduct
 from lp.registry.model.karma import (
     Karma,
-    KarmaAction,
     KarmaAssignedEvent,
     KarmaCache,
     KarmaCategory,
@@ -1013,12 +1013,12 @@ class Person(
             ProductSet,
             )
         tableset = Store.of(self).using(
-            KarmaCache, LeftJoin(Product, Product.id == KarmaCache.productID),
+            KarmaCache, LeftJoin(Product, Product.id == KarmaCache.product_id),
             LeftJoin(Distribution, Distribution.id ==
-                     KarmaCache.distributionID))
+                     KarmaCache.distribution_id))
         result = tableset.find(
             (Product, Distribution, KarmaCache.karmavalue),
-             KarmaCache.personID == self.id,
+             KarmaCache.person == self.id,
              KarmaCache.category == None,
              KarmaCache.projectgroup == None,
              Or(
@@ -1209,8 +1209,9 @@ class Person(
             """ % replacements
         cur = cursor()
         cur.execute(query)
-        ids = ",".join(str(id) for [id] in cur.fetchall())
-        return KarmaCategory.select("id IN (%s)" % ids)
+        ids = [id for [id] in cur.fetchall()]
+        return IStore(KarmaCategory).find(
+            KarmaCategory, KarmaCategory.id.is_in(ids))
 
     @property
     def karma_category_caches(self):
@@ -1231,7 +1232,8 @@ class Person(
     def karma(self):
         """See `IPerson`."""
         # May also be loaded from _members
-        cache = KarmaTotalCache.selectOneBy(person=self)
+        cache = IStore(KarmaTotalCache).find(
+            KarmaTotalCache, person=self).one()
         if cache is None:
             # Newly created accounts may not be in the cache yet, meaning the
             # karma updater script hasn't run since the account was created.
@@ -1288,9 +1290,8 @@ class Person(
             raise AssertionError(
                 'You must provide either a product or a distribution.')
 
-        try:
-            action = KarmaAction.byName(action_name)
-        except SQLObjectNotFound:
+        action = getUtility(IKarmaActionSet).getByName(action_name)
+        if action is None:
             raise AssertionError(
                 "No KarmaAction found with name '%s'." % action_name)
 
@@ -1300,13 +1301,14 @@ class Person(
             person=self, action=action, product=product,
             distribution=distribution, sourcepackagename=sourcepackagename,
             datecreated=datecreated)
+        Store.of(karma).flush()
         notify(KarmaAssignedEvent(self, karma))
         return karma
 
     def latestKarma(self, quantity=25):
         """See `IPerson`."""
-        return Karma.selectBy(person=self,
-            orderBy='-datecreated')[:quantity]
+        return IStore(Karma).find(Karma, person=self).order_by(
+            Desc(Karma.datecreated))[:quantity]
 
     # This is to cache TeamParticipation information as that's used tons of
     # times in each request.
diff --git a/lib/lp/registry/tests/test_karmacache_updater.py b/lib/lp/registry/tests/test_karmacache_updater.py
index ec5ad9f..4639f42 100644
--- a/lib/lp/registry/tests/test_karmacache_updater.py
+++ b/lib/lp/registry/tests/test_karmacache_updater.py
@@ -10,6 +10,7 @@ from zope.component import getUtility
 from lp.registry.interfaces.person import IPersonSet
 from lp.registry.interfaces.product import IProductSet
 from lp.registry.model.karma import KarmaCache
+from lp.services.database.interfaces import IStore
 from lp.services.database.sqlbase import flush_database_caches
 from lp.testing import (
     ANONYMOUS,
@@ -33,7 +34,7 @@ class TestKarmaCacheUpdater(unittest.TestCase):
         self.layer.force_dirty_database()
 
     def _getCacheEntriesByPerson(self, person):
-        return KarmaCache.selectBy(person=person)
+        return IStore(KarmaCache).find(KarmaCache, person=person)
 
     def _runScript(self):
         process = subprocess.Popen(
diff --git a/lib/lp/registry/tests/test_person.py b/lib/lp/registry/tests/test_person.py
index c601415..586cf1e 100644
--- a/lib/lp/registry/tests/test_person.py
+++ b/lib/lp/registry/tests/test_person.py
@@ -56,6 +56,7 @@ from lp.registry.model.person import (
     get_recipients,
     Person,
     )
+from lp.services.database.interfaces import IStore
 from lp.services.database.sqlbase import (
     flush_database_caches,
     flush_database_updates,
@@ -1233,7 +1234,8 @@ class KarmaTestMixin:
             total = 0
             # Insert category total for person and project.
             for category_name, value in category_name_values:
-                category = KarmaCategory.byName(category_name)
+                category = IStore(KarmaCategory).find(
+                    KarmaCategory, name=category_name).one()
                 self.cache_manager.new(
                     value, person.id, category.id, product_id=product.id)
                 total += value
@@ -1249,7 +1251,7 @@ class KarmaTestMixin:
         must be retrieved again.
         """
         with dbuser('karma'):
-            KarmaTotalCache(person=person.id, karma_total=total)
+            KarmaTotalCache(person=person, karma_total=total)
 
 
 class TestPersonKarma(TestCaseWithFactory, KarmaTestMixin):
diff --git a/lib/lp/registry/vocabularies.py b/lib/lp/registry/vocabularies.py
index e0f9837..d08fe72 100644
--- a/lib/lp/registry/vocabularies.py
+++ b/lib/lp/registry/vocabularies.py
@@ -209,6 +209,7 @@ from lp.services.webapp.vocabulary import (
     IHugeVocabulary,
     NamedSQLObjectVocabulary,
     NamedStormHugeVocabulary,
+    NamedStormVocabulary,
     SQLObjectVocabularyBase,
     StormVocabularyBase,
     VocabularyFilter,
@@ -264,10 +265,10 @@ class BasePersonVocabulary:
             return term
 
 
-class KarmaCategoryVocabulary(NamedSQLObjectVocabulary):
+class KarmaCategoryVocabulary(NamedStormVocabulary):
     """All `IKarmaCategory` objects vocabulary."""
     _table = KarmaCategory
-    _orderBy = 'name'
+    _order_by = "name"
 
 
 @implementer(IHugeVocabulary)
diff --git a/lib/lp/services/worlddata/doc/language.rst b/lib/lp/services/worlddata/doc/language.rst
index ad4c280..4a4db88 100644
--- a/lib/lp/services/worlddata/doc/language.rst
+++ b/lib/lp/services/worlddata/doc/language.rst
@@ -242,24 +242,31 @@ have the language among their preferred languages.
     >>> translator_40.addLanguage(sr)
 
     # We need to fake some Karma.
-    >>> from lp.registry.model.karma import KarmaCategory, KarmaCache
+    >>> from lp.registry.interfaces.karma import IKarmaCacheManager
+    >>> from lp.registry.model.karma import KarmaCategory
+    >>> from lp.services.database.interfaces import IStore
     >>> from lp.testing.dbuser import switch_dbuser
 
     >>> switch_dbuser('karma')
-    >>> translations_category = KarmaCategory.selectOne(
-    ...     KarmaCategory.name=='translations')
-    >>> karma = KarmaCache(person=translator_30,
-    ...                    category=translations_category,
-    ...                    karmavalue=30)
-    >>> karma = KarmaCache(person=translator_10,
-    ...                    category=translations_category,
-    ...                    karmavalue=10)
-    >>> karma = KarmaCache(person=translator_20,
-    ...                    category=translations_category,
-    ...                    karmavalue=20)
-    >>> karma = KarmaCache(person=translator_40,
-    ...                    category=translations_category,
-    ...                    karmavalue=40)
+    >>> translations_category = IStore(KarmaCategory).find(
+    ...     KarmaCategory, name="translations").one()
+    >>> cache_manager = getUtility(IKarmaCacheManager)
+    >>> karma = cache_manager.new(
+    ...     person_id=translator_30.id,
+    ...     category_id=translations_category.id,
+    ...     value=30)
+    >>> karma = cache_manager.new(
+    ...     person_id=translator_10.id,
+    ...     category_id=translations_category.id,
+    ...     value=10)
+    >>> karma = cache_manager.new(
+    ...     person_id=translator_20.id,
+    ...     category_id=translations_category.id,
+    ...     value=20)
+    >>> karma = cache_manager.new(
+    ...     person_id=translator_40.id,
+    ...     category_id=translations_category.id,
+    ...     value=40)
     >>> switch_dbuser('launchpad')
     >>> for translator in sr.translators:
     ...   print(translator.name)
diff --git a/lib/lp/services/worlddata/model/language.py b/lib/lp/services/worlddata/model/language.py
index af89652..fc72fab 100644
--- a/lib/lp/services/worlddata/model/language.py
+++ b/lib/lp/services/worlddata/model/language.py
@@ -186,13 +186,12 @@ class LanguageSet:
                 KarmaCategory,
                 And(
                     KarmaCategory.name == 'translations',
-                    KarmaCache.categoryID == KarmaCategory.id,
-                    KarmaCache.productID == None,
-                    KarmaCache.projectgroupID == None,
-                    KarmaCache.sourcepackagenameID == None,
-                    KarmaCache.distributionID == None)),
-            PersonLanguage.person_id ==
-                KarmaCache.personID)
+                    KarmaCache.category_id == KarmaCategory.id,
+                    KarmaCache.product == None,
+                    KarmaCache.projectgroup == None,
+                    KarmaCache.sourcepackagename == None,
+                    KarmaCache.distribution == None)),
+            PersonLanguage.person_id == KarmaCache.person_id)
 
     @property
     def _visible_languages(self):
diff --git a/lib/lp/soyuz/stories/distribution/xx-distribution-packages.rst b/lib/lp/soyuz/stories/distribution/xx-distribution-packages.rst
index abebea1..de2c94e 100644
--- a/lib/lp/soyuz/stories/distribution/xx-distribution-packages.rst
+++ b/lib/lp/soyuz/stories/distribution/xx-distribution-packages.rst
@@ -127,17 +127,17 @@ It will thus not be listed in the "...other untrusted versions of..." portlet.
     # karma for their efforts.
     >>> from lp.registry.model.karma import KarmaTotalCache
     >>> from lp.testing.dbuser import dbuser
-    >>> ppa_beta_owner_id = ppa_beta.owner.id
-    >>> ppa_nightly_owner_id = ppa_nightly.owner.id
-    >>> ppa_disabled_owner_id = ppa_disabled.owner.id
+    >>> ppa_beta_owner = ppa_beta.owner
+    >>> ppa_nightly_owner = ppa_nightly.owner
+    >>> ppa_disabled_owner = ppa_disabled.owner
     >>> ppa_disabled.disable()
     >>> with dbuser('karma'):
     ...     cache_entry = KarmaTotalCache(
-    ...         person=ppa_beta_owner_id, karma_total=200)
+    ...         person=ppa_beta_owner, karma_total=200)
     ...     cache_entry = KarmaTotalCache(
-    ...         person=ppa_nightly_owner_id, karma_total=201)
+    ...         person=ppa_nightly_owner, karma_total=201)
     ...     cache_entry = KarmaTotalCache(
-    ...         person=ppa_disabled_owner_id, karma_total=202)
+    ...         person=ppa_disabled_owner, karma_total=202)
 
     >>> logout()
 
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index c3a709c..88ee88c 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -710,7 +710,7 @@ class BareLaunchpadObjectFactory(ObjectFactory):
         if karma is not None:
             with dbuser('karma'):
                 # Give the user karma to make the user non-probationary.
-                KarmaTotalCache(person=person.id, karma_total=karma)
+                KarmaTotalCache(person=person, karma_total=karma)
         # Ensure updated ValidPersonCache
         flush_database_updates()
         return person
diff --git a/lib/lp/translations/doc/rosetta-karma.rst b/lib/lp/translations/doc/rosetta-karma.rst
index caad946..524c48c 100644
--- a/lib/lp/translations/doc/rosetta-karma.rst
+++ b/lib/lp/translations/doc/rosetta-karma.rst
@@ -392,7 +392,9 @@ Now, let's ensure that we've covered every one of Rosetta's karma
 actions.
 
     >>> from lp.registry.model.karma import KarmaCategory
-    >>> translation_category = KarmaCategory.byName('translations')
+    >>> from lp.services.database.interfaces import IStore
+    >>> translation_category = IStore(KarmaCategory).find(
+    ...     KarmaCategory, name="translations").one()
     >>> for karma_action in translation_category.karmaactions:
     ...     assert karma_action in karma_helper.added_karma_actions, (
     ...         '%s was not test!' % karma_action.name)
diff --git a/lib/lp/translations/doc/translationsoverview.rst b/lib/lp/translations/doc/translationsoverview.rst
index 8465166..cbafdf7 100644
--- a/lib/lp/translations/doc/translationsoverview.rst
+++ b/lib/lp/translations/doc/translationsoverview.rst
@@ -19,6 +19,7 @@ test too much.
     >>> from lp.registry.interfaces.product import IProductSet
     >>> from lp.registry.model.karma import KarmaCategory
     >>> from lp.registry.model.sourcepackagename import SourcePackageName
+    >>> from lp.services.database.interfaces import IStore
     >>> from lp.translations.interfaces.translationsoverview import (
     ...     ITranslationsOverview)
     >>> from lp.testing.dbuser import switch_dbuser
@@ -100,7 +101,8 @@ Adding some translations karma attributed to Carlos will make
 alsa-utils displayed among the top translated pillars as well.
 
     >>> carlos = person_set.getByName('carlos')
-    >>> translations = KarmaCategory.byName('translations')
+    >>> translations = IStore(KarmaCategory).find(
+    ...     KarmaCategory, name="translations").one()
     >>> alsa_utils = product_set.getByName('alsa-utils')
 
     >>> start_karma_update()