Browse Source

Move the helpers module to the utils package

sh4nks 11 years ago
parent
commit
ec71d481f9
5 changed files with 361 additions and 108 deletions
  1. 0 0
      flaskbb/utils/__init__.py
  2. 27 108
      flaskbb/utils/helpers.py
  3. 212 0
      flaskbb/utils/populate.py
  4. 49 0
      flaskbb/utils/types.py
  5. 73 0
      flaskbb/utils/wtforms.py

+ 0 - 0
flaskbb/utils/__init__.py


+ 27 - 108
flaskbb/helpers.py → flaskbb/utils/helpers.py

@@ -1,9 +1,9 @@
 # -*- coding: utf-8 -*-
 """
-    flaskbb.utils
+    flaskbb.utils.helpers
     ~~~~~~~~~~~~~~~~~~~~
 
-    A few utils that are used by flaskbb
+    A few helpers that are used by flaskbb
 
     :copyright: (c) 2013 by the FlaskBB Team.
     :license: BSD, see LICENSE for more details.
@@ -12,9 +12,6 @@ import time
 from datetime import datetime, timedelta
 
 from flask import current_app
-from sqlalchemy import types
-from sqlalchemy.ext.mutable import Mutable
-from wtforms.widgets.core import Select, HTMLString, html_params
 from postmarkup import render_bbcode
 
 from flaskbb.extensions import redis
@@ -41,6 +38,9 @@ def mark_online(user_id, guest=False):
 
 
 def get_last_user_activity(user_id, guest=False):
+    """
+    Returns the last active time from a given `user_id`.
+    """
     if guest:
         last_active = redis.get('guest-activity/%s' % user_id)
     else:
@@ -52,6 +52,9 @@ def get_last_user_activity(user_id, guest=False):
 
 
 def get_online_users(guest=False):
+    """
+    Returns all online users within a specified time range
+    """
     current = int(time.time()) // 60
     minutes = xrange(current_app.config['ONLINE_LAST_MINUTES'])
     if guest:
@@ -85,7 +88,7 @@ def can_moderate(user, forum):
     return user.permissions['super_mod'] or user.permissions['admin']
 
 
-def perm_edit_post(user, post_user_id, forum):
+def can_edit_post(user, post_user_id, forum):
     """
     Check if the post can be edited by the user
     """
@@ -93,7 +96,7 @@ def perm_edit_post(user, post_user_id, forum):
                       post_user_id=post_user_id)
 
 
-def perm_delete_post(user, post_user_id, forum):
+def can_delete_post(user, post_user_id, forum):
     """
     Check if the post can be deleted by the user
     """
@@ -101,7 +104,7 @@ def perm_delete_post(user, post_user_id, forum):
                       post_user_id=post_user_id)
 
 
-def perm_delete_topic(user, post_user_id, forum):
+def can_delete_topic(user, post_user_id, forum):
     """
     Check if the topic can be deleted by the user
     """
@@ -109,14 +112,14 @@ def perm_delete_topic(user, post_user_id, forum):
                       post_user_id=post_user_id)
 
 
-def perm_post_reply(user, forum):
+def can_post_reply(user, forum):
     """
     Check if the user is allowed to post in the forum
     """
     return check_perm(user=user, perm='postreply', forum=forum)
 
 
-def perm_post_topic(user, forum):
+def can_post_topic(user, forum):
     """
     Check if the user is allowed to create a new topic in the forum
     """
@@ -134,14 +137,25 @@ def crop_title(title):
 
 
 def render_markup(text):
+    """
+    Renders the given text as bbcode
+    """
     return render_bbcode(text)
 
 
 def is_online(user):
+    """
+    A simple check, to see if the user was online
+    within a specified time range
+    """
     return user.lastseen >= time_diff()
 
 
 def time_diff():
+    """
+    Calculates the time difference between `now` and the ONLINE_LAST_MINUTES
+    variable from the configuration.
+    """
     now = datetime.utcnow()
     diff = now - timedelta(minutes=current_app.config['ONLINE_LAST_MINUTES'])
     return diff
@@ -155,6 +169,9 @@ def format_date(value, format='%Y-%m-%d'):
 
 
 def time_since(value):
+    """
+    Just a interface for `time_delta_format`
+    """
     return time_delta_format(value)
 
 
@@ -192,101 +209,3 @@ def time_delta_format(dt, default=None):
             return u'%d %s ago' % (period, plural)
 
     return default
-
-
-class DenormalizedText(Mutable, types.TypeDecorator):
-    """
-    Stores denormalized primary keys that can be
-    accessed as a set.
-
-    :param coerce: coercion function that ensures correct
-                   type is returned
-
-    :param separator: separator character
-
-    Source: https://github.com/imwilsonxu/fbone/blob/master/fbone/user/models.py#L13-L45
-    """
-
-    impl = types.Text
-
-    def __init__(self, coerce=int, separator=" ", **kwargs):
-
-        self.coerce = coerce
-        self.separator = separator
-
-        super(DenormalizedText, self).__init__(**kwargs)
-
-    def process_bind_param(self, value, dialect):
-        if value is not None:
-            items = [str(item).strip() for item in value]
-            value = self.separator.join(item for item in items if item)
-        return value
-
-    def process_result_value(self, value, dialect):
-        if not value:
-            return set()
-        return set(self.coerce(item) for item in value.split(self.separator))
-
-    def copy_value(self, value):
-        return set(value)
-
-
-class SelectDateWidget(object):
-    """
-    Renders a DateTime field with 3 selects.
-    For more information see: http://stackoverflow.com/a/14664504
-    """
-    FORMAT_CHOICES = {
-        '%d': [(x, str(x)) for x in range(1, 32)],
-        '%m': [(x, str(x)) for x in range(1, 13)]
-    }
-
-    FORMAT_CLASSES = {
-        '%d': 'select_date_day',
-        '%m': 'select_date_month',
-        '%Y': 'select_date_year'
-    }
-
-    def __init__(self, years=range(1930, datetime.utcnow().year+1)):
-        super(SelectDateWidget, self).__init__()
-        self.FORMAT_CHOICES['%Y'] = [(x, str(x)) for x in years]
-
-    def __call__(self, field, **kwargs):
-        field_id = kwargs.pop('id', field.id)
-        html = []
-        allowed_format = ['%d', '%m', '%Y']
-
-        for format in field.format.split():
-            if (format in allowed_format):
-                choices = self.FORMAT_CHOICES[format]
-                id_suffix = format.replace('%', '-')
-                id_current = field_id + id_suffix
-
-                kwargs['class'] = self.FORMAT_CLASSES[format]
-                try:
-                    del kwargs['placeholder']
-                except:
-                    pass
-
-                html.append('<select %s>' % html_params(name=field.name,
-                                                        id=id_current,
-                                                        **kwargs))
-
-                if field.data:
-                    current_value = int(field.data.strftime(format))
-                else:
-                    current_value = None
-
-                for value, label in choices:
-                    selected = (value == current_value)
-                    html.append(Select.render_option(value, label, selected))
-                html.append('</select>')
-            else:
-                html.append(format)
-                html.append(
-                    """<input type="hidden" value="{}" {}></input>""".format(
-                        html_params(name=field.name, id=id_current, **kwargs)))
-
-            html.append(' ')
-
-        return HTMLString(''.join(html))

+ 212 - 0
flaskbb/utils/populate.py

@@ -0,0 +1,212 @@
+# -*- coding: utf-8 -*-
+"""
+    flaskbb.utils.populate
+    ~~~~~~~~~~~~~~~~~~~~
+
+    A module that makes creating data more easily
+
+    :copyright: (c) 2013 by the FlaskBB Team.
+    :license: BSD, see LICENSE for more details.
+"""
+from collections import OrderedDict
+
+from flaskbb.extensions import db
+from flaskbb.user.models import User, Group
+from flaskbb.forum.models import Post, Topic, Forum
+
+
+GROUPS = OrderedDict((
+    ('Administrator', {
+        'description': 'The Administrator Group',
+        'admin': True,
+        'super_mod': False,
+        'mod': False,
+        'banned': False,
+        'guest': False,
+        'editpost': True,
+        'deletepost': True,
+        'deletetopic': True,
+        'posttopic': True,
+        'postreply': True,
+        'viewtopic': True,
+        'viewprofile': True
+    }),
+    ('Super Moderator', {
+        'description': 'The Super Moderator Group',
+        'admin': False,
+        'super_mod': True,
+        'mod': False,
+        'banned': False,
+        'guest': False,
+        'editpost': True,
+        'deletepost': True,
+        'deletetopic': True,
+        'posttopic': True,
+        'postreply': True,
+        'viewtopic': True,
+        'viewprofiles': True
+    }),
+    ('Moderator', {
+        'description': 'The Moderator Group',
+        'admin': False,
+        'super_mod': False,
+        'mod': True,
+        'banned': False,
+        'guest': False,
+        'editpost': True,
+        'deletepost': True,
+        'deletetopic': True,
+        'posttopic': True,
+        'postreply': True,
+        'viewtopic': True,
+        'viewprofile': True
+    }),
+    ('Member', {
+        'description': 'The Member Group',
+        'admin': False,
+        'super_mod': False,
+        'mod': False,
+        'banned': False,
+        'guest': False,
+        'editpost': True,
+        'deletepost': False,
+        'deletetopic': False,
+        'posttopic': True,
+        'postreply': True,
+        'viewtopic': True,
+        'viewprofile': True
+    }),
+    ('Banned', {
+        'description': 'The Banned Group',
+        'admin': False,
+        'super_mod': False,
+        'mod': False,
+        'banned': True,
+        'guest': False,
+        'editpost': False,
+        'deletepost': False,
+        'deletetopic': False,
+        'posttopic': False,
+        'postreply': False,
+        'viewtopic': False,
+        'viewprofile': False
+    }),
+    ('Guest', {
+        'description': 'The Guest Group',
+        'admin': False,
+        'super_mod': False,
+        'mod': False,
+        'banned': False,
+        'guest': True,
+        'editpost': False,
+        'deletepost': False,
+        'deletetopic': False,
+        'posttopic': False,
+        'postreply': False,
+        'viewtopic': False,
+        'viewprofile': False
+    })
+))
+
+
+def create_default_groups():
+    """
+    This will create the 5 default groups
+    """
+    for key, value in GROUPS.items():
+        group = Group(name=key)
+
+        for k, v in value.items():
+            setattr(group, k, v)
+
+        group.save()
+
+
+def create_admin_user(username, password, email):
+    """
+    Creates the administrator user
+    """
+    admin_group = Group.query.filter_by(admin=True).first()
+    user = User(username=username, password=password, email=email)
+    user.primary_group_id = admin_group.id
+    user.save()
+
+
+def create_welcome_forum():
+    """
+    This will create the `welcome forum` that nearly every
+    forum software has after the installation process is finished
+    """
+
+    if User.query.count() < 1:
+        raise "You need to create the admin user first!"
+
+    user = User.query.filter_by(id=1).first()
+
+    category = Forum(is_category=True, title="My Category")
+    category.save()
+
+    forum = Forum(title="Welcome", description="Your first forum",
+                  parent_id=category.id)
+    forum.save()
+
+    topic = Topic(title="Welcome!")
+    post = Post(content="Have fun with your new FlaskBB Forum!")
+
+    topic.save(user=user, forum=forum, post=post)
+
+
+def create_test_data():
+
+    create_default_groups()
+
+    # create 5 users
+    for u in range(1, 6):
+        username = "test%s" % u
+        email = "test%s@example.org" % u
+        user = User(username=username, password="test", email=email)
+        user.primary_group_id = u
+        db.session.add(user)
+        db.session.commit()
+
+    # create 2 categories
+    for i in range(1, 3):
+        category_title = "Test Category %s" % i
+        category = Forum(is_category=True, title=category_title,
+                         description="Test Description")
+        db.session.add(category)
+
+        # create 2 forums in each category
+        for j in range(1, 3):
+            if i == 2:
+                j += 2
+
+            forum_title = "Test Forum %s %s" % (j, i)
+            forum = Forum(title=forum_title, description="Test Description",
+                          parent_id=i)
+            db.session.add(forum)
+        db.session.commit()
+
+    # create 1 topic in each forum
+    for k in [2, 3, 5, 6]:  # Forum ids are not sequential because categories.
+        topic = Topic()
+        topic.first_post = Post()
+
+        topic.title = "Test Title %s" % k
+        topic.user_id = 1
+        topic.forum_id = k
+        topic.first_post.content = "Test Content"
+        topic.first_post.user_id = 1
+        topic.first_post.topic_id = topic.id
+
+        db.session.add(topic)
+        db.session.commit()
+
+        # Update the post and topic count
+        topic.forum.topic_count += 1
+        topic.forum.post_count += 1
+        topic.post_count += 1
+        topic.first_post.user.post_count += 1
+        db.session.commit()
+
+    db.session.commit()

+ 49 - 0
flaskbb/utils/types.py

@@ -0,0 +1,49 @@
+# -*- coding: utf-8 -*-
+"""
+    flaskbb.utils.types
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Additional types for SQLAlchemy
+
+    :copyright: (c) 2013 by the FlaskBB Team.
+    :license: BSD, see LICENSE for more details.
+"""
+from sqlalchemy import types
+from sqlalchemy.ext.mutable import Mutable
+
+
+class DenormalizedText(Mutable, types.TypeDecorator):
+    """
+    Stores denormalized primary keys that can be
+    accessed as a set.
+
+    :param coerce: coercion function that ensures correct
+                   type is returned
+
+    :param separator: separator character
+
+    Source: https://github.com/imwilsonxu/fbone/blob/master/fbone/user/models.py#L13-L45
+    """
+
+    impl = types.Text
+
+    def __init__(self, coerce=int, separator=" ", **kwargs):
+
+        self.coerce = coerce
+        self.separator = separator
+
+        super(DenormalizedText, self).__init__(**kwargs)
+
+    def process_bind_param(self, value, dialect):
+        if value is not None:
+            items = [str(item).strip() for item in value]
+            value = self.separator.join(item for item in items if item)
+        return value
+
+    def process_result_value(self, value, dialect):
+        if not value:
+            return set()
+        return set(self.coerce(item) for item in value.split(self.separator))
+
+    def copy_value(self, value):
+        return set(value)

+ 73 - 0
flaskbb/utils/wtforms.py

@@ -0,0 +1,73 @@
+# -*- coding: utf-8 -*-
+"""
+    flaskbb.utils.wtforms
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Additional widgets for wtforms
+
+    :copyright: (c) 2013 by the FlaskBB Team.
+    :license: BSD, see LICENSE for more details.
+"""
+from datetime import datetime
+from wtforms.widgets.core import Select, HTMLString, html_params
+
+
+class SelectDateWidget(object):
+    """
+    Renders a DateTime field with 3 selects.
+    For more information see: http://stackoverflow.com/a/14664504
+    """
+    FORMAT_CHOICES = {
+        '%d': [(x, str(x)) for x in range(1, 32)],
+        '%m': [(x, str(x)) for x in range(1, 13)]
+    }
+
+    FORMAT_CLASSES = {
+        '%d': 'select_date_day',
+        '%m': 'select_date_month',
+        '%Y': 'select_date_year'
+    }
+
+    def __init__(self, years=range(1930, datetime.utcnow().year+1)):
+        super(SelectDateWidget, self).__init__()
+        self.FORMAT_CHOICES['%Y'] = [(x, str(x)) for x in years]
+
+    def __call__(self, field, **kwargs):
+        field_id = kwargs.pop('id', field.id)
+        html = []
+        allowed_format = ['%d', '%m', '%Y']
+
+        for format in field.format.split():
+            if (format in allowed_format):
+                choices = self.FORMAT_CHOICES[format]
+                id_suffix = format.replace('%', '-')
+                id_current = field_id + id_suffix
+
+                kwargs['class'] = self.FORMAT_CLASSES[format]
+                try:
+                    del kwargs['placeholder']
+                except:
+                    pass
+
+                html.append('<select %s>' % html_params(name=field.name,
+                                                        id=id_current,
+                                                        **kwargs))
+
+                if field.data:
+                    current_value = int(field.data.strftime(format))
+                else:
+                    current_value = None
+
+                for value, label in choices:
+                    selected = (value == current_value)
+                    html.append(Select.render_option(value, label, selected))
+                html.append('</select>')
+            else:
+                html.append(format)
+                html.append(
+                    """<input type="hidden" value="{}" {}></input>""".format(
+                        html_params(name=field.name, id=id_current, **kwargs)))
+
+            html.append(' ')
+
+        return HTMLString(''.join(html))