Просмотр исходного кода

trees_map helper object for handling different thread types

Rafał Pitoń 9 лет назад
Родитель
Сommit
cc8cae30e3

+ 0 - 46
misago/categories/categorytypes.py

@@ -1,46 +0,0 @@
-from django.core.urlresolvers import reverse
-from django.utils.translation import ugettext_lazy as _
-
-from misago.threads.threadtypes.thread import Thread
-
-
-class RootCategory(Thread):
-    type_name = 'root_category'
-
-    def get_category_name(self, category):
-        return _('None (will become top level category)')
-
-    def get_category_absolute_url(self, category):
-        return reverse('misago:threads')
-
-    def get_category_last_thread_url(self, category):
-        return '/threads/%s-%s/' % (
-            category.last_thread_slug,
-            category.last_thread_id,
-        )
-
-    def get_category_last_post_url(self, category):
-        return '/threads/%s-%s/last/' % (
-            category.last_thread_slug,
-            category.last_thread_id,
-        )
-
-    def get_category_api_read_url(self, category):
-        return reverse('misago:api:thread-read')
-
-
-class Category(RootCategory):
-    type_name = 'category'
-
-    def get_category_name(self, category):
-        return category.name
-
-    def get_category_absolute_url(self, category):
-        return reverse('misago:category', kwargs={
-            'pk': category.pk,
-            'slug': category.slug,
-        })
-
-    def get_category_api_read_url(self, category):
-        return '%s?category=%s' % (
-            reverse('misago:api:thread-read'), category.pk)

+ 2 - 2
misago/categories/models.py

@@ -11,7 +11,7 @@ from misago.acl.models import BaseRole
 from misago.conf import settings
 from misago.core.cache import cache
 from misago.core.utils import slugify
-from misago.threads import threadtypes
+from misago.threads.threadtypes import trees_map
 
 
 CACHE_NAME = 'misago_categories_tree'
@@ -105,7 +105,7 @@ class Category(MPTTModel):
 
     @property
     def thread_type(self):
-        return threadtypes.get(self.special_role or 'category')
+        return trees_map.get_type_for_tree_id(self.tree_id)
 
     def __unicode__(self):
         return unicode(self.thread_type.get_category_name(self))

+ 0 - 5
misago/conf/defaults.py

@@ -135,11 +135,6 @@ MISAGO_POSTING_MIDDLEWARES = (
 )
 
 MISAGO_THREAD_TYPES = (
-    # category and redirect types
-    'misago.categories.categorytypes.RootCategory',
-    'misago.categories.categorytypes.Category',
-
-    # real thread types
     'misago.threads.threadtypes.thread.Thread',
     'misago.threads.threadtypes.privatethread.PrivateThread',
 )

+ 92 - 0
misago/threads/tests/test_typestree.py

