Exemplo n.º 1
0
class Report(db.Model, CRUDMixin):
    __tablename__ = "forum_reports"

    id = db.Column(db.Integer, primary_key=True)
    reporter_id = db.Column(db.Integer,
                            db.ForeignKey("account.id"),
                            nullable=False)
    reported = db.Column(db.DateTime, default=datetime.utcnow())
    post_id = db.Column(db.Integer,
                        db.ForeignKey("forum_posts.id"),
                        nullable=False)
    zapped = db.Column(db.DateTime)
    zapped_by = db.Column(db.Integer, db.ForeignKey("account.id"))
    reason = db.Column(db.Text)

    post = db.relationship("Post", backref="report", lazy="joined")
    reporter = db.relationship("Account",
                               lazy="joined",
                               foreign_keys=[reporter_id])
    zapper = db.relationship("Account",
                             lazy="joined",
                             foreign_keys=[zapped_by])

    def __repr__(self):
        return "<{} {}>".format(self.__class__.__name__, self.id)

    def save(self, post=None, user=None):
        """Saves a report.

        :param post: The post that should be reported
        :param user: The user who has reported the post
        :param reason: The reason why the user has reported the post
        """

        if self.id:
            db.session.add(self)
            db.session.commit()
            return self

        if post and user:
            self.reporter_id = user.id
            self.reported = datetime.utcnow()
            self.post_id = post.id

        db.session.add(self)
        db.session.commit()
        return self
Exemplo n.º 2
0
class SettingsGroup(db.Model, CRUDMixin):
    __tablename__ = "forum_settingsgroup"

    key = db.Column(db.String(255), primary_key=True)
    name = db.Column(db.String(255), nullable=False)
    description = db.Column(db.Text, nullable=False)
    settings = db.relationship("Setting",
                               lazy="dynamic",
                               backref="group",
                               cascade="all, delete-orphan")
Exemplo n.º 3
0
class Category(db.Model, CRUDMixin):
    __tablename__ = "forum_categories"
    __searchable__ = ['title', 'description']

    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(255), nullable=False)
    description = db.Column(db.Text)
    position = db.Column(db.Integer, default=1, nullable=False)

    # One-to-many
    forums = db.relationship("Forum",
                             backref="category",
                             lazy="dynamic",
                             primaryjoin='Forum.category_id == Category.id',
                             order_by='asc(Forum.position)',
                             cascade="all, delete-orphan")

    # Properties
    @property
    def slug(self):
        """Returns a slugified version from the category title"""
        return slugify(self.title)

    @property
    def url(self):
        """Returns the slugified url for the category"""
        return url_for("forum.view_category",
                       category_id=self.id,
                       slug=self.slug)

    # Methods
    def __repr__(self):
        """Set to a unique key specific to the object in the database.
        Required for cache.memoize() to work across requests.
        """
        return "<{} {}>".format(self.__class__.__name__, self.id)

    def delete(self, users=None):
        """Deletes a category. If a list with involved user objects is passed,
        it will also update their post counts

        :param users: A list with user objects
        """

        # and finally delete the category itself
        db.session.delete(self)
        db.session.commit()

        # Update the users post count
        if users:
            for user in users:
                user.post_count = Post.query.filter_by(user_id=user.id).count()
                db.session.commit()

        return self

    # Classmethods
    @classmethod
    def get_all(cls, user):
        """Get all categories with all associated forums.
        It returns a list with tuples. Those tuples are containing the category
        and their associated forums (whose are stored in a list).

        For example::

            [(<Category 1>, [(<Forum 2>, <ForumsRead>), (<Forum 1>, None)]),
             (<Category 2>, [(<Forum 3>, None), (<Forum 4>, None)])]

        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        forums = db.session.query(cls).\
            order_by(Category.position, Category.id).\
            all()
        # # import Group model locally to avoid cicular imports
        # if user.is_authenticated() and not getattr(user, 'is_lockdownuser', False):
        #     # get list of user group idsnot
        #     # filter forums by user groups
        #     # get all
        #     forums = db.session.query(cls).\
        #         order_by(Category.position, Category.id).\
        #         all()
        # else:
        #     # filter forums by guest groups
        #     forums = db.session.query(cls).\
        #         order_by(cls.position, cls.id).\
        #         all()

        return get_categories_and_forums(forums, user)

    @classmethod
    def get_forums(cls, category_id, user):
        """Get the forums for the category.
        It returns a tuple with the category and the forums with their
        forumsread object are stored in a list.

        A return value can look like this for a category with two forums::

            (<Category 1>, [(<Forum 1>, None), (<Forum 2>, None)])

        :param category_id: The category id
        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        if is_authenticated(user):
            # get list of user group ids

            forums = cls.query.\
                filter(cls.id == category_id).\
                all()
            # outerjoin(ForumsRead,
            #           db.and_(ForumsRead.user_id == user.id)).\
            # add_entity(ForumsRead).\

        else:
            forums = cls.query.\
                filter(cls.id == category_id).\
                all()

        if not forums:
            abort(404)

        return get_forums(forums, user)
Exemplo n.º 4
0
class Forum(db.Model, CRUDMixin):
    __tablename__ = "forum_forums"
    __searchable__ = ['title', 'description']

    id = db.Column(db.Integer, primary_key=True)
    category_id = db.Column(db.Integer,
                            db.ForeignKey("forum_categories.id"),
                            nullable=False)
    title = db.Column(db.String(255), nullable=False)
    description = db.Column(db.Text)
    position = db.Column(db.Integer, default=1, nullable=False)
    locked = db.Column(db.Boolean, default=False, nullable=False)
    show_moderators = db.Column(db.Boolean, default=False, nullable=False)
    external = db.Column(db.String(200))

    post_count = db.Column(db.Integer, default=0, nullable=False)
    topic_count = db.Column(db.Integer, default=0, nullable=False)

    # One-to-one
    last_post_id = db.Column(db.Integer, db.ForeignKey("forum_posts.id"))
    last_post = db.relationship("Post",
                                backref="last_post_forum",
                                uselist=False,
                                foreign_keys=[last_post_id])

    # Not nice, but needed to improve the performance
    last_post_title = db.Column(db.String(255))
    last_post_user_id = db.Column(db.Integer, db.ForeignKey("account.id"))
    last_post_username = db.Column(db.String(255))
    last_post_created = db.Column(db.DateTime, default=datetime.utcnow())

    # One-to-many
    topics = db.relationship("Topic",
                             backref="forum",
                             lazy="dynamic",
                             cascade="all, delete-orphan")

    # Many-to-many
    moderators = db.relationship("Account",
                                 secondary=moderators,
                                 primaryjoin=(moderators.c.forum_id == id),
                                 backref=db.backref("forummoderator",
                                                    lazy="dynamic"),
                                 lazy="joined")

    # Properties
    @property
    def slug(self):
        """Returns a slugified version from the forum title"""
        return slugify(self.title)

    @property
    def url(self):
        """Returns the slugified url for the forum"""
        if self.external:
            return self.external
        return url_for("forum.view_forum", forum_id=self.id, slug=self.slug)

    @property
    def last_post_url(self):
        """Returns the url for the last post in the forum"""
        return url_for("forum.view_post", post_id=self.last_post_id)

    # Methods
    def __repr__(self):
        """Set to a unique key specific to the object in the database.
        Required for cache.memoize() to work across requests.
        """
        return "<{} {}>".format(self.__class__.__name__, self.id)

    def update_last_post(self):
        """Updates the last post in the forum."""
        last_post = Post.query.\
            filter(Post.topic_id == Topic.id,
                   Topic.forum_id == self.id).\
            order_by(Post.date_created.desc()).\
            first()

        # Last post is none when there are no topics in the forum
        if last_post is not None:

            # a new last post was found in the forum
            if not last_post.id == self.last_post_id:
                self.last_post_id = last_post.id
                self.last_post_title = last_post.topic.title
                self.last_post_user_id = last_post.user_id
                self.last_post_username = last_post.username
                self.last_post_created = last_post.date_created

        # No post found..
        else:
            self.last_post_id = None
            self.last_post_title = None
            self.last_post_user_id = None
            self.last_post_username = None
            self.last_post_created = None

        db.session.commit()

    def update_read(self, user, forumsread, topicsread):
        """Updates the ForumsRead status for the user. In order to work
        correctly, be sure that `topicsread is **not** `None`.

        :param user: The user for whom we should check if he has read the
                     forum.

        :param forumsread: The forumsread object. It is needed to check if
                           if the forum is unread. If `forumsread` is `None`
                           and the forum is unread, it will create a new entry
                           in the `ForumsRead` relation, else (and the forum
                           is still unread) we are just going to update the
                           entry in the `ForumsRead` relation.

        :param topicsread: The topicsread object is used in combination
                           with the forumsread object to check if the
                           forumsread relation should be updated and
                           therefore is unread.
        """
        if (not is_authenticated(user)) or topicsread is None:
            return False

        read_cutoff = None
        if flaskbb_config['TRACKER_LENGTH'] > 0:
            read_cutoff = datetime.utcnow() - timedelta(
                days=flaskbb_config['TRACKER_LENGTH'])

        # fetch the unread posts in the forum
        unread_count = Topic.query.\
            outerjoin(TopicsRead,
                      db.and_(TopicsRead.topic_id == Topic.id,
                              TopicsRead.user_id == user.id)).\
            outerjoin(ForumsRead,
                      db.and_(ForumsRead.forum_id == Topic.forum_id,
                              ForumsRead.user_id == user.id)).\
            filter(Topic.forum_id == self.id,
                   Topic.last_updated > read_cutoff,
                   db.or_(TopicsRead.last_read == None,
                          TopicsRead.last_read < Topic.last_updated)).\
            count()

        # No unread topics available - trying to mark the forum as read
        if unread_count == 0:

            if forumsread and forumsread.last_read > topicsread.last_read:
                return False

            # ForumRead Entry exists - Updating it because a new topic/post
            # has been submitted and has read everything (obviously, else the
            # unread_count would be useless).
            elif forumsread:
                forumsread.last_read = datetime.utcnow()
                forumsread.save()
                return True

            # No ForumRead Entry existing - creating one.
            forumsread = ForumsRead()
            forumsread.user_id = user.id
            forumsread.forum_id = self.id
            forumsread.last_read = datetime.utcnow()
            forumsread.save()
            return True

        # Nothing updated, because there are still more than 0 unread
        # topicsread
        return False

    def recalculate(self, last_post=False):
        """Recalculates the post_count and topic_count in the forum.
        Returns the forum with the recounted stats.

        :param last_post: If set to ``True`` it will also try to update
                          the last post columns in the forum.
        """
        topic_count = Topic.query.filter_by(forum_id=self.id).count()
        post_count = Post.query.\
            filter(Post.topic_id == Topic.id,
                   Topic.forum_id == self.id).\
            count()
        self.topic_count = topic_count
        self.post_count = post_count

        if last_post:
            self.update_last_post()

        self.save()
        return self

    def save(self, groups=None):
        """Saves a forum

        :param moderators: If given, it will update the moderators in this
                           forum with the given iterable of user objects.
        :param groups: A list with group objects.
        """
        if self.id:
            db.session.merge(self)
        else:
            db.session.add(self)

        db.session.commit()
        return self

    def delete(self, users=None):
        """Deletes forum. If a list with involved user objects is passed,
        it will also update their post counts

        :param users: A list with user objects
        """
        # Delete the forum
        db.session.delete(self)
        db.session.commit()

        # Delete the entries for the forum in the ForumsRead and TopicsRead
        # relation
        ForumsRead.query.filter_by(forum_id=self.id).delete()
        TopicsRead.query.filter_by(forum_id=self.id).delete()

        # Update the users post count
        if users:
            users_list = []
            for user in users:
                user.post_count = Post.query.filter_by(user_id=user.id).count()
                users_list.append(user)
            db.session.add_all(users_list)
            db.session.commit()

        return self

    def move_topics_to(self, topics):
        """Moves a bunch a topics to the forum. Returns ``True`` if all
        topics were moved successfully to the forum.

        :param topics: A iterable with topic objects.
        """
        status = False
        for topic in topics:
            status = topic.move(self)
        return status

    # Classmethods
    @classmethod
    @can_access_forum
    def get_forum(cls, forum_id, user):
        """Returns the forum and forumsread object as a tuple for the user.

        :param forum_id: The forum id
        :param user: The user object is needed to check if we also need their
                     forumsread object.
        """
        if is_authenticated(user):
            forum, forumsread = Forum.query.\
                filter(Forum.id == forum_id).\
                options(db.joinedload("category")).\
                outerjoin(ForumsRead,
                          db.and_(ForumsRead.forum_id == Forum.id,
                                  ForumsRead.user_id == user.id)).\
                add_entity(ForumsRead).\
                first_or_404()
        else:
            forum = Forum.query.filter(Forum.id == forum_id).first_or_404()
            forumsread = None

        return forum, forumsread

    @classmethod
    def get_topics(cls, forum_id, user, page=1, per_page=20):
        """Get the topics for the forum. If the user is logged in,
        it will perform an outerjoin for the topics with the topicsread and
        forumsread relation to check if it is read or unread.

        :param forum_id: The forum id
        :param user: The user object
        :param page: The page whom should be loaded
        :param per_page: How many topics per page should be shown
        """
        if is_authenticated(user):
            topics = Topic.query.filter_by(forum_id=forum_id).\
                outerjoin(TopicsRead,
                          db.and_(TopicsRead.topic_id == Topic.id,
                                  TopicsRead.user_id == user.id)).\
                add_entity(TopicsRead).\
                order_by(Topic.important.desc(), Topic.last_updated.desc()).\
                paginate(page, per_page, True)
        else:
            topics = Topic.query.filter_by(forum_id=forum_id).\
                order_by(Topic.important.desc(), Topic.last_updated.desc()).\
                paginate(page, per_page, True)

            topics.items = [(topic, None) for topic in topics.items]

        return topics
Exemplo n.º 5
0
class Topic(db.Model, CRUDMixin):
    __tablename__ = "forum_topics"
    __searchable__ = ['title', 'username']

    id = db.Column(db.Integer, primary_key=True)
    forum_id = db.Column(db.Integer,
                         db.ForeignKey("forum_forums.id",
                                       use_alter=True,
                                       name="fk_topic_forum_id"),
                         nullable=False)
    title = db.Column(db.String(255), nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey("account.id"))
    username = db.Column(db.String(200), nullable=False)
    date_created = db.Column(db.DateTime, default=datetime.utcnow())
    last_updated = db.Column(db.DateTime, default=datetime.utcnow())
    locked = db.Column(db.Boolean, default=False)
    important = db.Column(db.Boolean, default=False)
    views = db.Column(db.Integer, default=0)
    post_count = db.Column(db.Integer, default=0)

    # One-to-one (uselist=False) relationship between first_post and topic
    first_post_id = db.Column(
        db.Integer, db.ForeignKey("forum_posts.id", ondelete="CASCADE"))
    first_post = db.relationship("Post",
                                 backref="first_post",
                                 uselist=False,
                                 foreign_keys=[first_post_id])

    # One-to-one
    last_post_id = db.Column(db.Integer, db.ForeignKey("forum_posts.id"))

    last_post = db.relationship("Post",
                                backref="last_post",
                                uselist=False,
                                foreign_keys=[last_post_id])

    # One-to-many
    posts = db.relationship("Post",
                            backref="topic",
                            lazy="dynamic",
                            primaryjoin="Post.topic_id == Topic.id",
                            cascade="all, delete-orphan",
                            post_update=True)

    # Properties
    @property
    def second_last_post(self):
        """Returns the second last post."""
        return self.posts[-2].id

    @property
    def slug(self):
        """Returns a slugified version from the topic title"""
        return slugify(self.title)

    @property
    def url(self):
        """Returns the slugified url for the topic"""
        return url_for("forum.view_topic", topic_id=self.id, slug=self.slug)

    # Methods
    def __init__(self, title=None):
        if title:
            self.title = title

    def __repr__(self):
        """
        Set to a unique key specific to the object in the database.
        Required for cache.memoize() to work across requests.
        """
        return "<{} {}>".format(self.__class__.__name__, self.id)

    @classmethod
    @can_access_topic
    def get_topic(cls, topic_id, user):
        topic = Topic.query.filter_by(id=topic_id).first_or_404()
        return topic

    def tracker_needs_update(self, forumsread, topicsread):
        """Returns True if the topicsread tracker needs an update.
        Also, if the ``TRACKER_LENGTH`` is configured, it will just recognize
        topics that are newer than the ``TRACKER_LENGTH`` (in days) as unread.

        :param forumsread: The ForumsRead object is needed because we also
                           need to check if the forum has been cleared
                           sometime ago.
        :param topicsread: The topicsread object is used to check if there is
                           a new post in the topic.
        """
        read_cutoff = None
        if flaskbb_config['TRACKER_LENGTH'] > 0:
            read_cutoff = datetime.utcnow() - timedelta(
                days=flaskbb_config['TRACKER_LENGTH'])

        # The tracker is disabled - abort
        if read_cutoff is None:
            return False

        # Else the topic is still below the read_cutoff
        elif read_cutoff > self.last_post.date_created:
            return False

        # Can be None (cleared) if the user has never marked the forum as read.
        # If this condition is false - we need to update the tracker
        if forumsread and forumsread.cleared is not None and \
                forumsread.cleared >= self.last_post.date_created:
            return False

        if topicsread and topicsread.last_read >= self.last_post.date_created:
            return False

        return True

    def update_read(self, user, forum, forumsread):
        """Updates the topicsread and forumsread tracker for a specified user,
        if the topic contains new posts or the user hasn't read the topic.
        Returns True if the tracker has been updated.

        :param user: The user for whom the readstracker should be updated.
        :param forum: The forum in which the topic is.
        :param forumsread: The forumsread object. It is used to check if there
                           is a new post since the forum has been marked as
                           read.
        """
        # User is not logged in - abort
        if not is_authenticated(user):
            return False

        topicsread = TopicsRead.query.\
            filter(TopicsRead.user_id == user.id,
                   TopicsRead.topic_id == self.id).first()

        if not self.tracker_needs_update(forumsread, topicsread):
            return False

        # Because we return True/False if the trackers have been
        # updated, we need to store the status in a temporary variable
        updated = False

        # A new post has been submitted that the user hasn't read.
        # Updating...
        if topicsread:
            topicsread.last_read = datetime.utcnow()
            topicsread.save()
            updated = True

        # The user has not visited the topic before. Inserting him in
        # the TopicsRead model.
        elif not topicsread:
            topicsread = TopicsRead()
            topicsread.user_id = user.id
            topicsread.topic_id = self.id
            topicsread.forum_id = self.forum_id
            topicsread.last_read = datetime.utcnow()
            topicsread.save()
            updated = True

        # No unread posts
        else:
            updated = False

        # Save True/False if the forums tracker has been updated.
        updated = forum.update_read(user, forumsread, topicsread)

        return updated

    def recalculate(self):
        """Recalculates the post count in the topic."""
        post_count = Post.query.filter_by(topic_id=self.id).count()
        self.post_count = post_count
        self.save()
        return self

    def move(self, new_forum):
        """Moves a topic to the given forum.
        Returns True if it could successfully move the topic to forum.

        :param new_forum: The new forum for the topic
        """

        # if the target forum is the current forum, abort
        if self.forum_id == new_forum.id:
            return False

        old_forum = self.forum
        self.forum.post_count -= self.post_count
        self.forum.topic_count -= 1
        self.forum_id = new_forum.id

        new_forum.post_count += self.post_count
        new_forum.topic_count += 1

        db.session.commit()

        new_forum.update_last_post()
        old_forum.update_last_post()

        TopicsRead.query.filter_by(topic_id=self.id).delete()

        return True

    def save(self, user=None, forum=None, post=None):
        """Saves a topic and returns the topic object. If no parameters are
        given, it will only update the topic.

        :param user: The user who has created the topic
        :param forum: The forum where the topic is stored
        :param post: The post object which is connected to the topic
        """

        # Updates the topic
        if self.id:
            db.session.add(self)
            db.session.commit()
            return self

        # Set the forum and user id
        self.forum_id = forum.id
        self.user_id = user.id
        self.username = user.username

        # Set the last_updated time. Needed for the readstracker
        self.last_updated = datetime.utcnow()

        self.date_created = datetime.utcnow()

        # Insert and commit the topic
        db.session.add(self)
        db.session.commit()

        # Create the topic post
        post.save(user, self)

        # Update the first post id
        self.first_post_id = post.id

        # Update the topic count
        forum.topic_count += 1
        db.session.commit()

        return self

    def delete(self, users=None):
        """Deletes a topic with the corresponding posts. If a list with
        user objects is passed it will also update their post counts

        :param users: A list with user objects
        """
        # Grab the second last topic in the forum + parents/childs
        topic = Topic.query.\
            filter_by(forum_id=self.forum_id).\
            order_by(Topic.last_post_id.desc()).limit(2).offset(0).all()

        # do we want to delete the topic with the last post in the forum?
        if topic and topic[0].id == self.id:
            try:
                # Now the second last post will be the last post
                self.forum.last_post_id = topic[1].last_post_id
                self.forum.last_post_title = topic[1].title
                self.forum.last_post_user_id = topic[1].user_id
                self.forum.last_post_username = topic[1].username
                self.forum.last_post_created = topic[1].last_updated
            # Catch an IndexError when you delete the last topic in the forum
            # There is no second last post
            except IndexError:
                self.forum.last_post_id = None
                self.forum.last_post_title = None
                self.forum.last_post_user_id = None
                self.forum.last_post_username = None
                self.forum.last_post_created = None

            # Commit the changes
            db.session.commit()

        # These things needs to be stored in a variable before they are deleted
        forum = self.forum

        TopicsRead.query.filter_by(topic_id=self.id).delete()

        # Delete the topic
        db.session.delete(self)
        db.session.commit()

        # Update the post counts
        if users:
            for user in users:
                user.post_count = Post.query.filter_by(user_id=user.id).count()
                db.session.commit()

        forum.topic_count = Topic.query.\
            filter_by(forum_id=self.forum_id).\
            count()

        forum.post_count = Post.query.\
            filter(Post.topic_id == Topic.id,
                   Topic.forum_id == self.forum_id).\
            count()

        db.session.commit()
        return self
