changeset 2145:7fa2d72bcd1f

determining subscribers by subscription_ids
author Ana Balica <ana.balica@gmail.com>
date Tue, 02 Jul 2013 23:35:52 +0300
parents 8c952ec3222e
children f370ed053c95
files MoinMoin/config/default.py MoinMoin/constants/keys.py MoinMoin/storage/middleware/indexing.py MoinMoin/storage/middleware/validation.py MoinMoin/util/_tests/test_subscriptions.py MoinMoin/util/subscriptions.py
diffstat 6 files changed, 163 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/MoinMoin/config/default.py	Fri Jun 28 20:20:07 2013 +0300
+++ b/MoinMoin/config/default.py	Tue Jul 02 23:35:52 2013 +0300
@@ -490,6 +490,7 @@
             BOOKMARKS: {},
             QUICKLINKS: [],
             SUBSCRIBED_ITEMS: [],
+            SUBSCRIPTION_IDS: [],
             EMAIL_SUBSCRIBED_EVENTS: [
                 # XXX PageChangedEvent.__name__
                 # XXX PageRenamedEvent.__name__
--- a/MoinMoin/constants/keys.py	Fri Jun 28 20:20:07 2013 +0300
+++ b/MoinMoin/constants/keys.py	Tue Jul 02 23:35:52 2013 +0300
@@ -22,6 +22,7 @@
 # needs more precise name / use case:
 SOMEDICT = u"somedict"
 
+# TODO review plural constants
 CONTENTTYPE = u"contenttype"
 ITEMTYPE = u"itemtype"
 SIZE = u"size"
@@ -67,6 +68,7 @@
 TIMEZONE = u"timezone"
 ENC_PASSWORD = u"enc_password"
 SUBSCRIBED_ITEMS = u"subscribed_items"
+SUBSCRIPTION_IDS = u"subscription_ids"
 BOOKMARKS = u"bookmarks"
 QUICKLINKS = u"quicklinks"
 SESSION_KEY = u"session_key"
@@ -90,8 +92,8 @@
     # User objects proxy these attributes of the UserProfile objects:
     NAME, DISABLED, ITEMID, DISPLAY_NAME, ENC_PASSWORD, EMAIL, OPENID,
     MAILTO_AUTHOR, SHOW_COMMENTS, RESULTS_PER_PAGE, EDIT_ON_DOUBLECLICK, SCROLL_PAGE_AFTER_EDIT,
-    EDIT_ROWS, THEME_NAME, LOCALE, TIMEZONE, SUBSCRIBED_ITEMS, QUICKLINKS,
-    CSS_URL,
+    EDIT_ROWS, THEME_NAME, LOCALE, TIMEZONE, SUBSCRIBED_ITEMS, SUBSCRIPTION_IDS,
+    QUICKLINKS, CSS_URL,
 ]
 
 # keys for blog homepages
--- a/MoinMoin/storage/middleware/indexing.py	Fri Jun 28 20:20:07 2013 +0300
+++ b/MoinMoin/storage/middleware/indexing.py	Tue Jul 02 23:35:52 2013 +0300
@@ -322,6 +322,8 @@
             EMAIL: ID(stored=True),
             OPENID: ID(stored=True),
             DISABLED: BOOLEAN(stored=True),
+            LOCALE: ID(stored=True),
+            SUBSCRIPTION_IDS: ID(),
         }
         latest_revs_fields.update(**userprofile_fields)
 
--- a/MoinMoin/storage/middleware/validation.py	Fri Jun 28 20:20:07 2013 +0300
+++ b/MoinMoin/storage/middleware/validation.py	Tue Jul 02 23:35:52 2013 +0300
@@ -369,6 +369,7 @@
     Boolean.named(keys.MAILTO_AUTHOR).using(optional=True),
     List.named(keys.QUICKLINKS).of(String.named('quicklinks')).using(optional=True),
     List.named(keys.SUBSCRIBED_ITEMS).of(String.named('subscribed_item')).using(optional=True),