@@ -0,0 +1,92 @@
+from django.test import TestCase
+from misago.categories.models import Category
+from misago.threads.threadtypes import TreesMap
+
+
+THREAD_TYPE = 'misago.threads.threadtypes.thread.Thread'
+
+
+class TreesMapTests(TestCase):
+    def test_load_types(self):
+        """TreesMap().load_types() loads types modules"""
+        trees_map = TreesMap(None)
+        types = trees_map.load_types([THREAD_TYPE])
+
+        self.assertEqual(len(types), 1, "expected to load only one thread type")
+        self.assertIn('root_category', types, "invalid thread type was loaded")
+
+    def test_load_trees(self):
+        """TreesMap().load_trees() loads trees ids"""
+        trees_map = TreesMap(None)
+        types = trees_map.load_types([THREAD_TYPE])
+        trees = trees_map.load_trees(types)
+
+        tree_id = Category.objects.get(special_role='root_category').tree_id
+
+        self.assertEqual(len(trees), 1, "expected to load only one tree")
+        self.assertEqual(trees[tree_id].root_name, 'root_category', "invalid tree was loaded")
+
+    def test_get_roots(self):
+        """TreesMap().get_roots() returns roots to trees dict"""
+        trees_map = TreesMap(None)
+        types = trees_map.load_types([THREAD_TYPE])
+        trees = trees_map.load_trees(types)
+        roots = trees_map.get_roots(trees)
+
+        tree_id = Category.objects.get(special_role='root_category').tree_id
+
+        self.assertEqual(len(roots), 1, "expected to load only one root")
+        self.assertIn('root_category', roots, "invalid root was loaded")
+        self.assertEqual(roots['root_category'], tree_id, "invalid tree_id was loaded")
+
+    def test_load(self):
+        """TreesMap().load() populates trees map"""
+        trees_map = TreesMap([THREAD_TYPE])
+
+        self.assertFalse(trees_map.is_loaded, "trees map should be not loaded by default")
+
+        trees_map.load()
+
+        self.assertTrue(trees_map.is_loaded, "trees map should be loaded after call to load()")
+
+        self.assertEqual(len(trees_map.types), 1, "expected to load one type")
+        self.assertEqual(len(trees_map.trees), 1, "expected to load one tree")
+        self.assertEqual(len(trees_map.roots), 1, "expected to load one root")
+
+        tree_id = Category.objects.get(special_role='root_category').tree_id
+
+        self.assertIn('root_category', trees_map.types, "invalid thread type was loaded")
+        self.assertEqual(trees_map.trees[tree_id].root_name, 'root_category', "invalid tree was loaded")
+        self.assertIn('root_category', trees_map.roots, "invalid root was loaded")
+
+    def test_get_type_for_tree_id(self):
+        """TreesMap().get_type_for_tree_id() returns type for valid id"""
+        trees_map = TreesMap([THREAD_TYPE])
+        trees_map.load()
+
+        tree_id = Category.objects.get(special_role='root_category').tree_id
+        thread_type = trees_map.get_type_for_tree_id(tree_id)
+
+        self.assertEqual(thread_type.root_name, 'root_category', "returned invalid thread type for given tree id")
+
+        try:
+            trees_map.get_type_for_tree_id(tree_id + 1000)
+            self.fail("invalid tree id should cause KeyError being raised")
+        except KeyError as e:
+            self.assertIn("tree id has no type defined", unicode(e), "invalid exception message as given")
+
+    def test_get_tree_id_for_root(self):
+        """TreesMap().get_tree_id_for_root() returns tree id for valid type name"""
+        trees_map = TreesMap([THREAD_TYPE])
+        trees_map.load()
+
+        in_db_tree_id = Category.objects.get(special_role='root_category').tree_id
+        tree_id = trees_map.get_tree_id_for_root('root_category')
+
+        self.assertEqual(tree_id, in_db_tree_id, "root name didn't match one in database")
+
+        try:
+            trees_map.get_tree_id_for_root('hurr_durr')
+            self.fail("invalid root name should cause KeyError being raised")
+        except KeyError as e:
+            self.assertIn('"hurr_durr" root has no tree defined', unicode(e), "invalid exception message as given")

+ 54 - 19
misago/threads/threadtypes/__init__.py

@@ -1,30 +1,65 @@
 from importlib import import_module
-
 from django.conf import settings
-from django.utils.translation import ugettext_lazy as _
 
 
-class ThreadTypeBase(object):
-    type_name = 'undefined'
+class TreesMap(object):
+    """Object that maps trees to strategies"""
+    def __init__(self, types_modules):
+        self.is_loaded = False
+        self.types_modules = types_modules
+
+    def load(self):
+        self.types = self.load_types(self.types_modules)
+        self.trees = self.load_trees(self.types)
+        self.roots = self.get_roots(self.trees)
+        self.is_loaded = True
+
+    def load_types(self, types_modules):
+        loaded_types = {}
+        for path in types_modules:
+            module = import_module('.'.join(path.split('.')[:-1]))
+            type_cls = getattr(module, path.split('.')[-1])
+            loaded_types[type_cls.root_name] = type_cls()
+        return loaded_types
+
+    def load_trees(self, types):
+        from misago.categories.models import Category
+        trees = {}
+        for category in Category.objects.filter(level=0, special_role__in=types.keys()):
+            trees[category.tree_id] = types[category.special_role]
+        return trees
+
+    def get_roots(self, trees):
+        roots = {}
+        for tree_id in trees:
+            roots[trees[tree_id].root_name] = tree_id
+        return roots
+
+    def get_type_for_tree_id(self, tree_id):
+        if not self.is_loaded:
+            self.load()
+
+        try:
+            return self.trees[tree_id]
+        except KeyError:
+            raise KeyError("'%s' tree id has no type defined" % tree_id)
 
