Browse Source

fix tag related_tags add forums count

修复相关节点的bug,增加社区统计(包括用户,主题等)
honmaple 8 years ago
parent
commit
5a69483beb

+ 20 - 32
README.org

@@ -1,4 +1,4 @@
-* Honmaple(maplebb)
+* Honmaple
 
   [[license][https://img.shields.io/badge/license-GPL3.0-blue.svg]]
 
@@ -7,6 +7,16 @@
   [[https://raw.githubusercontent.com/honmaple/maple-bbs/master/screenshooter/ask.png]]
 
   This is a free,open-source forums system based on the flask
+  
+  *If you have used honmaple before 2017-4-1,please use upgrade script to upgrade data*
+  
+  *important !* : please modify script to configure as your own database.
+  #+BEGIN_SRC sh
+  # session1:old database
+  # session2:new database
+  python upgrade.py
+  python upgrade_count.py
+  #+END_SRC
 
 ** Features
    + Register & login & forget password
@@ -18,7 +28,6 @@
    + Choice markdown to ask
    + Tags rss
    + Avatar
-   + Topic vote
      
 ** Installation
 
@@ -31,11 +40,10 @@
     Beacause flask_maple.rbac isn't uploaded to pypi
     you should 
     #+BEGIN_SRC shell
-git clone https://github.com/honmaple/flask-maple
-cd flask-maple
-git checkout develop
-pip uninstall flask-maple
-python setup.py install
+      git clone https://github.com/honmaple/flask-maple
+      cd flask-maple
+      pip uninstall flask-maple
+      python setup.py install
     #+END_SRC
     to install flask-maple
 
@@ -43,29 +51,6 @@ python setup.py install
     #+BEGIN_SRC shell
     mv config.example config
     #+END_SRC
-    after finished configure
-
-    *For Production*
-    Please set DEBUG = False or change *maple/__init__.py*
-    #+BEGIN_SRC python
-    app.config.from_object('config.config')
-    #+END_SRC
-    to
-    #+BEGIN_SRC python
-    app.config.from_object('config.production')
-    #+END_SRC
-
-
-*** Comment before init db
-
-    place of file: *maple/topic/forms.py*
-    #+BEGIN_SRC python
-    category = SelectField(
-        _('Category:'),
-        choices=[(b.id, b.board + '   --' + b.parent_board)
-                 for b in Board.query.all()],
-        coerce=int)
-    #+END_SRC
 
 *** Init sql
     #+BEGIN_SRC python
@@ -73,11 +58,14 @@ python setup.py install
     python manager.py db migrate -m "first migrate"
     python manager.py db upgrade
     #+END_SRC
-    *Ok*,please recovery *maple/topic/forms.py*
+    Or
+    #+BEGIN_SRC sh
+    python manager.py init_db
+    #+END_SRC
 
 *** Create admin account
     #+BEGIN_SRC shell
-python manager.py create_user
+    python manager.py create_user
     #+END_SRC
 
 *** Login and visit admin 

+ 85 - 0
config.example

@@ -0,0 +1,85 @@
+#!/usr/bin/env python
+# -*- coding=UTF-8 -*-
+# **************************************************************************
+# Copyright © 2016 jianglin
+# File Name: config.py
+# Author: jianglin
+# Email: xiyang0807@gmail.com
+# Created: 2016-05-20 12:31:46 (CST)
+# Last Update:星期日 2017-4-2 15:39:4 (CST)
+#          By: jianglin
+# Description:
+# **************************************************************************
+from datetime import timedelta
+from os import path, pardir
+
+DEBUG = True
+SECRET_KEY = 'secret key'
+SECURITY_PASSWORD_SALT = 'you will never guess'
+SECRET_KEY_SALT = 'you will never guess'
+
+# avatar upload directory
+AVATAR_FOLDER = path.join(path.abspath(path.dirname(__file__)), 'avatar')
+# avatar generate range
+AVATAR_RANGE = [122, 512]
+
+# for development use localhost:5000
+# for production use xxx.com
+# SERVER_NAME = 'localhost:5000'
+
+# remember me to save cookies
+PERMANENT_SESSION_LIFETIME = timedelta(days=3)
+REMEMBER_COOKIE_DURATION = timedelta(days=3)
+ONLINE_LAST_MINUTES = 5
+
+# You want show how many topics per page
+PER_PAGE = 12
+
+# Use cache
+CACHE_TYPE = 'null'
+CACHE_DEFAULT_TIMEOUT = 60
+CACHE_KEY_PREFIX = 'cache:'
+CACHE_REDIS_HOST = '127.0.0.1'
+CACHE_REDIS_PORT = '6379'
+CACHE_REDIS_PASSWORD = 'your password'
+CACHE_REDIS_DB = 2
+
+# Redis setting
+REDIS = {'db': 1, 'password': 'your password', 'decode_responses': True}
+
+# some middleware
+MIDDLEWARE = ['forums.common.middleware.GlobalMiddleware',
+              'forums.common.middleware.OnlineMiddleware']
+
+# Mail such as qq
+MAIL_SERVER = 'smtp.qq.com'
+MAIL_PORT = 25
+MAIL_USE_TLS = True
+MAIL_USE_SSL = False
+MAIL_USERNAME = "your email"
+MAIL_PASSWORD = "your password"
+MAIL_DEFAULT_SENDER = 'your email'
+# MAIL_SUPPRESS_SEND = True
+
+# Log,if SEND_LOGS is True when web app has some error happen(500)
+# the email will be sent to RECEIVER
+SEND_LOGS = True
+RECEIVER = ["yourname@gmail.com"]
+INFO_LOG = "info.log"
+ERROR_LOG = "error.log"
+SERVER_NAME = 'localhost:5000'
+SUBDOMAIN = {'forums': True, 'docs': True}
+
+# Sql
+SQLALCHEMY_DATABASE_URI = 'postgresql://postgres:password@localhost/your_db'
+WHOOSH_BASE = 'search.db'
+# SQLALCHEMY_ECHO = True
+# SQLALCHEMY_DATABASE_URI = 'sqlite:///test.db'
+# SQLALCHEMY_DATABASE_URI = 'mysql://username:password@server/db'
+
+# avatar upload folder
+AVATAR_FOLDER = 'avatars/'
+
+# Locale
+LANGUAGES = {'en': 'English', 'zh': 'Chinese'}
+SITE = {'title': 'Honmaple', 'description': '爱生活,更爱自由', 'avatar': ''}

+ 3 - 1
forums/api/auth/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-10-28 10:26:10 (CST)
-# Last Update:星期日 2017-4-2 11:49:2 (CST)
+# Last Update:星期日 2017-4-2 15:25:52 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -25,6 +25,7 @@ from forums.api.user.models import User
 from forums.common.response import HTTPResponse
 from forums.common.serializer import Serializer
 from forums.permission import is_guest
+from forums.count import Count
 
 
 class LoginView(MethodView):
@@ -88,6 +89,7 @@ class RegisterView(MethodView):
         user.set_password(password)
         user.save()
         login_user(user)
+        Count.forums_user_count(1)
         self.email(user)
         return HTTPResponse(HTTPResponse.NORMAL_STATUS).to_response()
 

+ 9 - 1
forums/api/tag/models.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 20:46:13 (CST)
-# Last Update:星期三 2017-3-29 19:17:5 (CST)
+# Last Update:星期日 2017-4-2 15:5:23 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -63,6 +63,14 @@ class Tags(db.Model, ModelMixin):
             tag_follower.c.tag_id == self.id,
             tag_follower.c.follower_id == user.id).exists()
 
+    @property
+    def related_tags(self):
+        parent = self.parent
+        if not parent:
+            return []
+        relateds = parent.children.exclude_by(id=self.id).all()
+        return relateds
+
     def __str__(self):
         return self.name
 

+ 1 - 1
forums/api/tag/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 22:07:04 (CST)
-# Last Update:星期二 2017-3-28 17:25:34 (CST)
+# Last Update:星期日 2017-4-2 14:45:58 (CST)
 #          By:
 # Description:
 # **************************************************************************

+ 6 - 2
forums/api/topic/views.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-12-15 22:07:39 (CST)
-# Last Update:星期日 2017-4-2 0:23:55 (CST)
+# Last Update:星期日 2017-4-2 14:49:1 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -78,13 +78,17 @@ class TopicListView(MethodView):
         keys = ['title']
         order_by = gen_order_by(query_dict, keys)
         filter_dict = gen_filter_dict(query_dict, keys)
+        title = _('All Topics')
         if request.path.endswith('good'):
             filter_dict.update(is_good=True)
+            title = _('Good Topics')
         elif request.path.endswith('top'):
             filter_dict.update(is_top=True)
+            title = _('Top Topics')
         topics = Topic.query.filter_by(
             **filter_dict).order_by(*order_by).paginate(page, number, True)
-        return render_template('topic/topic_list.html', topics=topics)
+        data = {'title': title, 'topics': topics}
+        return render_template('topic/topic_list.html', **data)
 
     @form_validate(form_board, error=error_callback, f='')
     def post(self):

+ 25 - 1
forums/count.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-03-29 21:28:52 (CST)
-# Last Update:星期日 2017-4-2 13:22:19 (CST)
+# Last Update:星期日 2017-4-2 15:24:37 (CST)
 #          By:
 # Description: 一些统计信息
 # **************************************************************************
@@ -70,6 +70,8 @@ class Count(object):
             pipe = redis_data.pipeline()
             pipe.hincrby(key, 'topic', value)
             pipe.execute()
+            cls.forums_post_count(1)
+            cls.forums_topic_count(1)
         return redis_data.hget(key, 'topic') or 0
 
     @classmethod
@@ -79,6 +81,7 @@ class Count(object):
             pipe = redis_data.pipeline()
             pipe.hincrby(key, 'replies', value)
             pipe.execute()
+            cls.forums_post_count(1)
         return redis_data.hget(key, 'replies') or 0
 
     @classmethod
@@ -98,3 +101,24 @@ class Count(object):
         if value is not None:
             redis_data.hset(key, 'email', value)
         return redis_data.hget(key, 'email') or '2015-1-1 1:1:1'
+
+    @classmethod
+    def forums_user_count(cls, value=None):
+        key = 'count:forums'
+        if value is not None:
+            redis_data.hincrby(key, 'user', value)
+        return redis_data.hget(key, 'user') or 0
+
+    @classmethod
+    def forums_topic_count(cls, value=None):
+        key = 'count:forums'
+        if value is not None:
+            redis_data.hincrby(key, 'topic', value)
+        return redis_data.hget(key, 'topic') or 0
+
+    @classmethod
+    def forums_post_count(cls, value=None):
+        key = 'count:forums'
+        if value is not None:
+            redis_data.hincrby(key, 'post', value)
+        return redis_data.hget(key, 'post') or 0

+ 8 - 1
forums/filters.py

@@ -6,7 +6,7 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2016-11-07 21:00:32 (CST)
-# Last Update:星期六 2017-4-1 20:16:51 (CST)
+# Last Update:星期日 2017-4-2 15:30:0 (CST)
 #          By:
 # Description:
 # **************************************************************************
@@ -81,6 +81,12 @@ def recent_tags():
     return tags
 
 
+def forums_count():
+    from forums.extension import redis_data
+    key = 'count:forums'
+    return redis_data.hgetall(key)
+
+
 def is_not_confirmed(user):
     return (not user.is_confirmed and user.id == current_user.id)
 
@@ -91,6 +97,7 @@ def register_jinja2(app):
     app.jinja_env.globals['hot_tags'] = hot_tags
     app.jinja_env.globals['recent_tags'] = recent_tags
     app.jinja_env.globals['show_time'] = show_time
+    app.jinja_env.globals['forums_count'] = forums_count
     app.jinja_env.filters['timesince'] = timesince
     app.jinja_env.filters['safe_clean'] = safe_clean
     app.jinja_env.filters['is_not_confirmed'] = is_not_confirmed

BIN
forums/test.db


+ 1 - 1
requirements.txt

@@ -2,7 +2,7 @@ bleach==1.4.3
 cssmin==0.2.0
 Flask==0.11.1
 Flask-Admin==1.4.2
-Flask-Assets==0.11
+Flask-Assets==0.12
 Flask-Avatar==0.1.0
 Flask-BabelEx==0.9.3
 Flask-Cache==0.13.1

+ 2 - 2
templates/base/base.html

@@ -6,7 +6,7 @@
 {% from 'base/head.html' import breadcrumb %}
 
 {% block title -%}
-  {{ title }} {{ SITE['title'] }} - {{ _(SITE['description']) }}
+  {{ title }} - {{ SITE['title'] }} - {{ _(SITE['description']) }}
 {%- endblock title %}
 
 {% block script -%}
@@ -15,7 +15,7 @@
 
 {% block style -%}
   {{ super() }}
-  <link href="http://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
+  <link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet">
 {%- endblock style %}
 
 {% block main %}

+ 1 - 1
templates/base/header.html

@@ -18,7 +18,7 @@
       <ul class="nav navbar-nav">
         {{ navlist(url_for('forums.forums'),_('Forums')) }}
         {{ navlist(url_for('docs.list'),_('Wiki')) }}
-        {{ navlist('#',_('Blog')) }}
+        {{ navlist('http://honmaple.org',_('Blog')) }}
         {{ navlist(url_for('topic.good'),_('Good')) }}
       </ul>
       <form onsubmit="return dispatch()" class="navbar-form navbar-right" style="margin-top:10px;" >

+ 0 - 48
templates/base/notice.html

@@ -1,48 +0,0 @@
-{% import 'base/link.html' as link_base %}
-{% macro privacy(notice) -%}
-<small style="color:#999;">由{{ link_base.user(notice.send_user) }} 发来的私信:</small>
-<small class="pull-right">{{ notice.publish | timesince }}</small>
-<p style="margin-bottom:0px">{{ notice.content['content'] }}</p>
-{%- endmacro %}
-
-{% macro reply(notice) -%}
-{% set content = notice.content %}
-<small style="color:#999;"> {{ link_base.user(notice.send_user)}} 回复了你创建的主题:
-    <a href="{{ content['url'] }}">{{ content['title'] }}</a>
-</small>
-<small class="pull-right"> {{ notice.publish | timesince }} </small>
-<p style="margin-bottom:0px">{{ content["content"] | safe_clean }}</p>
-{%- endmacro %}
-
-{% macro rereply(notice) -%}
-{% set content = notice.content %}
-<small style="color:#999;">
-    {{ link_base.user(notice.send_user)}}在回复
-    <a href="{{ content['url'] }}">{{ content['title'] }}</a>时提到了你
-</small>
-<small class="pull-right"> {{ notice.publish | timesince }} </small>
-<p style="margin-bottom:0px">{{ content["content"] | safe_clean }}</p>
-{%- endmacro %}
-
-{% macro collect(notice) -%}
-{% set content = notice.content %}
-<small style="color:#999;"> {{ link_base.user(notice.send_user)}} 收藏了你创建的主题:
-    <a href="{{ content['url'] }}">{{ content['title'] }}</a>
-</small>
-<small class="pull-right"> {{ notice.publish | timesince }} </small>
-{%- endmacro %}
-
-{% macro like(notice) -%}
-{% set content = notice.content %}
-<small style="color:#999;"> {{ link_base.user(notice.send_user)}} 在
-    <a href="{{ content['url'] }}">{{ content['title'] }}</a> 赞了你回复
-</small>
-<small class="pull-right"> {{ notice.publish | timesince }} </small>
-<p style="margin-bottom:0px">{{ content["content"] | safe_clean }}</p>
-{%- endmacro %}
-
-{% macro user(notice) -%}
-{% set content = notice.content %}
-<small style="color:#999;"> {{ link_base.user(notice.send_user)}} 关注了你 </small>
-<small class="pull-right"> {{ notice.publish | timesince }} </small>
-{%- endmacro %}

+ 4 - 3
templates/base/panel.html

@@ -91,9 +91,10 @@
         {{ _('Forums Count') }}
     </div>
     <ul class="list-group">
-        <li class="list-group-item">{{ _('Total number of registered users:')}} 0</li>
-        <li class="list-group-item">{{ _('Total number of topics:') }} 0 </li>
-        <li class="list-group-item">{{ _('Total number of posts:') }} 0 </li>
+        {% set count = forums_count() %}
+        <li class="list-group-item">{{ _('Total number of registered users:')}} {{ count['user'] }}</li>
+        <li class="list-group-item">{{ _('Total number of topics:') }} {{ count['topic'] }} </li>
+        <li class="list-group-item">{{ _('Total number of posts:') }} {{ count['post'] }} </li>
     </ul>
 </div>
 {%- endmacro %}

+ 0 - 2
templates/base/sort.html

@@ -1,2 +0,0 @@
-{% set topics = topics.items %}
-{% include 'topic/topic_list_base.html' %}

+ 1 - 1
templates/forums/index.html

@@ -9,7 +9,7 @@
 <div class="row hidden-sm hidden-xs">
     {{ forums_item(_('Forums'),url_for('forums.forums'),"fa-comments","#F86334")}}
     {{ forums_item(_('Wiki'),url_for('docs.list'),"fa-book","#eeccaa")}}
-    {{ forums_item(_('Blog'),'https//honmaple.com',"fa-tasks")}}
+    {{ forums_item(_('Blog'),'http://honmaple.org',"fa-tasks")}}
     {{ forums_item(_('Good'),url_for('topic.good'),"fa-globe","#017e66")}}
 </div>
 <div class="row">

+ 42 - 43
templates/tag/panel.html

@@ -1,60 +1,59 @@
 {% from 'base/panel.html' import base %}
 {% from 'base/panel.html' import count %}
 {% macro tag_list() -%}
-{{ base() }}
+  {{ base() }}
 {%- endmacro %}
 
 {% macro tag(tag) -%}
-{{ base() }}
-<div class="panel panel-default hidden-xs" style="font-size:13px;">
-  {{ parents(tag) }}
-  {{ children(tag) }}
-</div>
-{{ count() }}
+  {{ base() }}
+  <div class="panel panel-default hidden-xs" style="font-size:13px;">
+    {{ parents(tag) }}
+    {{ relation(tag) }}
+    {{ children(tag) }}
+  </div>
+  {{ count() }}
 {%- endmacro %}
 
 {% macro parents(tag) -%}
-<div class="panel-body" style="padding:10;margin:0;">
-  <strong>父节点</strong>
-</div>
-{% if tag.parents -%}
-{% for parent in tag.parents -%}
-{{ link(parent.name) }}
-{%- endfor %}
-<div style="border-bottom:1px solid #eee"></div>
-{{ relation(tag) }}
-{% else %}
-{{ link('honmaple') }}
-{%- endif %}
-<div style="border-bottom:1px solid #eee"></div>
+  <div class="panel-body" style="padding:10;margin:0;">
+    <strong>父节点</strong>
+  </div>
+  {% if tag.parent_id -%}
+    {{ link(tag.parent.name) }}
+  {% else %}
+    {{ link('honmaple') }}
+  {%- endif %}
+  <div style="border-bottom:1px solid #eee"></div>
 {%- endmacro %}
 
-{% macro children(tag) -%}
-{% if tag.children -%}
-<div class="panel-body" style="padding:10;margin:0;">
-  <strong>子节点</strong>
-</div>
-{% for child in tag.children -%}
-{{ link(child.name) }}
-{%- endfor %}
-{%- endif %}
+{% macro relation(tag) -%}
+  {% set relates = tag.related_tags %}
+  {% if relates -%}
+    <div class="panel-body" style="padding:10;margin:0;">
+      <strong>相关节点</strong>
+    </div>
+    {% for child in relates -%}
+      {{ link(child.name) }}
+    {%- endfor %}
+  {%- endif %}
 {%- endmacro %}
 
-{% macro relation(tag) -%}
-{% set parent = tag.parents[0] %}
-{% if parent.children -%}
-<div class="panel-body" style="padding:10;margin:0;">
-  <strong>相关节点</strong>
-</div>
-{% for child in parent.children if child.name != tag.name -%}
-{{ link(child.name) }}
-{%- endfor %}
-{%- endif %}
+{% macro children(tag) -%}
+  {% set children = tag.children.all() %}
+  {% if children -%}
+    <div class="panel-body" style="padding:10;margin:0;">
+      <strong>子节点</strong>
+    </div>
+    {% for child in children -%}
+      {{ link(child.name) }}
+    {%- endfor %}
+  {%- endif %}
 {%- endmacro %}
 
+
 {% macro link(name) -%}
-<div class="panel-body" style="padding:10px;margin-top:0;padding-top:0">
-  <img alt="" src="{{ url_for('avatar',text=name)}}" style="width:24px;"/>
-  <a class="text-capitalize" href="{{ url_for('tag.tag',name=name)}}" style="color:#555;">{{ name }}</a>
-</div>
+  <div class="panel-body" style="padding:10px;margin-top:0;padding-top:0">
+    <img alt="" src="{{ url_for('avatar',text=name)}}" style="width:24px;"/>
+    <a class="text-capitalize" href="{{ url_for('tag.tag',name=name)}}" style="color:#555;">{{ name }}</a>
+  </div>
 {%- endmacro %}

+ 1 - 1
templates/tag/tag_list.html

@@ -9,7 +9,7 @@
         </div>
         <div class="panel-body">
           {% for tag in tags.items %}
-            <a class="tag" href="{{ url_for('tag.tag',name=tag.name)}}">{{ tag.name }}</a>
+            <a class="tag" href="{{ url_for('tag.tag',name=tag.name) }}">{{ tag.name }}</a>
           {% else %}
             <span class="text-center">
               {{ _('No Tags') }}

+ 16 - 10
templates/topic/topic_list.html

@@ -1,15 +1,21 @@
 {% extends 'base/base.html' %}
 {% block content %}
-{{ breadcrumb(active=_('All Topics')) }}
-<div class="row">
-  <div class="col-md-9">
-    <div class="panel panel-default">
-      {% include "topic/_topic.html" %}
-      {{ p_footer(topics,'topic.list')}}
+  {% if request.path.endswith('top') %}
+    {{ breadcrumb(hrefs={_('All Topics'):url_for('topic.list')},active=_('Top Topics')) }}
+  {% elif request.path.endswith('good') %}
+    {{ breadcrumb(hrefs={_('All Topics'):url_for('topic.list')},active=_('Good Topics')) }}
+  {% else %}
+    {{ breadcrumb(active=_('All Topics')) }}
+  {% endif %}
+  <div class="row">
+    <div class="col-md-9">
+      <div class="panel panel-default">
+        {% include "topic/_topic.html" %}
+        {{ p_footer(topics,'topic.list')}}
+      </div>
+    </div>
+    <div class="col-md-3" style="padding-left:0">
+      {{ panel_base.topic_list() }}
     </div>
   </div>
-  <div class="col-md-3" style="padding-left:0">
-    {{ panel_base.topic_list() }}
-  </div>
-</div>
 {% endblock %}

+ 10 - 2
upgrade_count.py

@@ -6,15 +6,22 @@
 # Author: jianglin
 # Email: xiyang0807@gmail.com
 # Created: 2017-04-02 13:00:02 (CST)
-# Last Update:星期日 2017-4-2 13:47:2 (CST)
+# Last Update:星期日 2017-4-2 15:52:41 (CST)
 #          By:
 # Description:
 # **************************************************************************
-from runserver import app
 from forums.api.topic.models import Topic, Reply
 from forums.api.forums.models import Board
 from forums.api.user.models import User
 from forums.extension import redis_data
+from runserver import app
+
+
+def forums():
+    key = 'count:forums'
+    redis_data.hset(key, 'user', User.query.count())
+    redis_data.hset(key, 'topic', Topic.query.count())
+    redis_data.hset(key, 'post', Reply.query.count() + Topic.query.count())
 
 
 def topic():
@@ -75,6 +82,7 @@ def board():
 
 def main():
     with app.app_context():
+        forums()
         topic()
         board()
         user()