Skip to content

Commit

Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
WIP: Add appreciations API
Browse files Browse the repository at this point in the history
recrsn committed Jan 25, 2021
1 parent c63b6c8 commit e787ed5
Showing 10 changed files with 249 additions and 21 deletions.
1 change: 1 addition & 0 deletions snowflake/acl/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
from .appreciations import *
from .one_on_one import *
13 changes: 13 additions & 0 deletions snowflake/acl/appreciations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from ..models import Appreciation


def can_view_appreciations():
return True


def can_create_appreciations():
return True


def can_view_appreciation(_: Appreciation):
return True
1 change: 1 addition & 0 deletions snowflake/app.py
Original file line number Diff line number Diff line change
@@ -35,6 +35,7 @@ def load_user(user_id):
app.register_blueprint(api.users.blueprint, url_prefix="/api/users")
app.register_blueprint(api.notifications.blueprint, url_prefix="/api/notifications")
app.register_blueprint(api.one_on_ones.blueprint, url_prefix="/api/one_on_ones")
app.register_blueprint(api.appreciations.blueprint, url_prefix="/api/appreciations")

app.register_blueprint(login.blueprint, url_prefix="/login")
app.register_blueprint(register.blueprint, url_prefix="/register")
1 change: 1 addition & 0 deletions snowflake/controllers/api/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from . import notifications
from . import one_on_ones
from . import users
from . import appreciations
142 changes: 142 additions & 0 deletions snowflake/controllers/api/appreciations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import re
from datetime import datetime

from flask import Blueprint, request
from flask_login import login_required, current_user

from .response import bad_request, not_found, unauthorized
from ... import acl
from ...models import Appreciation, Like, User, Mention
from ...schemas.appreciation import AppreciationSchema, CreateAppreciationSchema
from ...services import notification

blueprint = Blueprint('api.appreciations', __name__)

appreciation_schema = AppreciationSchema()
create_appreciation_schema = CreateAppreciationSchema()


def get_appreciation_viewer_like(appreciation: Appreciation):
return Like.get_by_appreciation_and_user(appreciation, current_user)


def appreciation_view(appreciation: Appreciation):
return {
'id': appreciation.id,
'content': appreciation.content,
'created_at': appreciation.created_at,
'created_by': appreciation.created_by,
'like_count': appreciation.like_count,
'comment_count': appreciation.comment_count,
'mentions': appreciation.mentions,

'viewer_like': get_appreciation_viewer_like(appreciation),
}


@login_required
@blueprint.route('', methods=['GET'])
def list_all_appreciations():
if not acl.can_view_appreciations():
return unauthorized()

formatted_appreciations = [appreciation_view(a) for a in Appreciation.get_all()]
return appreciation_schema.jsonify(formatted_appreciations, many=True)


@login_required
@blueprint.route('', methods=['PUT'])
def create_appreciation():
if not request.is_json:
return bad_request()

if not acl.can_create_appreciations():
return unauthorized()

appreciation: Appreciation = create_appreciation_schema.load(request.json)

appreciation.created_by = current_user
appreciation.created_at = datetime.now()

Appreciation.create(appreciation)

mentions = re.findall(r'@[a-zA-Z0-9._]+', appreciation.content)

for mention in mentions:
user = User.get_by_username(mention[1:])
if user is None:
continue
m = Mention(user=user, appreciation=appreciation)
Mention.create(m)

notification.notify_appreciation(appreciation)

return appreciation_schema.jsonify(appreciation)


@login_required
@blueprint.route('/<_id>', methods=['GET'])
def get_appreciation(_id):
appreciation = Appreciation.get(_id)

if not appreciation:
return not_found()

if not acl.can_view_appreciation(appreciation):
return unauthorized()

return appreciation_schema.jsonify(appreciation)


@login_required
@blueprint.route('/<_id>', methods=['PATCH'])
def update_appreciation(_id):
return not_found()


@login_required
@blueprint.route('/<_id>', methods=['DELETE'])
def delete_appreciation(_id):
pass


@login_required
@blueprint.route('/<appreciation_id>/likes', methods=['GET'])
def get_appreciation_likes(appreciation_id):
pass


@login_required
@blueprint.route('/<appreciation_id>/likes', methods=['PUT'])
def like(appreciation_id):
pass


@login_required
@blueprint.route('/<appreciation_id>/likes/<like_id>', methods=['DELETE'])
def delete_like(appreciation_id, like_id):
pass


@login_required
@blueprint.route('/<appreciation_id>/comments', methods=['GET'])
def get_comments(appreciation_id):
pass


@login_required
@blueprint.route('/<appreciation_id>/comments', methods=['GET'])
def create_comment(appreciation_id):
pass


@login_required
@blueprint.route('/<appreciation_id>/comments/<comment_id>', methods=['GET'])
def update_comment(appreciation_id, comment_id):
pass


@login_required
@blueprint.route('/<appreciation_id>/comments/<comment_id>', methods=['GET'])
def delete_comment(appreciation_id, comment_id):
pass
35 changes: 19 additions & 16 deletions snowflake/models/appreciation.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from .comment import Comment
from .like import Like
from .user import User
from ..db import db
@@ -19,20 +20,13 @@ class Appreciation(db.Model):
def creator(self):
return self.created_by