-    def get_forum_name(self, forum):
-        return forum.name
+    def get_tree_id_for_root(self, root_name):
+        if not self.is_loaded:
+            self.load()
 
+        try:
+            return self.roots[root_name]
+        except KeyError:
+            raise KeyError('"%s" root has no tree defined' % root_name)
 
-def load_types(types_list):
-    loaded_types = {}
-    for path in types_list:
-        module = import_module('.'.join(path.split('.')[:-1]))
-        type_obj = getattr(module, path.split('.')[-1])()
-        loaded_types[type_obj.type_name] = type_obj
-    return loaded_types
 
+trees_map = TreesMap(settings.MISAGO_THREAD_TYPES)
 
-THREAD_TYPES = load_types(settings.MISAGO_THREAD_TYPES)
 
+class ThreadType(object):
+    """Abstract class for thread type strategy"""
+    root_name = 'undefined'
 
-def get(thread_type):
-    try:
-        return THREAD_TYPES[thread_type]
-    except KeyError:
-        raise KeyError("thread type %s is undefined" % thread_type)
+    def get_forum_name(self, category):
+        return category.name

+ 3 - 4
misago/threads/threadtypes/privatethread.py

@@ -1,11 +1,10 @@
 from django.core.urlresolvers import reverse
 from django.utils.translation import ugettext_lazy as _
+from misago.threads.threadtypes import ThreadType
 
-from misago.threads.threadtypes import ThreadTypeBase
 
-
-class PrivateThread(ThreadTypeBase):
-    type_name = 'private_threads'
+class PrivateThread(ThreadType):
+    root_name = 'private_threads'
 
     def get_category_name(self, category):
         return _('Private Threads')

+ 33 - 9
misago/threads/threadtypes/thread.py

@@ -1,19 +1,43 @@
 from django.core.urlresolvers import reverse
+from django.utils.translation import ugettext_lazy as _
+from misago.threads.threadtypes import ThreadType
 
-from misago.threads.threadtypes import ThreadTypeBase
 
-
-class Thread(ThreadTypeBase):
-    type_name = 'thread'
+class Thread(ThreadType):
+    root_name = 'root_category'
 
     def get_category_name(self, category):
-        return category.name
+        if category.level:
+            return category.name
+        else:
+            return _('None (will become top level category)')
 
     def get_category_absolute_url(self, category):
-        return reverse('misago:category', kwargs={
-            'pk': category.pk,
-            'slug': category.slug,
-        })
+        if category.level:
+            return reverse('misago:category', kwargs={
+                'pk': category.pk,
+                'slug': category.slug,
+            })
+        else:
+            return reverse('misago:threads')
+
+    def get_category_last_thread_url(self, category):
+        return '/threads/%s-%s/' % (
+            category.last_thread_slug,
+            category.last_thread_id,
+        )
+
+    def get_category_last_post_url(self, category):
+        return '/threads/%s-%s/last/' % (
+            category.last_thread_slug,
+            category.last_thread_id,
+        )
+
+    def get_category_api_read_url(self, category):
+        if category.level:
+            return '%s?category=%s' % (reverse('misago:api:thread-read'), category.pk)
+        else:
+            return reverse('misago:api:thread-read')
 
     def get_thread_absolute_url(self, thread, page=1):
         if page > 1: