Source code for fardel.apps.blog.views

"""
.. _restapi/auth:

Objects
=======

1. Category
    :id: ID for the category in database.
    :name: Name of the category to display.
    :posts:


2. Post
    :id: ID for the post in database.
    :title: 
    :content:
    :allow_comment:
    :category:
    :image:
    :comments_count:
    :tags:
    :create_time:
    :update_time:
    :summarized:


3. Tag
    :id: ID for the tag in database.
    :name: Name of the tag.
    :frequency: 
    :posts: list of PostObjects (conditional).


4. Comment
    :id: ID for the comment in database.
    :content: Content of the comment.
    :create_time: (Timestamp) create time of the comment.
    :user: UserObject :ref:`restapi-auth` if user is login.
    :author_mail: Author email if user is not login.
    :author_name: Author name if user is not login.
    :replies: List of Comment Objects.
    
    
"""
import math

from flask import request

from flask_jwt_extended import current_user, jwt_optional

from fardel.core.rest import create_api, abort, Resource
from fardel.core.utils import cache_get_key
from . import mod
from .models import *
from fardel.ext import db, cache


blog_api = create_api(mod)
    

def rest_resource(resource_cls):
    """ Decorator for adding resources to Api App """
    blog_api.add_resource(resource_cls, *resource_cls.endpoints)
    return resource_cls

def get_valid_post(post_id):
    post = Post.query.filter_by(id=post_id).first()
    if not post:
        return None
    if post and post.status.name != "Publish":
        return None
    return post

@mod.before_app_first_request
def create_permissions():
    setup_permissions()
    PostStatus.generate_default()


[docs]@rest_resource @cache_get_key class PostApi(Resource): """ :URL: ``/api/blog/posts/`` and ``/api/blog/posts/<post_id>/`` """ endpoints = ['/posts/', '/posts/<post_id>/']
[docs] @cache.cached(timeout=50) def get(self, post_id=None): """ :optional url parameter: * post_id :optional url query string: * page (default: 1) * per_page (default: 16) :response: If post_id is provided: .. code-block:: python { "post": PostObject(with content) } If post_id is not provided: .. code-block:: python { "posts":[list of PostObjects(without content and with summarized)] "pages": Number of pages } :errors: If post id is not valid: :status_code: 404 :response: .. code-block:: json {"message":"No post with this id"} """ if post_id: post = get_valid_post(post_id) if not post: return {"message":"No post with this id"}, 404 return {'post':post.dict(summarized=False)} page = request.args.get('page', 1, type=int) per_page = request.args.get('per_page', 16, type=int) query = Post.query.filter(PostStatus.name=="Publish").join( PostStatus, Post.status_id==PostStatus.id) pages = math.ceil(query.count() / per_page) posts = query.order_by(Post.publish_time).paginate(page=page, per_page=per_page, error_out=False).items return {'posts':[post.dict() for post in posts], 'pages':pages}
[docs]@rest_resource @cache_get_key class CommentApi(Resource): """ :URL: ``/api/blog/posts/<post_id>/comments/`` """ endpoints = ['/posts/<post_id>/comments/'] method_decorators = { 'get': [cache.cached(timeout=20)], 'post':[jwt_optional], }
[docs] def get(self, post_id): """ :optional url query string: * page (default: 1) * per_page (default: 16) :response: .. code-block:: python { "comments":[list of CommentObjects] "pages": Number of pages } :errors: If post_id is not in valid ( not a published post or not found ): :status_code: 404 .. code-block:: python {"message":"No post with this id"} """ post = get_valid_post(post_id) if not post: return {"message":"No post with this id"}, 404 page = request.args.get('page', 1, type=int) per_page = request.args.get('per_page', 16, type=int) comment_query = Comment.query.filter_by( post_id=post_id, parent_comment_id=None, approved=True ) pages = math.ceil(comment_query.count() / per_page) comments = comment_query.paginate(page=page, per_page=per_page, error_out=False).items return {'comments':[c.dict() for c in comments], 'pages':pages}
[docs] def post(self, post_id): """ * Authorization token is not required but optional :required data: * author_name (if you dont use access_token) * author_email (if you dont use access_token) * content :optional data: * parent_comment_id :response: .. code-block:: json {"message":"Comment successfuly added"} :errors: If post_id is not in valid ( not a published post or not found ): :status_code: 404 :response: .. code-block:: python {"message":"No post with this id"} If commenter information (user_id or (author_name, author_email) ) is not provided: :status_code: 422 :response: .. code-block:: python {"message":"Author name and Author email are required to post a comment or you need to sign in"} """ post = get_valid_post(post_id) if not post: return {"message":"No post with this id"}, 404 data = request.get_json() user_id = getattr(current_user, 'id', None) author_name = data.get('author_name') author_email = data.get('author_email') parent_comment_id = data.get('parent_comment_id') content = data.get('content') if not user_id and (not author_email or not author_name): return { "message":"Author name and Author email are required to\ post a comment or you need to sign in" }, 422 if not content: return {"message":"Comment needs content"}, 422 c = Comment( post_id=post_id, parent_comment_id=parent_comment_id, content=content, author_name=author_name, author_email=author_email, user_id=user_id, ) db.session.add(c) db.session.commit() return {"message":"Comment successfuly added", 'comment':c.dict()}
[docs]@rest_resource class TagApi(Resource): """ :URL: ``/api/blog/tags/`` or ``/api/blog/tags/<tag_id>/posts/`` """ endpoints = ['/tags/<tag_id>/posts/', '/tags/']
[docs] @cache.cached(timeout=50) def get(self, tag_id=None): """ If tag_id is provided: :response: .. code-block:: python { "tag": TagObject(with posts) } :errors: If tag_id is not in valid: :status_code: 404 .. code-block:: python {"message":"No tag found with this id"} Without tag_id: :response: .. code-block:: python { "tags": [list of TagObjects(without posts)] } """ if tag_id: tag = Tag.query.filter_by(id=tag_id).first() if not tag: return {"message":"No tag found with this id"}, 404 return {'tag':tag.dict(posts=True)} tags = Tag.query.all() return {'tags':[tag.dict() for tag in tags]}
[docs]@rest_resource @cache_get_key class CategoryApi(Resource): """ :URL: ``/api/blog/categories/`` or ``/api/blog/categories/<category_id>/posts/`` """ endpoints = ['/categories/<category_id>/posts/', '/categories/']
[docs] @cache.cached(timeout=50) def get(self, category_id=None): """ If category_id is provided: :optional url query string: * page (default: 1) * per_page (default: 16) :response: .. code-block:: python { "category": CategoryObject(with posts) } :errors: If category_id is not in valid: :status_code: 404 .. code-block:: python {"message":"No category found with this id"} Without category_id: :response: .. code-block:: python { "categories": [list of CategoryObject(without posts)] } """ if category_id: category = Category.query.filter_by(id=category_id).first() if not category: return {"message":"No category found with this id"}, 404 page = request.args.get('page', 1, type=int) per_page = request.args.get('per_page', 16, type=int) category_dict = category.dict() posts = Post.query.outerjoin(PostStatus # Alternatively use filter_by(status_id==1) ? ).filter(PostStatus.name=='Publish', Post.category_id==category_id ).order_by(Post.publish_time ).paginate(page=page, per_page=per_page, error_out=False).items category_dict['posts'] = [post.dict() for post in posts] return {'category':category_dict} categories = Category.query.all() return {'categories':[category.dict() for category in categories]}