+    List.named(keys.SUBSCRIPTION_IDS).of(String.named('subscription_id')).using(optional=True),
     List.named(keys.EMAIL_SUBSCRIBED_EVENTS).of(String.named('email_subscribed_event')).using(optional=True),
     #TODO: DuckDict.named('bookmarks').using(optional=True),
     *common_meta
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/util/_tests/test_subscriptions.py	Tue Jul 02 23:35:52 2013 +0300
@@ -0,0 +1,104 @@
+# Copyright: 2013 MoinMoin:AnaBalica
+# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.
+
+"""
+    MoinMoin - MoinMoin.util.notifications Tests
+"""
+
+
+from flask import g as flaskg
+
+from whoosh.query import Term
+
+from MoinMoin import user
+from MoinMoin.items import Item
+from MoinMoin.constants.keys import (ITEMID, CONTENTTYPE, LATEST_REVS, NAME, TAGS)
+from MoinMoin.util.subscriptions import get_subscribers, extract_users_info
+
+
+class TestNotifications(object):
+    reinit_storage = True
+
+    def setup_method(self, method):
+        # create an item
+        self.item_name = u'foo'
+        self.tagname = u'XXX'
+        self.namespace = u''
+        meta = {CONTENTTYPE: u'text/plain;charset=utf-8', TAGS: [self.tagname]}
+        item = Item.create(self.item_name)
+        item._save(meta)
+        self.item = Item.create(self.item_name)
+
+    def test_get_subscribers_empty(self):
+        users = get_subscribers(self.item)
+        assert users == set()
+        name = u'baz'
+        password = u'password'
+        email = u'baz@example.org'
+        meta = dict(locale=u'en')
+        user.create_user(username=name, password=password, email=email, **meta)
+        subscribers = get_subscribers(self.item)
+        assert subscribers == set()
+
+    def test_get_subscribers_by_itemid(self):
+        name = u'bar'
+        password = u'password'
+        email = u'bar@example.org'
+        subscriptions = ["{0}:{1}".format(ITEMID, self.item.meta[ITEMID])]
+        meta = dict(locale=u'en', subscription_ids=subscriptions)
+        u = user.create_user(username=name, password=password, email=email, **meta)
+        u = user.User(name=name, password=password)
+        subscribers = get_subscribers(self.item)
+        subscribers_names = [subscriber.name for subscriber in subscribers]
+        expected_name = u.profile._meta[NAME][0]
+        assert expected_name in subscribers_names
+
+    def test_get_subscribers_by_tag(self):
+        name = u'barfoo'
+        password = u'password'
+        email = u'barfoo@examle.org'
+        subscriptions = ["{0}:{1}:{2}".format(TAGS, self.namespace, self.tagname)]
+        meta = dict(locale=u'en', subscription_ids=subscriptions)
+        user.create_user(username=name, password=password, email=email, **meta)
+        u = user.User(name=name, password=password)
+        subscribers = get_subscribers(self.item)
+        subscribers_names = [subscriber.name for subscriber in subscribers]
+        expected_name = u.profile._meta[NAME][0]
+        assert expected_name in subscribers_names
+
+    def test_get_subscribers_by_name(self):
+        name = u'foobar'
+        password = u'password'
+        email = u"foobar@example.org"
+        subscriptions = ["{0}:{1}:{2}".format(NAME, self.namespace, self.item_name)]
+        meta = dict(locale=u'en', subscription_ids=subscriptions)
+        user.create_user(username=name, password=password, email=email, **meta)
+        u = user.User(name=name, password=password)
+        subscribers = get_subscribers(self.item)
+        subscribers_names = [subscriber.name for subscriber in subscribers]
+        expected_name = u.profile._meta[NAME][0]
+        assert expected_name in subscribers_names
+
+    def test_extract_users_info_empty(self):
+        query = Term(u'namespace', u'userprofiles')
+        with flaskg.storage.indexer.ix[LATEST_REVS].searcher() as searcher:
+            results = searcher.search(query)
+            users = extract_users_info(results)
+            assert users == set()
+
+    def test_extract_users_info(self):
+        meta = dict(locale=u'en')
+        user.create_user(username=u'foo', password=u'password',
+                         email=u'foo@example.org', **meta)
+        user.create_user(username=u'bar', password=u'password',
+                         email=u'bar@example.org', **meta)
+        u1 = user.User(name=u"foo", password=u"password")
+        u2 = user.User(name=u"bar", password=u"password")
+        query = Term(u'namespace', u'userprofiles')
+        with flaskg.storage.indexer.ix[LATEST_REVS].searcher() as searcher:
+            results = searcher.search(query)
+            users = extract_users_info(results)
+            users_itemids = set([u.name for u in users])
+            expected_users = set([u1, u2])
+            expected_users_names = set([u.name0 for u in expected_users])
+            assert users_itemids == expected_users_names
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MoinMoin/util/subscriptions.py	Tue Jul 02 23:35:52 2013 +0300
@@ -0,0 +1,51 @@
+# Copyright: 2013 MoinMoin:AnaBalica
+# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.
+
+"""
+    MoinMoin - Subscriptions
+"""
+
+
+from flask import g as flaskg
+
+from collections import namedtuple
+from whoosh.query import Term, Or
+
+from MoinMoin.constants.keys import (EMAIL, ITEMID, LATEST_REVS, LOCALE, NAME,
+                                     NAMESPACE, SUBSCRIPTION_IDS, TAGS)
+
+
+Subscriber = namedtuple('Subscriber', [ITEMID, NAME, EMAIL, LOCALE])
+
+
+def get_subscribers(item):
+    """ Get all users that are subscribed to the item
+
+    :param item: Item object
+    :return: a set of all item subscribers
+    """
+    meta = item.meta
+    namespace = meta[NAMESPACE]
+    terms = [Term(SUBSCRIPTION_IDS, "{0}:{1}".format(ITEMID, meta[ITEMID]))]
+    terms.extend(Term(SUBSCRIPTION_IDS, "{0}:{1}:{2}".format(NAME, namespace, name))
+                 for name in meta[NAME])
+    terms.extend(Term(SUBSCRIPTION_IDS, "{0}:{1}:{2}".format(TAGS, namespace, tag))
+                 for tag in meta[TAGS])
+    query = Or(terms)
+    with flaskg.storage.indexer.ix[LATEST_REVS].searcher() as searcher:
+        results = searcher.search(query, limit=None)
+        subscribers = extract_users_info(results)
+    return subscribers
+
+
+def extract_users_info(user_items):
+    """ Extract user information (itemid, email and locale) and store it to
+    Subscriber objects.
+
+    :param user_items: whoosh.searching.Results object that contains user profile Hits
+    :return: a set of users
+    """
+    # store just the first name
+    users = {Subscriber(user[ITEMID], user[NAME][0], user[EMAIL], user[LOCALE])
+             for user in user_items}
+    return users