Exemplo n.º 6
0
class Account(db.Model):
    __tablename__ = 'account'

    id = Column(Integer, primary_key=True)

    fullname = Column(Unicode(2000))

    email = Column(Unicode(2000), unique=True)
    password = Column(Unicode(2000))
    api_key = Column(Unicode(2000), default=make_uuid)
    admin = Column(Boolean, default=False)

    moderator= Column(Boolean, default=False)

    verified = Column(Boolean, default=False) 

    #use this in the future to mark based on domain
    usg_group = Column(Unicode(2000))

    login_hash = Column(Unicode(2000), default=make_uuid)

    datasets = relationship(Dataset,
                            secondary=account_dataset_table,
                            backref=backref('managers', lazy='dynamic'))


    #forus

    website = db.Column(db.String(200))
    location = db.Column(db.String(100))
    post_count = db.Column(db.Integer, default=0)

    posts = db.relationship("Post", backref="user", lazy="dynamic")
    topics = db.relationship("Topic", backref="user", lazy="dynamic")


    tracked_topics = \
            db.relationship("Topic", secondary=topictracker,
                            primaryjoin=(topictracker.c.user_id == id),
                            backref=db.backref("topicstracked", lazy="dynamic"),
                            lazy="dynamic")

    def __init__(self):
        self.api_key = make_uuid()

    def is_authenticated(self):
        return True

    def is_active(self):
        return True

    def get_id(self):
        return self.id

    def reset_loginhash(self):
        self.login_hash = make_uuid()
        

    @property
    def display_name(self):
        return self.fullname

    @property
    def token(self):
        h = hmac.new('')
        h.update(self.api_key)
        if self.password:
            h.update(self.password)
        return h.hexdigest()

    #helper for forums
    @property 
    def username(self):
        return self.fullname
 
    def is_anonymous(self):
        return False


    #forums


    # Properties
    @property
    def last_post(self):
        """Returns the latest post from the user"""

        return Post.query.filter(Post.user_id == self.id).\
            order_by(Post.date_created.desc()).first()

    @property
    def url(self):
        """Returns the url for the user"""
        #change to public user page
        return url_for("account.profile", account_id=self.id)
        #return url_for("account.profile", username=self.username)

    @property
    def permissions(self):
        """Returns the permissions for the user"""
        return self.get_permissions()


    @property
    def days_registered(self):
        """Returns the amount of days the user is registered."""
        days_registered = (datetime.utcnow() - self.date_joined).days
        if not days_registered:
            return 1
        return days_registered

    @property
    def topic_count(self):
        """Returns the thread count"""
        return Topic.query.filter(Topic.user_id == self.id).count()

    @property
    def posts_per_day(self):
        """Returns the posts per day count"""
        return round((float(self.post_count) / float(self.days_registered)), 1)

    @property
    def topics_per_day(self):
        """Returns the topics per day count"""
        return round((float(self.topic_count) / float(self.days_registered)), 1)

    def recalculate(self):
        """Recalculates the post count from the user."""
        post_count = Post.query.filter_by(user_id=self.id).count()
        self.post_count = post_count
        self.save()
        return self

    def all_topics(self, page):
        """Returns a paginated result with all topics the user has created."""

        return Topic.query.filter(Topic.user_id == self.id).\
            filter(Post.topic_id == Topic.id).\
            order_by(Post.id.desc()).\
            paginate(page, flaskbb_config['TOPICS_PER_PAGE'], False)

    def all_posts(self, page):
        """Returns a paginated result with all posts the user has created."""

        return Post.query.filter(Post.user_id == self.id).\
            paginate(page, flaskbb_config['TOPICS_PER_PAGE'], False)

    def track_topic(self, topic):
        """Tracks the specified topic
        :param topic: The topic which should be added to the topic tracker.
        """

        if not self.is_tracking_topic(topic):
            self.tracked_topics.append(topic)
            return self

    def untrack_topic(self, topic):
        """Untracks the specified topic
        :param topic: The topic which should be removed from the
                      topic tracker.
        """

        if self.is_tracking_topic(topic):
            self.tracked_topics.remove(topic)
            return self

    def is_tracking_topic(self, topic):
        """Checks if the user is already tracking this topic
        :param topic: The topic which should be checked.
        """
        print topictracker
        return self.tracked_topics.filter(
            topictracker.c.forum_topic_id == topic.id).count() > 0


    #@cache.memoize(timeout=3600)
    def get_permissions(self, exclude=None):
        """Returns a dictionary with all the permissions the user has.
        :param exclude: a list with excluded permissions. default is None.
        """

        exclude = exclude or []
        exclude.extend(['id', 'name', 'description'])

        perms = {}
        return perms

    def invalidate_cache(self):
        """Invalidates this objects cached metadata."""
        return
        #cache.delete_memoized(self.get_permissions, self)

    def ban(self):
        """Bans the user. Returns True upon success."""
        #set value and invalid
        if not self.banned:
            self.banned = True
            db.session.commit()
            return True
        return False

    def unban(self):
        """Unbans the user. Returns True upon success."""

        if self.banned:
            self.banned = False
            db.session.commit()
            return True
        return False




    @classmethod
    def by_id(cls, id):
        return db.session.query(cls).filter_by(id=id).first()

    @classmethod
    def all(cls):
        return db.session.query(cls)

    @classmethod
    def by_email(cls, email):
        return db.session.query(cls).filter_by(email=email).first()

    @classmethod
    def by_api_key(cls, api_key):
        return db.session.query(cls).filter_by(api_key=api_key).first()

    @classmethod
    def by_login_hash(cls, login_hash):
        return db.session.query(cls).filter_by(login_hash=login_hash).first()

    def as_dict(self):
        """
        Return the dictionary representation of the account
        """

        # Dictionary will include name, fullname, email and the admin bit
        account_dict = {
            'fullname': self.fullname,
            'email': self.email,
            'admin': self.admin
        }


        # Return the dictionary representation
        return account_dict

    def __repr__(self):
        return '<Account(%r,%r)>' % (self.id, self.email)