@staticmethod
def create(appreciation):
db.session.add(appreciation)
db.session.commit()

@staticmethod
def get_all():
return Appreciation.query.order_by(Appreciation.created_at.desc()).all()

def get_like_count(self):
@property
def like_count(self):
return Like.query.filter_by(appreciation=self).count()

def get_comment_count(self):
return db.session.scalar('SELECT COUNT(*) FROM comment c WHERE c.appreciation_id = :id', {'id': self.id})
@property
def comment_count(self):
return Comment.query.filter_by(appreciation=self).count()

def is_liked_by(self, user: User):
return db.session.scalar(
@@ -41,12 +35,24 @@ def is_liked_by(self, user: User):
{'appreciation_id': self.id, 'user_id': user.id}) > 0

@staticmethod
def get(id_):
def create(appreciation):
db.session.add(appreciation)
db.session.commit()

@staticmethod
def get_all():
return Appreciation.query.order_by(Appreciation.created_at.desc()).all()

@staticmethod
def get(id_) -> 'Appreciation':
return Appreciation.query.get(id_)

def get_mentions(self):
return self.mentions

def get_comments(self):
return self.comments

@staticmethod
def count_by_user(user: User):
return Appreciation.query.filter_by(created_by=user).count()
@@ -74,6 +80,3 @@ def most_appreciated():
})

return result

def get_comments(self):
return self.comments
8 changes: 6 additions & 2 deletions snowflake/models/comment.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from .appreciation import Appreciation
from typing import TYPE_CHECKING

from ..db import db

if TYPE_CHECKING:
from .appreciation import Appreciation


class Comment(db.Model):
id = db.Column(db.BigInteger, primary_key=True)
@@ -9,7 +13,7 @@ class Comment(db.Model):
user_id = db.Column(db.String, db.ForeignKey('user.id'), nullable=False)
user = db.relationship('User', backref=db.backref('comments', lazy=True))
appreciation_id = db.Column(db.BigInteger, db.ForeignKey('appreciation.id'), nullable=False)
appreciation: Appreciation = db.relationship('Appreciation')
appreciation: 'Appreciation' = db.relationship('Appreciation')

@staticmethod
def create(comment):
11 changes: 10 additions & 1 deletion snowflake/models/like.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
from typing import TYPE_CHECKING

from ..db import db

if TYPE_CHECKING:
from . import Appreciation


class Like(db.Model):
id = db.Column(db.BigInteger, primary_key=True)
user_id = db.Column(db.String, db.ForeignKey('user.id'), nullable=False)
user = db.relationship('User', backref=db.backref('likes', lazy=True))
appreciation_id = db.Column(db.String, db.ForeignKey('appreciation.id'), nullable=False)
appreciation = db.relationship('Appreciation')
appreciation: 'Appreciation' = db.relationship('Appreciation')

@staticmethod
def create(like):
@@ -22,3 +27,7 @@ def dislike(appreciation, user):
like = Like.query.filter_by(appreciation_id=appreciation.id, user_id=user.id).first()
db.session.delete(like)
db.session.commit()

@staticmethod
def get_by_appreciation_and_user(appreciation, user):
return Like.query.filter_by(appreciation=appreciation, user=user).first()
54 changes: 54 additions & 0 deletions snowflake/schemas/appreciation.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
from marshmallow.fields import Integer, String, DateTime, List
from marshmallow_sqlalchemy.fields import Nested

from ..marshmallow import marshmallow
from ..models import Like, User
from ..schemas.user import UserSchema


class LikeSchema(marshmallow.SQLAlchemySchema):
class Meta:
model = Like

id = marshmallow.auto_field()
user = Nested(UserSchema)


class MentionedUserSchema(marshmallow.SQLAlchemySchema):
class Meta:
model = User

username = marshmallow.auto_field()


class MentionSchema(marshmallow.Schema):
user = Nested(MentionedUserSchema)


class ViewerLikeSchema(marshmallow.SQLAlchemySchema):
class Meta:
model = Like

id = marshmallow.auto_field()


class AppreciationSchema(marshmallow.Schema):
id = Integer()
content = String()
created_at = DateTime()

created_by = Nested(UserSchema)

like_count = Integer()
comment_count = Integer()
viewer_like = Nested(ViewerLikeSchema)

mentions = List(Nested(MentionSchema))


class CreateAppreciationSchema(marshmallow.Schema):
class Meta:
model = True
load_instance = True

content = String()
4 changes: 2 additions & 2 deletions snowflake/templates/home.html
Original file line number Diff line number Diff line change
@@ -92,7 +92,7 @@
<button type="button" class="clear-button has-text-danger level-item is-clickable"
data-toggle-modal="#likes-{{ appreciation.id }}">
<span>
{% set like_count = appreciation.get_like_count() %}
{% set like_count = appreciation.like_count %}
{{ like_count }} {{ choose_plural(like_count, 'like', 'likes') }}
</span>
</button>
@@ -104,7 +104,7 @@
<ion-icon name="chatbubble-outline"></ion-icon>
</span>
<span>
{{ appreciation.get_comment_count() }}
{{ appreciation.comment_count }}
</span>
</button>
<div class="modal" id="likes-{{ appreciation.id }}">

0 comments on commit e787ed5

Please sign in to comment.